1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.openimaj.demos;
31
32 import gnu.trove.map.hash.TIntObjectHashMap;
33 import gnu.trove.map.hash.TObjectIntHashMap;
34 import gnu.trove.procedure.TObjectIntProcedure;
35
36 import java.io.IOException;
37 import java.net.MalformedURLException;
38 import java.net.URL;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.Comparator;
42 import java.util.HashSet;
43 import java.util.List;
44 import java.util.Set;
45
46 import org.openimaj.citation.annotation.Reference;
47 import org.openimaj.citation.annotation.ReferenceType;
48 import org.openimaj.feature.local.filter.ByteEntropyFilter;
49 import org.openimaj.image.FImage;
50 import org.openimaj.image.ImageUtilities;
51 import org.openimaj.image.MBFImage;
52 import org.openimaj.image.colour.Transforms;
53 import org.openimaj.image.feature.local.engine.DoGSIFTEngine;
54 import org.openimaj.image.feature.local.keypoints.Keypoint;
55 import org.openimaj.image.processing.resize.ResizeProcessor;
56 import org.openimaj.lsh.functions.DoubleGaussianFactory;
57 import org.openimaj.lsh.sketch.IntLSHSketcher;
58 import org.openimaj.util.filter.FilterUtils;
59 import org.openimaj.util.hash.HashFunction;
60 import org.openimaj.util.hash.HashFunctionFactory;
61 import org.openimaj.util.hash.modifier.LSBModifier;
62 import org.openimaj.util.pair.ObjectIntPair;
63
64 import cern.jet.random.engine.MersenneTwister;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 @Reference(
87 author = { "Wei Dong", "Zhe Wang", "Kai Li" },
88 title = "High-Confidence Near-Duplicate Image Detection",
89 type = ReferenceType.Inproceedings,
90 year = "2012",
91 booktitle = "ACM International Conference on Multimedia Retrieval",
92 customData = { "location", "Hong Kong, China" })
93 public class BasicDuplicateImageDatabase<T> {
94 private final int ndims = 128;
95 private final double w = 6.0;
96 private final int nbits = 128;
97 private final float LOG_BASE = 0.001f;
98 private final int maxImageSize = 150;
99 private final int seed = 0;
100 private int minScore = 10;
101
102 private DoGSIFTEngine engine;
103 final ByteEntropyFilter filter;
104 private IntLSHSketcher<double[]> sketcher;
105 protected List<TIntObjectHashMap<Set<T>>> database;
106
107 public BasicDuplicateImageDatabase() {
108 engine = new DoGSIFTEngine();
109 engine.getOptions().setDoubleInitialImage(false);
110
111 filter = new ByteEntropyFilter();
112
113 final MersenneTwister rng = new MersenneTwister(seed);
114 final DoubleGaussianFactory gauss = new DoubleGaussianFactory(this.ndims, rng, w);
115 final HashFunctionFactory<double[]> factory = new HashFunctionFactory<double[]>() {
116 @Override
117 public HashFunction<double[]> create() {
118 return new LSBModifier<double[]>(gauss.create());
119 }
120 };
121
122 sketcher = new IntLSHSketcher<double[]>(factory, nbits);
123 database = new ArrayList<TIntObjectHashMap<Set<T>>>(sketcher.arrayLength());
124
125 for (int i = 0; i < sketcher.arrayLength(); i++)
126 database.add(new TIntObjectHashMap<Set<T>>());
127 }
128
129
130
131
132
133
134
135
136 private double[] logScale(double[] v) {
137 final double[] dfv = new double[v.length];
138 final double s = -Math.log(LOG_BASE);
139
140 for (int i = 0; i < v.length; i++) {
141 double d = (v[i] + 128.0) / 256.0;
142
143 if (d < LOG_BASE)
144 d = LOG_BASE;
145 d = (Math.log(d) + s) / s;
146 if (d > 1.0)
147 d = 1.0;
148
149 dfv[i] = d;
150 }
151 return dfv;
152 }
153
154
155
156
157
158
159
160
161 public int[][] extractFeatures(FImage image) {
162 image = ResizeProcessor.resizeMax(image, maxImageSize);
163
164 final List<Keypoint> features = engine.findFeatures(image);
165 final List<Keypoint> filtered = FilterUtils.filter(features, filter);
166
167 final int[][] sketches = new int[filtered.size()][];
168 for (int i = 0; i < filtered.size(); i++) {
169 final Keypoint k = filtered.get(i);
170 final double[] fv = logScale(k.getFeatureVector().asDoubleVector());
171 sketches[i] = sketcher.createSketch(fv);
172 }
173
174 return sketches;
175 }
176
177
178
179
180
181
182
183
184 public int[][] extractFeatures(MBFImage image) {
185 return extractFeatures(Transforms.calculateIntensityNTSC(image));
186 }
187
188
189
190
191
192
193
194
195
196 public synchronized void indexImage(int[][] features, T metadata) {
197 for (final int[] sketch : features) {
198 for (int i = 0; i < sketch.length; i++) {
199 final int sk = sketch[i];
200 Set<T> s = database.get(i).get(sk);
201 if (s == null)
202 database.get(i).put(sk, s = new HashSet<T>());
203
204 s.add(metadata);
205 }
206 }
207 }
208
209
210
211
212
213
214
215
216
217 public List<ObjectIntPair<T>> search(int[][] features) {
218 final TObjectIntHashMap<T> counter = new TObjectIntHashMap<>();
219
220 for (final int[] sketch : features) {
221 for (int i = 0; i < sketch.length; i++) {
222 final int sk = sketch[i];
223 final Set<T> s = database.get(i).get(sk);
224 if (s != null) {
225 for (final T key : s) {
226 counter.adjustOrPutValue(key, 1, 1);
227 }
228 }
229 }
230 }
231
232 final List<ObjectIntPair<T>> result = new ArrayList<>();
233
234 counter.forEachEntry(new TObjectIntProcedure<T>() {
235 @Override
236 public boolean execute(T a, int b) {
237 if (b > minScore)
238 result.add(ObjectIntPair.pair(a, b));
239 return false;
240 }
241 });
242
243 Collections.sort(result, new Comparator<ObjectIntPair<T>>() {
244 @Override
245 public int compare(ObjectIntPair<T> o1, ObjectIntPair<T> o2) {
246 return Integer.compare(o2.second, o1.second);
247 }
248 });
249
250 return result;
251 }
252
253 public static void main(String[] args) throws MalformedURLException, IOException {
254
255 final BasicDuplicateImageDatabase<String> database = new BasicDuplicateImageDatabase<>();
256
257
258 final int[][] f1 = database.extractFeatures(ImageUtilities.readF(new URL(
259 "http://comp3204.ecs.soton.ac.uk/cw/dog.jpg")));
260 database.indexImage(f1, "dog");
261
262 final int[][] f2 = database.extractFeatures(ImageUtilities.readF(new URL(
263 "http://comp3204.ecs.soton.ac.uk/cw/cat.jpg")));
264 database.indexImage(f2, "cat");
265
266
267 System.out.println(database.search(f1).get(0).first);
268 System.out.println(database.search(f2).get(0).first);
269
270
271 final int[][] f3 = database.extractFeatures(ImageUtilities.readF(new URL(
272 "http://comp3204.ecs.soton.ac.uk/cw/hybrid_image.jpg")));
273 System.out.println(database.search(f3));
274 }
275 }