1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2003-01-17
7 * Description : Haar 2d transform
8 * Wavelet algorithms, metric and query ideas based on the paper
9 * "Fast Multiresolution Image Querying"
10 * by Charles E. Jacobs, Adam Finkelstein and David H. Salesin.
11 * https://www.cs.washington.edu/homes/salesin/abstracts.html
12 *
13 * Copyright (C) 2003 by Ricardo Niederberger Cabral <nieder at mail dot ru>
14 * Copyright (C) 2008-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
15 * Copyright (C) 2008-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
16 *
17 * This program is free software; you can redistribute it
18 * and/or modify it under the terms of the GNU General
19 * Public License as published by the Free Software Foundation;
20 * either version 2, or (at your option)
21 * any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * ============================================================ */
29
30 #include "haar.h"
31
32 // C++ includes
33
34 #include <cstdlib>
35 #include <cmath>
36 #include <queue>
37
38 // Qt includes
39
40 #include <QImage>
41 #include <QString>
42
43 // Local includes
44
45 #include "dimg.h"
46
47 using namespace std;
48
49 namespace Digikam
50 {
51
52 namespace Haar
53 {
54
55 /**
56 * Signature structure
57 */
58 class Q_DECL_HIDDEN valStruct
59 {
60
61 public:
62
valStruct()63 valStruct()
64 : d(0.0),
65 i(0)
66 {
67 }
68
69 Unit d; ///< [f]abs(a[i])
70 int i; ///< index i of a[i]
71
operator <(const valStruct & right) const72 bool operator< (const valStruct& right) const
73 {
74 return (d > right.d);
75 }
76 };
77
78 typedef std::priority_queue<valStruct> valqueue;
79
80 // --------------------------------------------------------------------
81
82 /**
83 * Write pixels of a QImage in three arrays (one per color channel, pixels linearly)
84 */
fillPixelData(const QImage & im)85 void ImageData::fillPixelData(const QImage& im)
86 {
87 QImage image = im.scaled(Haar::NumberOfPixels, Haar::NumberOfPixels, Qt::IgnoreAspectRatio);
88 int cn = 0;
89
90 for (int h = 0 ; h < Haar::NumberOfPixels ; ++h)
91 {
92 // Get a scanline:
93
94 QRgb* const line = reinterpret_cast<QRgb*>(image.scanLine(h));
95
96 for (int w = 0 ; w < Haar::NumberOfPixels ; ++w)
97 {
98 QRgb pixel = line[w];
99 data1[cn] = qRed (pixel);
100 data2[cn] = qGreen(pixel);
101 data3[cn] = qBlue (pixel);
102 ++cn;
103 }
104 }
105 }
106
107 // --------------------------------------------------------------------
108
109 /**
110 * Write pixels of a DImg in three arrays (one per color channel, pixels linearly)
111 */
fillPixelData(const DImg & im)112 void ImageData::fillPixelData(const DImg& im)
113 {
114 DImg image(im);
115 image.convertToEightBit();
116 image = image.smoothScale(Haar::NumberOfPixels, Haar::NumberOfPixels, Qt::IgnoreAspectRatio);
117 uchar* ptr = image.bits();
118 int cn = 0;
119
120 for (int h = 0 ; h < Haar::NumberOfPixels ; ++h)
121 {
122 for (int w = 0 ; w < Haar::NumberOfPixels ; ++w)
123 {
124 data1[cn] = ptr[2];
125 data2[cn] = ptr[1];
126 data3[cn] = ptr[0];
127 ptr += 4;
128 ++cn;
129 }
130 }
131 }
132
133 // --------------------------------------------------------------------
134
135 /**
136 * Setup initial fixed Haar weights that each coefficient represents
137 */
WeightBin()138 WeightBin::WeightBin()
139 {
140 int i, j;
141
142 /*
143 0 1 2 3 4 5 6 i
144 0 0 1 2 3 4 5 5
145 1 1 1 2 3 4 5 5
146 2 2 2 2 3 4 5 5
147 3 3 3 3 3 4 5 5
148 4 4 4 4 4 4 5 5
149 5 5 5 5 5 5 5 5
150 5 5 5 5 5 5 5 5
151 j
152 */
153
154 // Every position has value 5
155
156 memset(m_bin, 5, NumberOfPixelsSquared);
157
158 // Except for the 5 by 5 upper-left quadrant
159
160 for (i = 0 ; i < 5 ; ++i)
161 {
162 for (j = 0 ; j < 5 ; ++j)
163 {
164 m_bin[i * 128 + j] = qMax(i, j);
165
166 // NOTE: imgBin[0] == 0
167 }
168 }
169 }
170
171 // --------------------------------------------------------------------
172
Calculator()173 Calculator::Calculator()
174 {
175 }
176
~Calculator()177 Calculator::~Calculator()
178 {
179 }
180
181 /**
182 * Do the Haar tensorial 2d transform itself.
183 * Here input is RGB data [0..255] in Unit arrays
184 * Computation is (almost) in-situ.
185 */
haar2D(Unit a[])186 void Calculator::haar2D(Unit a[])
187 {
188 int i;
189 Unit t[NumberOfPixels >> 1];
190
191 // scale by 1/sqrt(128) = 0.08838834764831843:
192 /*
193 for (i = 0 ; i < NUM_PIXELS_SQUARED ; ++i)
194 {
195 a[i] *= 0.08838834764831843;
196 }
197 */
198
199 // Decompose rows:
200
201 for (i = 0 ; i < NumberOfPixelsSquared ; i += NumberOfPixels)
202 {
203 int h, h1;
204 Unit C = 1;
205
206 for (h = NumberOfPixels ; h > 1 ; h = h1)
207 {
208 int j1, j2, k;
209
210 h1 = h >> 1; // h = 2*h1
211 C *= 0.7071; // 1/sqrt(2)
212
213 for (k = 0, j1 = j2 = i ; k < h1 ; ++k, ++j1, j2 += 2)
214 {
215 int j21 = j2+1;
216 t[k] = (a[j2] - a[j21]) * C;
217 a[j1] = (a[j2] + a[j21]);
218 }
219
220 // Write back subtraction results:
221
222 memcpy(a+i+h1, t, h1*sizeof(a[0]));
223 }
224
225 // Fix first element of each row:
226
227 a[i] *= C; // C = 1/sqrt(NUM_PIXELS)
228 }
229
230 // scale by 1/sqrt(128) = 0.08838834764831843:
231 /*
232 for (i = 0; i < NUM_PIXELS_SQUARED; ++i)
233 a[i] *= 0.08838834764831843;
234 */
235
236 // Decompose columns:
237
238 for (i = 0 ; i < NumberOfPixels ; ++i)
239 {
240 Unit C = 1;
241 int h, h1;
242
243 for (h = NumberOfPixels ; h > 1 ; h = h1)
244 {
245 int j1, j2, k;
246
247 h1 = h >> 1;
248 C *= 0.7071; // 1/sqrt(2) = 0.7071
249
250 for (k = 0, j1 = j2 = i ; k < h1 ; ++k, j1 += NumberOfPixels, j2 += 2*NumberOfPixels)
251 {
252 int j21 = j2+NumberOfPixels;
253 t[k] = (a[j2] - a[j21]) * C;
254 a[j1] = (a[j2] + a[j21]);
255 }
256
257 // Write back subtraction results:
258
259 for (k = 0, j1 = i+h1*NumberOfPixels ; k < h1 ; ++k, j1 += NumberOfPixels)
260 {
261 a[j1] = t[k];
262 }
263 }
264
265 // Fix first element of each column:
266
267 a[i] *= C;
268 }
269 }
270
271 /**
272 * Do the Haar tensorial 2d transform itself.
273 * Here input is RGB data [0..255] in Unit arrays.
274 * Results are available in a, b, and c.
275 * Fully inplace calculation; order of result is interleaved though,
276 * but we don't care about that.
277 */
transform(ImageData * const data)278 void Calculator::transform(ImageData* const data)
279 {
280 // RGB -> YIQ colorspace conversion; Y luminance, I,Q chrominance.
281 // If RGB in [0..255] then Y in [0..255] and I,Q in [-127..127].
282
283 Unit* a = data->data1;
284 Unit* b = data->data2;
285 Unit* c = data->data3;
286
287 for (int i = 0 ; i < NumberOfPixelsSquared ; ++i)
288 {
289 Unit Y, I, Q;
290
291 Y = 0.299 * a[i] + 0.587 * b[i] + 0.114 * c[i];
292 I = 0.596 * a[i] - 0.275 * b[i] - 0.321 * c[i];
293 Q = 0.212 * a[i] - 0.523 * b[i] + 0.311 * c[i];
294 a[i] = Y;
295 b[i] = I;
296 c[i] = Q;
297 }
298
299 haar2D(a);
300 haar2D(b);
301 haar2D(c);
302
303 // Reintroduce the skipped scaling factors
304
305 a[0] /= 256 * 128;
306 b[0] /= 256 * 128;
307 c[0] /= 256 * 128;
308 }
309
310 /**
311 * Find the m=NUM_COEFS largest numbers in cdata[] (in magnitude that is)
312 * and store their indices in sig[].
313 * Skips entry 0.
314 */
getmLargests(Unit * const cdata,Idx * const sig)315 void Calculator::getmLargests(Unit* const cdata, Idx* const sig)
316 {
317 int cnt, i;
318 valStruct val;
319 valqueue vq; // dynamic priority queue of valStruct's
320
321 // Could skip i=0: goes into separate avgl
322
323 // Fill up the bounded queue. (Assuming NUM_PIXELS_SQUARED > NUM_COEFS)
324
325 for (i = 1 ; i < NumberOfCoefficients + 1 ; ++i)
326 {
327 val.i = i;
328 val.d = fabs(cdata[i]);
329 vq.push(val);
330 }
331
332 // Queue is full (size is NUM_COEFS)
333
334 for (/*i = NUM_COEFS+1*/ ; i < NumberOfPixelsSquared ; ++i)
335 {
336 val.d = fabs(cdata[i]);
337
338 if (val.d > vq.top().d)
339 {
340 // Make room by dropping smallest entry:
341
342 vq.pop();
343
344 // Insert val as new entry:
345
346 val.i = i;
347 vq.push(val);
348 }
349
350 // else discard: do nothing
351 }
352
353 // Empty the (non-empty) queue and fill-in sig:
354
355 cnt = 0;
356
357 do
358 {
359 int t;
360
361 val = vq.top();
362 t = (cdata[val.i] <= 0); // t = 0 if pos else 1
363 /*
364 i - 0 ^ 0 = i; i - 1 ^ 0b111..1111 = 2-compl(i) = -i
365 */
366 sig[cnt++] = (val.i - t) ^ -t; // never 0
367 vq.pop();
368 }
369 while (!vq.empty());
370
371 // Must have cnt==NUM_COEFS here.
372 }
373
374 /**
375 * Determines a total of NUM_COEFS positions in the image that have the
376 * largest magnitude (absolute value) in color value. Returns linearized
377 * coordinates in sig1, sig2, and sig3. avgl are the [0,0] values.
378 * The order of occurrence of the coordinates in sig doesn't matter.
379 * Complexity is 3 x NUM_PIXELS^2 x 2log(NUM_COEFS).
380 */
calcHaar(ImageData * const data,SignatureData * const sigData)381 int Calculator::calcHaar(ImageData* const data, SignatureData* const sigData)
382 {
383 sigData->avg[0]=data->data1[0];
384 sigData->avg[1]=data->data2[0];
385 sigData->avg[2]=data->data3[0];
386
387 // Color channel 1:
388
389 getmLargests(data->data1, sigData->sig[0]);
390
391 // Color channel 2:
392
393 getmLargests(data->data2, sigData->sig[1]);
394
395 // Color channel 3:
396
397 getmLargests(data->data3, sigData->sig[2]);
398
399 return 1;
400 }
401
402 } // namespace Haar
403
404 } // namespace Digikam
405