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