001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.feature.local.interest.experiment;
031
032import java.io.File;
033import java.io.IOException;
034import java.util.ArrayList;
035import java.util.HashMap;
036import java.util.List;
037
038import org.apache.log4j.BasicConfigurator;
039import org.apache.log4j.Level;
040import org.apache.log4j.Logger;
041import org.openimaj.feature.local.list.LocalFeatureList;
042import org.openimaj.feature.local.list.MemoryLocalFeatureList;
043import org.openimaj.image.ImageUtilities;
044import org.openimaj.image.MBFImage;
045import org.openimaj.image.colour.Transforms;
046import org.openimaj.image.feature.local.detector.ipd.collector.CircularInterestPointKeypoint;
047import org.openimaj.image.feature.local.engine.ipd.FinderMode;
048import org.openimaj.image.feature.local.engine.ipd.IPDSIFTEngine;
049import org.openimaj.image.feature.local.interest.AffineAdaption;
050import org.openimaj.image.feature.local.interest.HarrisIPD;
051import org.openimaj.image.feature.local.interest.IPDSelectionMode;
052import org.openimaj.image.feature.local.interest.InterestPointData;
053import org.openimaj.image.feature.local.keypoints.InterestPointKeypoint;
054import org.openimaj.io.IOUtils;
055
056import Jama.Matrix;
057
058
059
060public class OxfordRepeatabilityExperiment {
061        private static final String DEFAULT_FEATURE_DUMP_PATH = "/tmp/featurePath";
062        static Logger logger = Logger.getLogger(AffineAdaption.class);
063        static{
064                BasicConfigurator.configure();
065                logger.setLevel(Level.DEBUG);
066        }
067        static class ExperimentException extends Exception{
068
069                public ExperimentException(String string) {
070                        super(string);
071                }
072
073                /**
074                 * 
075                 */
076                private static final long serialVersionUID = 1L;
077                
078        }
079        
080        private int nExperiments;
081        private String transformName;
082        private String imageName;
083        private HashMap<String, List<InterestPointData>> features;
084        private HashMap<String, Matrix> transforms;
085        private HashMap<String, MBFImage> images;
086        private ExperimentFeatureExtraction experiemtnFeatureExtraction;
087        private String featureDumpPath = DEFAULT_FEATURE_DUMP_PATH;
088
089        OxfordRepeatabilityExperiment(String experimentRoot, String transformName, String imageName, int nImages, ExperimentFeatureExtraction expFE, String featureDumpPath) throws IOException{
090                this.nExperiments = nImages - 1;
091                this.transformName = transformName;
092                this.imageName = imageName;
093                this.experiemtnFeatureExtraction = expFE;
094                this.featureDumpPath = featureDumpPath;
095                transforms = new HashMap<String,Matrix>();
096                images = new HashMap<String, MBFImage>();
097                
098                String imageNameFormated = String.format(imageName, 1);
099                String imageLocation = experimentRoot + "/" + imageNameFormated;
100                logger.debug("Loading image: " + imageLocation );
101                MBFImage im = ImageUtilities.readMBF(this.getClass().getResourceAsStream(imageLocation));
102                if(im == null){
103                        throw new IOException("Can't load image: " + imageLocation);
104                }
105                images.put(imageNameFormated, im);
106                
107                for (int i = 1; i < nImages; i++) {
108                        String transformNameFormated = String.format(transformName, 1, i + 1);
109                        String transformLocation = experimentRoot + "/" + transformNameFormated;
110                        logger.debug("Loading trasnform: " + transformLocation);
111                        transforms.put(transformNameFormated,IPDRepeatability.readHomography(this.getClass().getResourceAsStream(transformLocation)));
112                        imageNameFormated = String.format(imageName, i+1);
113                        imageLocation = experimentRoot + "/" + imageNameFormated;
114                        logger.debug("Loading image: " + imageLocation );
115                        images.put(imageNameFormated, ImageUtilities.readMBF(this.getClass().getResourceAsStream(imageLocation)));
116                }
117                
118                this.features = new HashMap<String, List<InterestPointData>>();
119        }
120        
121        public OxfordRepeatabilityExperiment(String expBase, String transformName,String imgName, int nExperiments, ExperimentFeatureExtraction experiemtnFeatureExtraction) throws IOException {
122                this(expBase, transformName, imgName, nExperiments, experiemtnFeatureExtraction, DEFAULT_FEATURE_DUMP_PATH);
123        }
124
125        public IPDRepeatability<InterestPointData> experimentWith(int n) throws ExperimentException{
126                if(n > this.nExperiments) 
127                        return null;
128                String image1Name = String.format(imageName, 1);
129                String image2Name = String.format(imageName, n+1);
130                
131                if(!this.images.containsKey(image1Name) || this.images.get(image1Name)  == null){
132                        throw new ExperimentException("Couldn't load: " + image1Name);
133                }
134                
135                if(!this.images.containsKey(image2Name)){
136                        throw new ExperimentException("Couldn't load: " + image2Name);
137                }
138                
139                List<InterestPointData> image1Features = getFeatures(image1Name);
140                List<InterestPointData> image2Features = getFeatures(image2Name);
141                
142                if(image1Features == null || image2Features == null){
143                        throw new ExperimentException("Couldn't load features correctly");
144                }
145                
146                String transformNameFormatted = String.format(this.transformName, 1,n+1);
147                
148                if(!this.transforms.containsKey(transformNameFormatted)){
149                        throw new ExperimentException("Couldn't load: " + transformNameFormatted);
150                }
151                
152                
153                
154                return IPDRepeatability.repeatability(
155                                this.images.get(image1Name), 
156                                this.images.get(image2Name), 
157                                image1Features, 
158                                image2Features, 
159                                this.transforms.get(transformNameFormatted), 
160                                4
161                );
162        }
163
164        private List<InterestPointData> getFeatures(String imageNameFormatted) {
165                
166                if(!this.features.containsKey(imageNameFormatted)){
167                        File featureDump = new File(featureDumpPath ,String.format("%s/%s.%s",experiemtnFeatureExtraction.experimentName(),imageNameFormatted,experiemtnFeatureExtraction.experimentName()));
168                        featureDump.getParentFile().mkdir();
169                        LocalFeatureList<? extends InterestPointKeypoint<InterestPointData>> kpts = null;
170                        if(featureDump.exists()){
171                                try {
172                                        kpts = MemoryLocalFeatureList.read(featureDump, CircularInterestPointKeypoint.class);
173                                } catch (IOException e) {
174                                        e.printStackTrace();
175                                }
176                        }
177                        if(kpts==null){
178//                              HarrisIPD hIPD = new HarrisIPD(1.4f);
179//                              hIPD.setImageBlurred(true);
180//                              // AffineAdaption affineIPD = new AffineAdaption(harrisIPD,new
181//                              // IPDSelectionMode.Threshold(10000f));
182//                              IPDSIFTEngine engine = new IPDSIFTEngine(hIPD);
183//                              engine.setAcrossScales(true);
184//                              engine.setFinderMode(new FinderMode.Characteristic<InterestPointData>());
185                                IPDSIFTEngine engine = experiemtnFeatureExtraction.engine();
186                                kpts = engine.findFeatures(Transforms.calculateIntensityNTSC(this.images.get(imageNameFormatted)));
187                                
188                                try {
189                                        IOUtils.writeBinary(featureDump, kpts);
190                                } catch (IOException e) {
191                                        e.printStackTrace();
192                                }
193                        }
194                        
195                        List<InterestPointData> ipts = new ArrayList<InterestPointData>();
196                        for (InterestPointKeypoint<InterestPointData> interestPointKeypoint : kpts) {
197                                ipts.add(interestPointKeypoint.location);
198                        }
199                        this.features.put(imageNameFormatted, ipts);
200                }
201                
202                
203                
204                return this.features.get(imageNameFormatted);
205        }
206        
207        static interface ExperimentFeatureExtraction{
208                public class AffineHarris implements ExperimentFeatureExtraction {
209                        @Override
210                        public String experimentName() {
211                                return "affineharris";
212                        }
213
214                        @Override
215                        public IPDSIFTEngine engine() {
216                                
217                                return null;
218                        }
219
220                }
221
222                public String experimentName();
223                public IPDSIFTEngine engine();
224                
225                static class Harris implements ExperimentFeatureExtraction{
226
227                        @Override
228                        public String experimentName() {
229                                return "harris";
230                        }
231
232                        @Override
233                        public IPDSIFTEngine engine() {
234                                HarrisIPD hIPD = new HarrisIPD(1.4f);
235                                hIPD.setImageBlurred(true);
236                                // AffineAdaption affineIPD = new AffineAdaption(harrisIPD,new
237                                // IPDSelectionMode.Threshold(10000f));
238                                IPDSIFTEngine engine = new IPDSIFTEngine(hIPD);
239                                engine.setSelectionMode(new IPDSelectionMode.Threshold(10000f));
240                                engine.setAcrossScales(true);
241                                engine.setFinderMode(new FinderMode.Characteristic<InterestPointData>());
242                                return engine;
243                        }
244                        
245                }
246        }
247        
248        public static void main(String args[]) throws IOException, ExperimentException{
249                String expBase = "/org/openimaj/image/feature/validator/graf";
250                String imgName = "img%d.ppm";
251                OxfordRepeatabilityExperiment exp = new OxfordRepeatabilityExperiment(
252                                expBase, // The root of all the images and transforms
253                                "H%dto%dp", // the name format of the transform 
254                                imgName, // the name format of the image
255                                6, // the number of experiments to expect
256                                new ExperimentFeatureExtraction.Harris()
257                );
258                
259                for(int i = 1; i < 6;i++)
260                {
261                        IPDRepeatability<InterestPointData> experiment = exp.experimentWith(i);
262                        System.out.println(String.format(imgName + ": %f", i+1, experiment.repeatability(0.6f)));
263                }
264//              IPDRepeatability<InterestPointData> experiment1v2 = exp.experimentWith(1);
265//              
266//              System.out.println("Experiment 1 v 2");
267//              for(float error = 0.1f; error <= .9f; error +=0.1f){
268//                      float overlap = 1.f - error;
269//                      System.out.format("Minimum overlap %f (err=%f): %f\n",overlap,error,experiment1v2.repeatability(overlap));
270//              }
271        }
272}