File indexing completed on 2025-01-05 03:58:06

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam
0004  *
0005  * Date        : 2013-06-14
0006  * Description : Alignment by Image Congealing.
0007  *               Funneling for complex, realistic images
0008  *               using sequence of distribution fields learned from congealReal
0009  *               Gary B. Huang, Vidit Jain, and Erik Learned-Miller.
0010  *               Unsupervised joint alignment of complex images.
0011  *               International Conference on Computer Vision (ICCV), 2007.
0012  *               Based on Gary B. Huang, UMass-Amherst implementation.
0013  *
0014  * SPDX-FileCopyrightText: 2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0015  *
0016  * SPDX-License-Identifier: GPL-2.0-or-later
0017  *
0018  * ============================================================ */
0019 
0020 #include "funnelreal.h"
0021 
0022 // C++ includes
0023 
0024 #include <cmath>
0025 #include <iostream>
0026 #include <fstream>
0027 #include <vector>
0028 #include <string>
0029 
0030 // Qt includes
0031 
0032 #include <QTest>
0033 #include <QFileInfo>
0034 #include <QStandardPaths>
0035 #include <QRandomGenerator>
0036 
0037 // Local includes
0038 
0039 #include "digikam_debug.h"
0040 #include "dtestdatadir.h"
0041 
0042 namespace Digikam
0043 {
0044 
0045 class Q_DECL_HIDDEN FunnelReal::Private
0046 {
0047 public:
0048 
0049     explicit Private()
0050         : isLoaded          (false),
0051           numParams         (4),          // similarity transforms - x translation, y translation, rotation, uniform scaling
0052           windowSize        (4),
0053           maxProcessAtOnce  (600),        // set based on memory limitations,
0054           outerDimW         (150),
0055           outerDimH         (150),
0056           innerDimW         (100),
0057           innerDimH         (100),
0058           siftHistDim       (4),
0059           siftBucketsDim    (8),
0060           siftDescDim       ((4*windowSize*windowSize*siftBucketsDim) / (siftHistDim*siftHistDim)),
0061           numRandPxls       (0),
0062           numFeatureClusters(0),
0063           edgeDescDim       (0)
0064     {
0065 /*
0066         if (outerDimW - innerDimW < 2*windowSize)
0067         {
0068             qCDebug(DIGIKAM_FACESENGINE_LOG) << "difference between outerDimW and innerDimW is not greater than window size for SIFT descriptor)";
0069             return -1;
0070         }
0071 
0072         if ((outerDimW - innerDimW) % 2 != 0)
0073         {
0074             qCDebug(DIGIKAM_FACESENGINE_LOG) << "shrinking innerDimW by 1 so outerDimW - innerDimW is divisible by 2";
0075             --innerDimW;
0076         }
0077 
0078         if (outerDimH - innerDimH < 2*windowSize)
0079         {
0080             qCDebug(DIGIKAM_FACESENGINE_LOG) << "difference between outerDimH and innerDimH is not greater than window size for SIFT descriptor)";
0081             return -1;
0082         }
0083 
0084         if ((outerDimH - innerDimH) % 2 != 0)
0085         {
0086             qCDebug(DIGIKAM_FACESENGINE_LOG) << "shrinking innerDimH by 1 so outerDimH - innerDimH is divisible by 2";
0087             --innerDimH;
0088         }
0089 */
0090         paddingW = ((outerDimW - innerDimW) - 2*windowSize) / 2;
0091         paddingH = ((outerDimH - innerDimH) - 2*windowSize) / 2;
0092     }
0093 
0094     void loadTrainingData(const QString& path);
0095     void computeGaussian(std::vector<std::vector<float> >& gaussian,
0096                          int windowSize)                                            const;
0097 
0098     /// Part 1: Fills originalFeatures from the image data
0099     void computeOriginalFeatures(std::vector<std::vector<std::vector<float> > >& originalFeatures,
0100                                  const cv::Mat& image,
0101                                  const int width,
0102                                  const int height)                                  const;
0103 
0104     /// Part 2: Returns a small vector contains transformation parameters
0105     std::vector<float> computeTransform(const std::vector<std::vector<std::vector<float> > >& originalFeatures,
0106                                         const int width,
0107                                         const int height)                           const;
0108 
0109     /// Part 3: Applies the transformation (v, form computeTransform) to the given image, returns the result.
0110     cv::Mat applyTransform      (const cv::Mat& image,
0111                                  const std::vector<float>& v,
0112                                  int h,
0113                                  int w)                                             const;
0114 
0115     // Utilities
0116     void getSIFTdescripter      (std::vector<float>& descripter,
0117                                  const std::vector<std::vector<float> >& m,
0118                                  const std::vector<std::vector<float> >& theta,
0119                                  int x, int y, int windowSize, int histDim, int bucketsDim,
0120                                  const std::vector<std::vector<float> >& Gaussian)  const;
0121 
0122     float computeLogLikelihood  (const std::vector<std::vector<float> >& logDistField,
0123                                  const std::vector<std::vector<float> >& fids,
0124                                  int numFeatureClusters) const;
0125 
0126     void  getNewFeatsInvT       (std::vector<std::vector<float> >& newFIDs,
0127                                  const std::vector<std::vector<std::vector<float> > >& originalFeats,
0128                                  const std::vector<float>& vparams,
0129                                  float centerX,
0130                                  float centerY)                                     const;
0131 
0132 public:
0133 
0134     bool                                            isLoaded;
0135 
0136     const int                                       numParams;
0137     const int                                       windowSize;
0138     const int                                       maxProcessAtOnce;
0139 
0140     int                                             outerDimW;
0141     int                                             outerDimH;
0142     int                                             innerDimW;
0143     int                                             innerDimH;
0144     int                                             paddingW;
0145     int                                             paddingH;
0146 
0147     const int                                       siftHistDim;
0148     const int                                       siftBucketsDim;
0149     const int                                       siftDescDim;
0150 
0151     /// Training data
0152     int                                             numRandPxls;
0153     int                                             numFeatureClusters;
0154     int                                             edgeDescDim;
0155     std::vector<std::vector<float> >                centroids;
0156     std::vector<float>                              sigmaSq;
0157     std::vector<std::pair<int, int> >               randPxls;
0158     std::vector<std::vector<std::vector<float> > >  logDFSeq;
0159     std::vector<std::vector<float> >                Gaussian;
0160 };
0161 
0162 // -----------------------------------------------------------------------------------------------------------------------------
0163 
0164 FunnelReal::FunnelReal()
0165     : d(new Private)
0166 {
0167     QString filesPath = DTestDataDir::TestData(QString::fromUtf8("core/tests/faceengine/alignment"))
0168                            .root().path() + QLatin1Char('/');
0169     qCDebug(DIGIKAM_TESTS_LOG) << "Test Data Dir:" << filesPath;
0170 
0171     QString trainingFile = filesPath + QLatin1String("face-funnel.data"); ///< data model file
0172 
0173     if (!QFileInfo::exists(trainingFile))
0174     {
0175         qCritical(DIGIKAM_FACESENGINE_LOG) << "Training data for Congealing/Funnel not found. Should be at" << trainingFile;
0176 
0177         return;
0178     }
0179 
0180     d->loadTrainingData(trainingFile);
0181 }
0182 
0183 FunnelReal::~FunnelReal()
0184 {
0185     delete d;
0186 }
0187 
0188 cv::Mat FunnelReal::align(const cv::Mat& inputImage)
0189 {
0190     if (!d->isLoaded)
0191     {
0192         return inputImage;
0193     }
0194 
0195     cv::Mat scaled, grey, image;
0196 
0197     // resize to outer window size
0198 
0199     cv::resize(inputImage, scaled, cv::Size(d->outerDimW, d->outerDimH), 0, 0,
0200                ((d->outerDimW < inputImage.cols) ? cv::INTER_AREA
0201                                                  : cv::INTER_CUBIC));
0202 
0203     // ensure it's grayscale
0204 
0205     if (scaled.channels() > 1)
0206     {
0207         cvtColor(scaled, grey, CV_RGB2GRAY);
0208     }
0209     else
0210     {
0211         grey = scaled;
0212     }
0213 
0214     // convert to float
0215 
0216     grey.convertTo(image, CV_32F);
0217 
0218     const int height     = image.rows - 2*d->windowSize;
0219     const int width      = image.cols - 2*d->windowSize;
0220 
0221     std::vector<std::vector<std::vector<float> > > originalFeatures;
0222 
0223     d->computeOriginalFeatures(originalFeatures, image, width, height);
0224 
0225     std::vector<float> v = d->computeTransform(originalFeatures, width, height);
0226 
0227     return (d->applyTransform(inputImage, v, d->outerDimH, d->outerDimW));
0228 }
0229 
0230 void FunnelReal::Private::loadTrainingData(const QString& path)
0231 {
0232     try
0233     {
0234         std::ifstream trainingInfo(path.toLocal8Bit().data());
0235         trainingInfo.exceptions(std::ifstream::badbit);
0236 
0237         trainingInfo >> numFeatureClusters >> edgeDescDim;
0238 
0239         std::vector<float> cRow(edgeDescDim, 0);
0240         centroids = std::vector<std::vector<float> >(numFeatureClusters, cRow);
0241         sigmaSq   = std::vector<float>(numFeatureClusters);
0242 
0243         for (int i = 0 ; i < numFeatureClusters ; ++i)
0244         {
0245             for (int j = 0 ; j < edgeDescDim ; ++j)
0246             {
0247                 trainingInfo >> centroids[i][j];
0248             }
0249 
0250             trainingInfo >> sigmaSq[i];
0251         }
0252 
0253         trainingInfo >> numRandPxls;
0254         randPxls  = std::vector<std::pair<int, int> >(numRandPxls);
0255 
0256         for (int j = 0 ; j < numRandPxls ; ++j)
0257         {
0258             trainingInfo >> randPxls[j].first >> randPxls[j].second;
0259         }
0260 
0261         std::vector<float>               dfCol(numFeatureClusters, 0);
0262         std::vector<std::vector<float> > logDistField(numRandPxls, dfCol);
0263 
0264         int iteration;
0265 
0266         while (true)
0267         {
0268             trainingInfo >> iteration;
0269 
0270             if (trainingInfo.eof())
0271             {
0272                 break;
0273             }
0274 
0275             for (int j = 0 ; j < numRandPxls ; ++j)
0276             {
0277                 for (int i = 0 ; i < numFeatureClusters ; ++i)
0278                 {
0279                     trainingInfo >> logDistField[j][i];
0280                 }
0281             }
0282 
0283             logDFSeq.push_back(logDistField);
0284         }
0285     }
0286     catch (const std::ifstream::failure& e)
0287     {
0288         qCritical(DIGIKAM_FACESENGINE_LOG) << "Error loading Congealing/Funnel training data:" << e.what();
0289     }
0290     catch (...)
0291     {
0292         qCritical(DIGIKAM_FACESENGINE_LOG) << "Default exception";
0293     }
0294 
0295     computeGaussian(Gaussian, windowSize);
0296 
0297     isLoaded = true;
0298 }
0299 
0300 void FunnelReal::Private::computeGaussian(std::vector<std::vector<float> >& gaussian, int windowSize) const
0301 {
0302     for (int i = 0 ; i < 2*windowSize ; ++i)
0303     {
0304         std::vector<float> grow(2*windowSize);
0305 
0306         for (int j = 0 ; j < 2*windowSize ; ++j)
0307         {
0308             float ii = i-(windowSize-0.5f);
0309             float jj = j-(windowSize-0.5f);
0310             grow[j]  = exp(-(ii*ii + jj*jj) / (2*windowSize*windowSize));
0311         }
0312 
0313         gaussian.push_back(grow);
0314     }
0315 }
0316 
0317 static float dist(const std::vector<float>& a, const std::vector<float>& b)
0318 {
0319     float r = 0;
0320 
0321     for (int i = 0 ; i < (signed)a.size() ; ++i)
0322     {
0323         r += (a[i]-b[i])*(a[i]-b[i]);
0324     }
0325 
0326     return r;
0327 }
0328 
0329 /**
0330  * Main function, part 1
0331  */
0332 void FunnelReal::Private::computeOriginalFeatures(std::vector<std::vector<std::vector<float> > >& originalFeatures,
0333                                                   const cv::Mat& image,
0334                                                   const int width,
0335                                                   const int height) const
0336 {
0337     std::vector<float>               ofEntry(edgeDescDim, 0);
0338     std::vector<std::vector<float> > ofCol(width, ofEntry);
0339 
0340     originalFeatures = std::vector<std::vector<std::vector<float> > >(height, ofCol);
0341 
0342     std::vector<float> SiftDesc(edgeDescDim);
0343 
0344     std::vector<float>               mtRow(image.cols);
0345     std::vector<std::vector<float> > m(image.rows, mtRow);
0346     std::vector<std::vector<float> > theta(image.rows, mtRow);
0347     float dx, dy;
0348 
0349     for (int j = 0 ; j < image.rows ; ++j)
0350     {
0351         const float* greaterRow;
0352         const float* lesserRow;
0353         const float* row;
0354         row = image.ptr<float>(j);
0355 
0356         if      (j == 0)
0357         {
0358             greaterRow = image.ptr<float>(j+1);
0359             lesserRow  = row;
0360         }
0361         else if (j == image.rows-1)
0362         {
0363             greaterRow = row;
0364             lesserRow  = image.ptr<float>(j-1);
0365         }
0366         else
0367         {
0368             greaterRow = image.ptr<float>(j+1);
0369             lesserRow  = image.ptr<float>(j-1);
0370         }
0371 
0372         for (int k = 0 ; k < image.cols ; ++k)
0373         {
0374             dy = greaterRow[k] - lesserRow[k];
0375 
0376             if      (k == 0)
0377             {
0378                 dx = row[k+1] - row[k];
0379             }
0380             else if (k == image.cols-1)
0381             {
0382                 dx = row[k] - row[k-1];
0383             }
0384             else
0385             {
0386                 dx = row[k+1] - row[k-1];
0387             }
0388 
0389             m[j][k]     = (float)sqrt(dx*dx+dy*dy);
0390             theta[j][k] = (float)atan2(dy,dx) * 180.0f/M_PI;
0391 
0392             if (theta[j][k] < 0)
0393             {
0394                 theta[j][k] += 360.0f;
0395             }
0396         }
0397     }
0398 
0399     for (int j = 0 ; j < height ; ++j)
0400     {
0401         for (int k = 0 ; k < width ; ++k)
0402         {
0403             getSIFTdescripter(SiftDesc,
0404                               m,
0405                               theta,
0406                               j+windowSize,
0407                               k+windowSize,
0408                               windowSize,
0409                               siftHistDim,
0410                               siftBucketsDim,
0411                               Gaussian);
0412 
0413             originalFeatures[j][k] = SiftDesc;
0414         }
0415     }
0416 
0417     for (int j = 0 ; j < height ; ++j)
0418     {
0419         for(int k = 0 ; k < width ; ++k)
0420         {
0421             std::vector<float> distances(numFeatureClusters);
0422             float sum = 0;
0423 
0424             for (int ii = 0 ; ii < numFeatureClusters ; ++ii)
0425             {
0426                 distances[ii] = exp(-dist(originalFeatures[j][k], centroids[ii]) /
0427                                 (2*sigmaSq[ii])) / sqrt(sigmaSq[ii]);
0428 
0429                 sum          += distances[ii];
0430             }
0431 
0432             for (int ii = 0 ; ii < numFeatureClusters ; ++ii)
0433             {
0434                 distances[ii] /= sum;
0435             }
0436 
0437             originalFeatures[j][k] = distances;
0438         }
0439     }
0440 }
0441 
0442 /**
0443  * Main function, part 2
0444  */
0445 std::vector<float> FunnelReal::Private::computeTransform(const std::vector<std::vector<std::vector<float> > >& originalFeatures,
0446                                                          const int width,
0447                                                          const int height) const
0448 {
0449     std::vector<float> v(numParams);
0450 
0451     std::vector<float>               fidsEntry(numFeatureClusters, 0);
0452     std::vector<std::vector<float> > featureIDs(numRandPxls, fidsEntry);
0453 
0454     std::vector<float>               nfEntry(numFeatureClusters, 0);
0455     std::vector<std::vector<float> > newFIDs(numRandPxls, nfEntry);
0456     float centerX = width/2.0f;
0457     float centerY = height/2.0f;
0458 
0459     float d[] = {
0460                     1.0f,
0461                     1.0f,
0462                     (float)M_PI / 180.0f,
0463                     0.02f
0464                 };
0465 
0466     getNewFeatsInvT(featureIDs, originalFeatures, v, centerX, centerY);
0467 
0468     for (uint iter = 0 ; iter < logDFSeq.size() ; ++iter)
0469     {
0470         float oldL = computeLogLikelihood(logDFSeq[iter], featureIDs, numFeatureClusters);
0471 
0472         for (int k = 0 ; k < numParams ; ++k)
0473         {
0474             float dn = QRandomGenerator::global()->bounded(-80, 80)/100.0f;
0475 
0476             if (k > 1)
0477             {
0478                 dn /= 100.0f;
0479             }
0480 
0481             v[k] += (d[k] + dn);
0482 
0483             getNewFeatsInvT(newFIDs, originalFeatures, v, centerX, centerY);
0484             float newL = computeLogLikelihood(logDFSeq[iter], newFIDs, numFeatureClusters);
0485 
0486             if (newL > oldL)
0487             {
0488                 featureIDs = newFIDs;
0489                 oldL       = newL;
0490             }
0491             else
0492             {
0493                 v[k] -= (2*(d[k] + dn));
0494                 getNewFeatsInvT(newFIDs, originalFeatures, v, centerX, centerY);
0495                 newL  = computeLogLikelihood(logDFSeq[iter], newFIDs, numFeatureClusters);
0496 
0497                 if (newL > oldL)
0498                 {
0499                     oldL       = newL;
0500                     featureIDs = newFIDs;
0501                 }
0502                 else
0503                 {
0504                     v[k] += (d[k]+dn);
0505                 }
0506             }
0507         }
0508     }
0509 
0510     return v;
0511 }
0512 
0513 cv::Mat FunnelReal::Private::applyTransform(const cv::Mat& image,
0514                                             const std::vector<float>& v,
0515                                             int h,
0516                                             int w) const
0517 {
0518     float cropT1inv[2][3] = {{1 , 0, image.cols/2.0f}, {0, 1, image.rows/2.0f}};
0519     float cropS1inv[3][3] = {{image.cols/(float)w, 0, 0}, {0, image.rows/(float)h, 0}, {0, 0, 1}};
0520     float cropS2inv[3][3] = {{w/(float)image.cols, 0, 0}, {0, h/(float)image.rows, 0}, {0, 0, 1}};
0521     float cropT2inv[3][3] = {{1, 0, -image.cols/2.0f}, {0, 1, -image.rows/2.0f}, {0, 0, 1}};
0522 
0523     float postM[3][3]     = {{1, 0,  w/2.0f}, {0, 1,  h/2.0f}, {0, 0, 1}};
0524     float preM[3][3]      = {{1, 0, -w/2.0f}, {0, 1, -h/2.0f}, {0, 0, 1}};
0525 
0526     float tM[3][3]        = {{1, 0, v[0]}, {0, 1, v[1]}, {0,0,1}};
0527     float rM[3][3]        = {{cosf(v[2]), -sinf(v[2]), 0}, {sinf(v[2]), cosf(v[2]), 0}, {0, 0, 1}};
0528     float sM[3][3]        = {{expf(v[3]), 0, 0}, {0, expf(v[3]), 0}, {0, 0, 1}};
0529 
0530     cv::Mat tCVM(3, 3, CV_32FC1, tM);
0531     cv::Mat rCVM(3, 3, CV_32FC1, rM);
0532     cv::Mat sCVM(3, 3, CV_32FC1, sM);
0533 
0534     cv::Mat postCVM(3, 3, CV_32FC1, postM);
0535     cv::Mat preCVM(3, 3, CV_32FC1, preM);
0536 
0537     cv::Mat cropT1invCVM(2,3, CV_32FC1, cropT1inv);
0538     cv::Mat cropS1invCVM(3,3, CV_32FC1, cropS1inv);
0539     cv::Mat cropS2invCVM(3,3, CV_32FC1, cropS2inv);
0540     cv::Mat cropT2invCVM(3,3, CV_32FC1, cropT2inv);
0541 
0542     cv::Mat xform(2, 3, CV_32FC1);
0543 
0544     xform = cropT1invCVM * cropS1invCVM;
0545     xform = xform * tCVM;
0546     xform = xform * rCVM;
0547     xform = xform * sCVM;
0548     xform = xform * cropS2invCVM;
0549     xform = xform * cropT2invCVM;
0550 
0551     cv::Mat dst;//(image.rows, image.cols, image.type())
0552     cv::warpAffine(image, dst, xform, image.size(), cv::WARP_INVERSE_MAP /*+ cv::WARP_FILL_OUTLIERS*/ + cv::INTER_CUBIC);
0553 
0554     return dst;
0555 }
0556 
0557 void FunnelReal::Private::getSIFTdescripter(std::vector<float>& descripter,
0558                                             const std::vector<std::vector<float> >& m,
0559                                             const std::vector<std::vector<float> >& theta,
0560                                             int x,
0561                                             int y,
0562                                             int windowSize,
0563                                             int histDim,
0564                                             int bucketsDim,
0565                                             const std::vector<std::vector<float> >& Gaussian) const
0566 {
0567     for (int i = 0 ; i < (signed)descripter.size() ; ++i)
0568     {
0569         descripter[i] = 0;
0570     }
0571 
0572     int histDimWidth = 2 * windowSize / histDim;
0573     float degPerBin  = 360.0f / bucketsDim;
0574 
0575     // weight magnitudes by Gaussian with sigma equal to half window
0576 
0577     std::vector<float> mtimesGRow(2*windowSize);
0578     std::vector<std::vector<float> > mtimesG(2*windowSize, mtimesGRow);
0579 
0580     for (int i = 0 ; i < 2*windowSize ; ++i)
0581     {
0582         for (int j = 0 ; j < 2*windowSize ; ++j)
0583         {
0584             int xx        = x+i-(windowSize-1);
0585             int yy        = y+j-(windowSize-1);
0586             mtimesG[i][j] = m[xx][yy] * Gaussian[i][j];
0587         }
0588     }
0589 
0590     // calculate descripter
0591     // using trilinear interpolation
0592 
0593     int histBin[2], histX[2], histY[2];
0594     float dX[2], dY[2], dBin[2];
0595 
0596     for (int i = 0; i < 2*windowSize ; ++i)
0597     {
0598         for (int j = 0 ; j < 2*windowSize ; ++j)
0599         {
0600             histX[0]      = i/histDim;
0601             histX[1]      = i/histDim;
0602             histY[0]      = j/histDim;
0603             histY[1]      = j/histDim;
0604             dX[1]         = 0;
0605             dY[1]         = 0;
0606 
0607             int iModHD    = i % histDim;
0608             int jModHD    = j % histDim;
0609             int histDimD2 = histDim/2;
0610 
0611             if ( iModHD >= histDimD2 && i < 2*windowSize - histDimD2 )
0612             {
0613                 histX[1] = histX[0] + 1;
0614                 dX[1]    = (iModHD + 0.5f - histDimD2) / histDim;
0615             }
0616 
0617             if ( iModHD < histDimD2 && i >= histDimD2 )
0618             {
0619                 histX[1] = histX[0] - 1;
0620                 dX[1]    = (histDimD2 + 0.5f - iModHD) / histDim;
0621             }
0622 
0623             if ( jModHD >= histDimD2 && j < 2*windowSize - histDimD2 )
0624             {
0625                 histY[1] = histY[0] + 1;
0626                 dY[1]    = (jModHD + 0.5f - histDimD2) / histDim;
0627             }
0628 
0629             if ( jModHD < histDimD2 && j >= histDimD2)
0630             {
0631                 histY[1] = histY[0] - 1;
0632                 dY[1]    = (histDimD2 + 0.5f - jModHD) / histDim;
0633             }
0634 
0635             dX[0]           = 1.0f - dX[1];
0636             dY[0]           = 1.0f - dY[1];
0637 
0638             float histAngle = theta[x+i-(windowSize-1)][y+j-(windowSize-1)];
0639 
0640             histBin[0]      = (int)(histAngle / degPerBin);
0641             histBin[1]      = (histBin[0]+1) % bucketsDim;
0642             dBin[1]         = (histAngle - histBin[0]*degPerBin) / degPerBin;
0643             dBin[0]         = 1.0f-dBin[1];
0644 
0645             for (int histBinIndex = 0 ; histBinIndex < 2 ; ++histBinIndex)
0646             {
0647                 for (int histXIndex = 0; histXIndex < 2 ; ++histXIndex)
0648                 {
0649                     for (int histYIndex = 0; histYIndex < 2 ; ++histYIndex)
0650                     {
0651                         int histNum                           = histX[histXIndex]*histDimWidth + histY[histYIndex];
0652                         int bin                               = histBin[histBinIndex];
0653                         descripter[histNum*bucketsDim + bin] += (mtimesG[i][j] * dX[histXIndex] * dY[histYIndex] * dBin[histBinIndex]);
0654                     }
0655                 }
0656             }
0657         }
0658     }
0659 
0660     // normalize
0661     // threshold values at .2, renormalize
0662 
0663     float sum = 0.0f;
0664 
0665     for (int i = 0 ; i < (signed)descripter.size() ; ++i)
0666     {
0667         sum += descripter[i];
0668     }
0669 
0670     if (sum < .0000001f)
0671     {
0672 /*
0673         float dn = 1.0f / (signed)descripter.size(); // is unused, don't know
0674 */
0675         for (int i = 0 ; i < (signed)descripter.size() ; ++i)
0676         {
0677             descripter[i] = 0;
0678         }
0679 
0680         return;
0681     }
0682 
0683     for (int i = 0 ; i < (signed)descripter.size() ; ++i)
0684     {
0685         descripter[i] /= sum;
0686 
0687         if (descripter[i] > .2f)
0688         {
0689             descripter[i] = .2f;
0690         }
0691     }
0692 
0693     sum = 0.0f;
0694 
0695     for (int i = 0 ; i < (signed)descripter.size() ; ++i)
0696     {
0697         sum += descripter[i];
0698     }
0699 
0700     for (int i = 0 ; i < (signed)descripter.size() ; ++i)
0701     {
0702         descripter[i] /= sum;
0703     }
0704 }
0705 
0706 void FunnelReal::Private::getNewFeatsInvT(std::vector<std::vector<float> >& newFIDs,
0707                                           const std::vector<std::vector<std::vector<float> > >& originalFeats,
0708                                           const std::vector<float>& vparams,
0709                                           float centerX,
0710                                           float centerY) const
0711 {
0712     int numFeats      = (int)newFIDs[0].size();
0713     std::vector<float> uniformDist(numFeats, 1.0f/numFeats);
0714 
0715     float postM[2][3] = {{1, 0,  centerX}, {0, 1, centerY}};
0716     float preM[3][3]  = {{1, 0, -centerX}, {0, 1, -centerY}, {0, 0, 1}};
0717 
0718     float tM[3][3]    = {{1, 0, vparams[0]}, {0, 1, vparams[1]}, {0, 0, 1}};
0719     float rM[3][3]    = {{cosf(vparams[2]), -sinf(vparams[2]), 0}, {sinf(vparams[2]), cosf(vparams[2]), 0}, {0, 0, 1}};
0720     float sM[3][3]    = {{expf(vparams[3]), 0, 0}, {0, expf(vparams[3]), 0}, {0, 0, 1}};
0721 
0722     cv::Mat tCVM(3, 3, CV_32FC1, tM);
0723     cv::Mat rCVM(3, 3, CV_32FC1, rM);
0724     cv::Mat sCVM(3, 3, CV_32FC1, sM);
0725 
0726     cv::Mat postCVM(2, 3, CV_32FC1, postM);
0727     cv::Mat preCVM(3, 3, CV_32FC1, preM);
0728 
0729     cv::Mat xform(2, 3, CV_32FC1);
0730     xform = postCVM * tCVM;
0731     xform = xform   * rCVM;
0732     xform = xform   * sCVM;
0733     xform = xform   * preCVM;
0734 
0735     int height = (signed)originalFeats.size();
0736     int width  = (signed)originalFeats[0].size();
0737 
0738     for (int i = 0 ; i < (signed)newFIDs.size() ; ++i)
0739     {
0740         int j  = randPxls[i].first;
0741         int k  = randPxls[i].second;
0742         int nx = (int)(xform.at<float>(0)*k + xform.at<float>(1)*j + xform.at<float>(2) + 0.5f);
0743         int ny = (int)(xform.at<float>(3)*k + xform.at<float>(4)*j + xform.at<float>(5) + 0.5f);
0744 
0745         if (!(ny >= 0 && ny < height && nx >= 0 && nx < width))
0746         {
0747             newFIDs[i] = uniformDist;
0748         }
0749         else
0750         {
0751             newFIDs[i] = originalFeats[ny][nx];
0752         }
0753     }
0754 }
0755 
0756 float FunnelReal::Private::computeLogLikelihood(const std::vector<std::vector<float> >& logDistField,
0757                                                 const std::vector<std::vector<float> >& fids,
0758                                                 int numFeatureClusters) const
0759 {
0760     float l = 0.0f;
0761 
0762     for (int j = 0 ; j < (signed)fids.size() ; ++j)
0763     {
0764         for (int i = 0 ; i < numFeatureClusters ; ++i)
0765         {
0766             l += fids[j][i] * logDistField[j][i];
0767         }
0768     }
0769 
0770     return l;
0771 }
0772 
0773 } // namespace Digikam