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.docs.tutorial.fund.ml.class101;
031
032import java.io.File;
033import java.io.IOException;
034import java.util.ArrayList;
035import java.util.List;
036import java.util.Map;
037
038import org.openimaj.data.DataSource;
039import org.openimaj.data.dataset.GroupedDataset;
040import org.openimaj.data.dataset.ListDataset;
041import org.openimaj.data.dataset.VFSListDataset;
042import org.openimaj.experiment.dataset.sampling.GroupSampler;
043import org.openimaj.experiment.dataset.sampling.GroupedUniformRandomisedSampler;
044import org.openimaj.experiment.dataset.split.GroupedRandomSplitter;
045import org.openimaj.experiment.evaluation.classification.ClassificationEvaluator;
046import org.openimaj.experiment.evaluation.classification.ClassificationResult;
047import org.openimaj.experiment.evaluation.classification.analysers.confusionmatrix.CMAnalyser;
048import org.openimaj.experiment.evaluation.classification.analysers.confusionmatrix.CMResult;
049import org.openimaj.feature.DiskCachingFeatureExtractor;
050import org.openimaj.feature.DoubleFV;
051import org.openimaj.feature.FeatureExtractor;
052import org.openimaj.feature.SparseIntFV;
053import org.openimaj.feature.local.data.LocalFeatureListDataSource;
054import org.openimaj.feature.local.list.LocalFeatureList;
055import org.openimaj.image.FImage;
056import org.openimaj.image.ImageUtilities;
057import org.openimaj.image.annotation.evaluation.datasets.Caltech101;
058import org.openimaj.image.annotation.evaluation.datasets.Caltech101.Record;
059import org.openimaj.image.feature.dense.gradient.dsift.ByteDSIFTKeypoint;
060import org.openimaj.image.feature.dense.gradient.dsift.DenseSIFT;
061import org.openimaj.image.feature.dense.gradient.dsift.PyramidDenseSIFT;
062import org.openimaj.image.feature.local.aggregate.BagOfVisualWords;
063import org.openimaj.image.feature.local.aggregate.BlockSpatialAggregator;
064import org.openimaj.ml.annotation.linear.LiblinearAnnotator;
065import org.openimaj.ml.annotation.linear.LiblinearAnnotator.Mode;
066import org.openimaj.ml.clustering.ByteCentroidsResult;
067import org.openimaj.ml.clustering.assignment.HardAssigner;
068import org.openimaj.ml.clustering.kmeans.ByteKMeans;
069import org.openimaj.util.pair.IntFloatPair;
070
071import de.bwaldvogel.liblinear.SolverType;
072
073/**
074 * OpenIMAJ Hello world!
075 *
076 */
077public class App {
078        /**
079         * Main method
080         *
081         * @param args
082         * @throws IOException
083         */
084        public static void main(String[] args) throws IOException {
085                System.out.println("Load dataset and take a sample");
086                final GroupedDataset<String, VFSListDataset<Record<FImage>>, Record<FImage>> allData = Caltech101
087                                .getData(ImageUtilities.FIMAGE_READER);
088                final GroupedDataset<String, ListDataset<Record<FImage>>, Record<FImage>> data = GroupSampler.sample(allData, 5,
089                                false);
090
091                System.out.println("Construct the base feature extractor");
092                final DenseSIFT dsift = new DenseSIFT(5, 7);
093                final PyramidDenseSIFT<FImage> pdsift = new PyramidDenseSIFT<FImage>(dsift, 6f, 7);
094
095                System.out.println("Create training and testing data");
096                final GroupedRandomSplitter<String, Record<FImage>> splits = new GroupedRandomSplitter<String, Record<FImage>>(
097                                data, 15, 0, 15);
098
099                System.out.println("Learn a vocabulary");
100                final HardAssigner<byte[], float[], IntFloatPair> assigner = trainQuantiser(GroupedUniformRandomisedSampler
101                                .sample(splits.getTrainingDataset(), 30), pdsift);
102
103                System.out.println("Define feature extractor");
104                // final HomogeneousKernelMap map = new
105                // HomogeneousKernelMap(KernelType.Chi2, WindowType.Rectangular);
106                // final FeatureExtractor<DoubleFV, Record<FImage>> extractor = map
107                // .createWrappedExtractor(new PHOWExtractor(pdsift, assigner));
108                final FeatureExtractor<DoubleFV, Record<FImage>> extractor = new DiskCachingFeatureExtractor<DoubleFV, Caltech101.Record<FImage>>(
109                                new File("/Users/jsh2/feature_cache/c101-small"), new SpPHOWExtractorImplementation(pdsift, assigner));
110
111                System.out.println("Construct and train classifier");
112                final LiblinearAnnotator<Record<FImage>, String> ann = new LiblinearAnnotator<Record<FImage>, String>(
113                                extractor, Mode.MULTICLASS, SolverType.L2R_L2LOSS_SVC, 1.0, 0.00001);
114                ann.train(splits.getTrainingDataset());
115
116                System.out.println("Evaluate classifier");
117                final ClassificationEvaluator<CMResult<String>, String, Record<FImage>> eval = new ClassificationEvaluator<CMResult<String>, String, Record<FImage>>(
118                                ann, splits.getTestDataset(), new CMAnalyser<Record<FImage>, String>(CMAnalyser.Strategy.SINGLE));
119                final Map<Record<FImage>, ClassificationResult<String>> guesses = eval.evaluate();
120                final CMResult<String> result = eval.analyse(guesses);
121
122                System.out.println(result.getDetailReport());
123        }
124
125        private static final class SpPHOWExtractorImplementation implements FeatureExtractor<DoubleFV, Record<FImage>> {
126                PyramidDenseSIFT<FImage> pdsift;
127                HardAssigner<byte[], float[], IntFloatPair> assigner;
128
129                public SpPHOWExtractorImplementation(PyramidDenseSIFT<FImage> pdsift,
130                                HardAssigner<byte[], float[], IntFloatPair> assigner)
131                {
132                        this.pdsift = pdsift;
133                        this.assigner = assigner;
134                }
135
136                @Override
137                public DoubleFV extractFeature(Record<FImage> object) {
138                        final FImage image = object.getImage();
139                        pdsift.analyseImage(image);
140
141                        final BagOfVisualWords<byte[]> bovw = new BagOfVisualWords<byte[]>(assigner);
142
143                        final BlockSpatialAggregator<byte[], SparseIntFV> spatial = new BlockSpatialAggregator<byte[], SparseIntFV>(
144                                        bovw, 2, 2);
145
146                        // final PyramidSpatialAggregator<byte[], SparseIntFV> spatial =
147                        // new PyramidSpatialAggregator<byte[], SparseIntFV>(bovw, 2, 4);
148
149                        return spatial.aggregate(pdsift.getByteKeypoints(0.015f), image.getBounds()).normaliseFV();
150                }
151        }
152
153        private static HardAssigner<byte[], float[], IntFloatPair> trainQuantiser(
154                        GroupedDataset<String, ListDataset<Record<FImage>>, Record<FImage>> sample, PyramidDenseSIFT<FImage> pdsift)
155                        {
156                List<LocalFeatureList<ByteDSIFTKeypoint>> allkeys = new ArrayList<LocalFeatureList<ByteDSIFTKeypoint>>();
157
158                for (final Record<FImage> rec : sample) {
159                        final FImage img = rec.getImage();
160
161                        pdsift.analyseImage(img);
162                        allkeys.add(pdsift.getByteKeypoints(0.005f));
163                }
164
165                if (allkeys.size() > 10000)
166                        allkeys = allkeys.subList(0, 10000);
167
168                final ByteKMeans km = ByteKMeans.createKDTreeEnsemble(300);
169                final DataSource<byte[]> datasource = new LocalFeatureListDataSource<ByteDSIFTKeypoint, byte[]>(allkeys);
170                final ByteCentroidsResult result = km.cluster(datasource);
171
172                return result.defaultHardAssigner();
173                        }
174}