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}