437
456
QMap<qlonglong, double> scores;
439
458
// Variables for data read from DB
459
DatabaseAccess access;
442
463
Haar::SignatureData targetSig;
465
// reference for easier access
466
QMap<qlonglong, Haar::SignatureData> &sigMap = *d->sigMap;
444
468
bool filterByAlbumRoots = !d->albumRootsToSearch.isEmpty();
446
DatabaseAccess access;
448
if (filterByAlbumRoots)
449
query = access.backend()->prepareQuery(QString("SELECT imageid, Albums.albumRoot, matrix FROM ImageHaarMatrix "
450
" LEFT JOIN Images ON Images.id=ImageHaarMatrix.imageid "
451
" LEFT JOIN Albums ON Albums.id=Images.album "
452
" WHERE Images.status=1; "));
454
query = access.backend()->prepareQuery(QString("SELECT imageid, 0, matrix FROM ImageHaarMatrix "
455
" LEFT JOIN Images ON Images.id=ImageHaarMatrix.imageid "
456
" WHERE Images.status=1; "));
457
if (!access.backend()->exec(query))
460
// We don't use DatabaseBackend's convenience calls, as the result set is large
461
// and we try to avoid copying in a temporary QList<QVariant>
470
// if no cache is used or the cache signature map is empty, query the database
471
if ( !d->useCachedSigs || (sigMap.isEmpty() && d->useCachedSigs) )
464
imageid = query.value(0).toLongLong();
465
473
if (filterByAlbumRoots)
467
int albumRootId = query.value(1).toInt();
468
if (!d->albumRootsToSearch.contains(albumRootId))
471
blob.read(query.value(2).toByteArray(), &targetSig);
473
// this is a reference
474
double &score = scores[imageid];
476
// Step 1: Initialize scores with average intensity values of all three channels
477
for (int channel=0; channel<3; channel++)
479
score += weights.weightForAverage(channel) * fabs( querySig->avg[channel] - targetSig.avg[channel] );
482
// Step 2: Decrease the score if query and target have significant coefficients in common
483
for (int channel=0; channel<3; channel++)
485
Haar::Idx *sig = targetSig.sig[channel];
486
Haar::SignatureMap *queryMap = queryMaps[channel];
488
for (int coef = 0; coef < Haar::NumberOfCoefficients; coef++)
490
// x is a pixel index, either positive or negative, 0..16384
492
// If x is a significant coefficient with the same sign in the query signature as well,
493
// descrease the score (lower is better)
494
// Note: both method calls called with x accept positive or negative values
496
score -= weights.weight(d->bin->binAbs(x), channel);
474
query = access.backend()->prepareQuery(QString("SELECT M.imageid, Albums.albumRoot, M.matrix "
475
" FROM ImageHaarMatrix AS M, Albums, Images "
476
" WHERE Images.id=M.imageid "
477
" AND Albums.id=Images.album "
478
" AND Images.status=1; "));
480
query = access.backend()->prepareQuery(QString("SELECT M.imageid, 0, M.matrix "
481
" FROM ImageHaarMatrix AS M, Images "
482
" WHERE Images.id=M.imageid "
483
" AND Images.status=1; "));
484
if (!access.backend()->exec(query))
487
// We don't use DatabaseBackend's convenience calls, as the result set is large
488
// and we try to avoid copying in a temporary QList<QVariant>
491
imageid = query.value(0).toLongLong();
493
if (filterByAlbumRoots)
495
int albumRootId = query.value(1).toInt();
496
if (!d->albumRootsToSearch.contains(albumRootId))
500
blob.read(query.value(2).toByteArray(), &targetSig);
502
if (d->useCachedSigs)
504
sigMap[imageid] = targetSig;
508
double &score = scores[imageid];
509
Haar::SignatureData &qSig = *querySig;
510
Haar::SignatureData &tSig = targetSig;
512
calculateScore(score, qSig, tSig, weights, queryMaps);
516
// read cached signature map if possible
519
foreach (const qlonglong &imageid, sigMap.keys())
521
double &score = scores[imageid];
522
Haar::SignatureData &qSig = *querySig;
523
Haar::SignatureData &tSig = sigMap[imageid];
525
calculateScore(score, qSig, tSig, weights, queryMaps);
715
d->enableCachedSignatures(false);
720
QMap< qlonglong, QList<qlonglong> > HaarIface::findDuplicatesFast(HaarProgressObserver *observer)
722
QMap<qlonglong, QList<qlonglong> > resultsMap;
723
QList<QByteArray> matrixList;
727
int progressStep = 20;
729
DatabaseAccess access;
732
* Step 1: get all fingerprints that are absolutely identical
734
QSqlQuery mainQuery = access.backend()->prepareQuery(QString(
735
"SELECT COUNT(*)AS x, ImageHaarMatrix.matrix "
736
"FROM ImageHaarMatrix, Images "
737
"WHERE Images.id=ImageHaarMatrix.imageid "
738
"AND Images.status=1 "
739
"GROUP BY ImageHaarMatrix.matrix HAVING x>1 ")
742
if (!access.backend()->exec(mainQuery))
745
// get all duplicate fingerprints and calculate total images
746
while (mainQuery.next())
748
matrixList << mainQuery.value(1).toByteArray();
749
_total += mainQuery.value(0).toInt();
752
// --------------------------------------------------------
757
progressStep = qMax(progressStep, total / 100);
758
observer->totalNumberToScan(total);
761
// --------------------------------------------------------
764
* Step 2: get all image ids for each duplicate fingerprint
766
QList<qlonglong> ids;
767
foreach(const QByteArray &matrix, matrixList)
769
QSqlQuery imageQuery = access.backend()->prepareQuery(QString(
771
"FROM Images, ImageHaarMatrix "
772
"WHERE Images.id=ImageHaarMatrix.imageid "
773
"AND Images.status=1 "
774
"AND ImageHaarMatrix.matrix=?; ")
776
imageQuery.bindValue(0, matrix);
777
access.backend()->exec(imageQuery);
781
while (imageQuery.next())
783
ids << imageQuery.value(0).toLongLong();
786
resultsMap[ids.first()] = ids;
788
if (observer && (progress % progressStep == 0))
789
observer->processedNumber(progress);
792
// make sure that the progress bar is really set to maximum now, just in case
793
// we have calculated a wrong amount of duplicate images
795
observer->processedNumber(total);
800
void HaarIface::calculateScore(double &score, Haar::SignatureData &querySig, Haar::SignatureData &targetSig,
801
Haar::Weights &weights, Haar::SignatureMap** queryMaps)
803
// this is a reference
804
// double &score = scores[imageid];
805
// Haar::SignatureData &targetSig = sigMap[imageid];
807
// Step 1: Initialize scores with average intensity values of all three channels
808
for (int channel=0; channel<3; channel++)
810
score += weights.weightForAverage(channel) * fabs( querySig.avg[channel] - targetSig.avg[channel] );
813
// Step 2: Decrease the score if query and target have significant coefficients in common
814
for (int channel=0; channel<3; channel++)
816
Haar::Idx *sig = targetSig.sig[channel];
817
Haar::SignatureMap *queryMap = queryMaps[channel];
819
for (int coef = 0; coef < Haar::NumberOfCoefficients; coef++)
821
// x is a pixel index, either positive or negative, 0..16384
823
// If x is a significant coefficient with the same sign in the query signature as well,
824
// decrease the score (lower is better)
825
// Note: both method calls called with x accept positive or negative values
827
score -= weights.weight(d->bin->binAbs(x), channel);
683
832
} // namespace Digikam