File indexing completed on 2025-03-09 03:52:49

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2003-01-17
0007  * Description : Haar 2d transform
0008  *               Wavelet algorithms, metric and query ideas based on the paper
0009  *               "Fast Multiresolution Image Querying"
0010  *               by Charles E. Jacobs, Adam Finkelstein and David H. Salesin.
0011  *               https://grail.cs.washington.edu/wp-content/uploads/2015/08/jacobs-1995.pdf
0012  *
0013  * SPDX-FileCopyrightText: 2003      by Ricardo Niederberger Cabral <nieder at mail dot ru>
0014  * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0015  * SPDX-FileCopyrightText: 2008-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0016  *
0017  * SPDX-License-Identifier: GPL-2.0-or-later
0018  *
0019  * ============================================================ */
0020 
0021 #ifndef DIGIKAM_HAAR_H
0022 #define DIGIKAM_HAAR_H
0023 
0024 // C++ includes
0025 
0026 #include <cstring>
0027 
0028 // Qt includes
0029 
0030 #include <QtGlobal>
0031 
0032 class QImage;
0033 
0034 namespace Digikam
0035 {
0036 
0037 class DImg;
0038 
0039 namespace Haar
0040 {
0041 
0042 /**
0043  * Weights for the Haar coefficients. Straight from the referenced paper
0044  * "Fast Multiresolution Image Querying"
0045  * by Charles E. Jacobs, Adam Finkelstein and David H. Salesin.
0046  * https://www.cs.washington.edu/homes/salesin/abstracts.html
0047  */
0048 static const float s_haar_weights[2][6][3] =
0049 {
0050     // For scanned picture (sketch=0):
0051     //       Y      I      Q         idx    total       occurs
0052 
0053     {   { 5.00F, 19.21F, 34.37F },   // 0   58.58       1 (`DC' component)
0054         { 0.83F,  1.26F,  0.36F },   // 1    2.45       3
0055         { 1.01F,  0.44F,  0.45F },   // 2    1.90       5
0056         { 0.52F,  0.53F,  0.14F },   // 3    1.19       7
0057         { 0.47F,  0.28F,  0.18F },   // 4    0.93       9
0058         { 0.30F,  0.14F,  0.27F }
0059     },  // 5      0.71    16384-25=16359
0060 
0061     // For handdrawn/painted sketch (sketch=1):
0062     //       Y      I      Q
0063 
0064     {   { 4.04F, 15.14F, 22.62F },
0065         { 0.78F,  0.92F,  0.40F },
0066         { 0.46F,  0.53F,  0.63F },
0067         { 0.42F,  0.26F,  0.25F },
0068         { 0.41F,  0.14F,  0.15F },
0069         { 0.32F,  0.07F,  0.38F }
0070     }
0071 };
0072 
0073 /**
0074  * Number of pixels on one side of image; required to be a power of 2.
0075  */
0076 enum { NumberOfPixels = 128 };
0077 
0078 /**
0079  * Total pixels in a square image.
0080  */
0081 enum { NumberOfPixelsSquared = NumberOfPixels * NumberOfPixels };
0082 
0083 /**
0084  * Number of Haar coefficients we retain as signature for an image.
0085  */
0086 enum { NumberOfCoefficients = 40 };
0087 
0088 typedef double Unit;
0089 
0090 /**
0091  * Keep this definition constant at qint32 (guaranteed binary size!)
0092  */
0093 typedef qint32 Idx;
0094 
0095 // ---------------------------------------------------------------------------------
0096 
0097 class ImageData
0098 {
0099 public:
0100 
0101     Unit data1[NumberOfPixelsSquared] = { 0.0 };
0102     Unit data2[NumberOfPixelsSquared] = { 0.0 };
0103     Unit data3[NumberOfPixelsSquared] = { 0.0 };
0104 
0105     void fillPixelData(const QImage& image);
0106     void fillPixelData(const DImg& image);
0107 };
0108 
0109 // ---------------------------------------------------------------------------------
0110 
0111 class SignatureData
0112 {
0113 public:
0114 
0115     /**
0116      * Y/I/Q positions with largest magnitude
0117      */
0118     Haar::Idx sig[3][Haar::NumberOfCoefficients] = { { 0 } };
0119 
0120     /**
0121      * YIQ for position [0,0]
0122      */
0123     double    avg[3] = { 0.0 };
0124 };
0125 
0126 // ---------------------------------------------------------------------------------
0127 
0128 /**
0129  * This class provides very fast lookup if a certain pixel
0130  * is set (positive or negative) in the loaded coefficient set.
0131  */
0132 class SignatureMap
0133 {
0134 public:
0135 
0136     SignatureMap()
0137     {
0138         m_indexList = new MapIndexType[2 * Haar::NumberOfPixelsSquared];
0139     }
0140 
0141     ~SignatureMap()
0142     {
0143         delete[] m_indexList;
0144     }
0145 
0146     /// Load a set of coefficients
0147 
0148     void fill(const Haar::Idx* const coefs)
0149     {
0150         // For maximum performance, we use a flat array.
0151         // First 16k for negative values, second 16k for positive values.
0152         // All values or false, only 2*40 are true.
0153 
0154         memset(m_indexList, 0, sizeof(MapIndexType[2 * Haar::NumberOfPixelsSquared]));
0155         int x = 0;
0156 
0157         for (int i = 0 ; i < Haar::NumberOfCoefficients ; ++i)
0158         {
0159             x              = coefs[i] + Haar::NumberOfPixelsSquared;
0160             m_indexList[x] = true;
0161         }
0162     }
0163 
0164     /// Query if the given index is set.
0165     /// Index must be in the range -16383..16383.
0166 
0167     bool operator[](Haar::Idx index) const
0168     {
0169         return m_indexList[index + Haar::NumberOfPixelsSquared];
0170     }
0171 
0172 private:
0173 
0174     // To prevent cppcheck warnings.
0175 
0176     explicit SignatureMap(const SignatureMap& other)
0177     {
0178         m_indexList = new MapIndexType[2 * Haar::NumberOfPixelsSquared];
0179         memcpy(m_indexList, other.m_indexList, sizeof(MapIndexType[2 * Haar::NumberOfPixelsSquared]));
0180     }
0181 
0182 public:
0183 
0184     typedef bool  MapIndexType;
0185     MapIndexType* m_indexList  = nullptr;
0186 
0187 private:
0188 
0189     SignatureMap& operator=(const SignatureMap&); // Disable
0190 };
0191 
0192 // ---------------------------------------------------------------------------------
0193 
0194 class WeightBin
0195 {
0196 public:
0197 
0198     explicit WeightBin();
0199 
0200     unsigned char bin(int index)    const
0201     {
0202         return m_bin[index];
0203     }
0204 
0205     unsigned char binAbs(int index) const
0206     {
0207         return (
0208                 (index > 0) ? m_bin[index]
0209                             : m_bin[-index]
0210                );
0211     }
0212 
0213 public:
0214 
0215     /**
0216      * Fixed weight mask for pixel positions (i,j).
0217      * Each entry x = i*NUM_PIXELS + j, gets value max(i,j) saturated at 5.
0218      * To be treated as a constant.
0219      */
0220     unsigned char m_bin[16384] = { 0 };
0221 };
0222 
0223 // ---------------------------------------------------------------------------------
0224 
0225 class Weights
0226 {
0227 public:
0228 
0229     enum SketchType
0230     {
0231         ScannedSketch = 0,
0232         PaintedSketch = 1
0233     };
0234 
0235 public:
0236 
0237     explicit Weights(SketchType type = ScannedSketch)
0238         : m_type(type)
0239     {
0240     }
0241 
0242     float weight(int weight, int channel) const
0243     {
0244         return (s_haar_weights[(int)m_type][weight][channel]);
0245     }
0246 
0247     float weightForAverage(int channel)   const
0248     {
0249         return (s_haar_weights[(int)m_type][0][channel]);
0250     }
0251 
0252 private:
0253 
0254     SketchType m_type;
0255 };
0256 
0257 // ---------------------------------------------------------------------------------
0258 
0259 class Calculator
0260 {
0261 
0262 public:
0263 
0264     explicit Calculator();
0265     ~Calculator();
0266 
0267     int  calcHaar(ImageData* const imageData, SignatureData* const sigData);
0268 
0269     void transform(ImageData* const data);
0270 
0271 private:
0272 
0273     void        haar2D(Unit a[]);
0274     inline void getmLargests(Unit* const cdata, Idx* const sig);
0275 };
0276 
0277 } // namespace Haar
0278 
0279 } // namespace Digikam
0280 
0281 #endif // DIGIKAM_HAAR_H