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