diff --git a/core/libs/dimg/filters/imgqsort/imagequalitycontainer.cpp b/core/libs/dimg/filters/imgqsort/imagequalitycontainer.cpp index b3fdcf38a7..16423504d6 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalitycontainer.cpp +++ b/core/libs/dimg/filters/imgqsort/imagequalitycontainer.cpp @@ -1,163 +1,163 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2013-08-19 * Description : Image quality Settings Container. * * Copyright (C) 2013-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "imagequalitycontainer.h" // KDE includes #include #include namespace Digikam { ImageQualityContainer::ImageQualityContainer() + : enableSorter(false), + detectBlur(true), + detectNoise(true), + detectCompression(true), + detectExposure(true), + lowQRejected(true), + mediumQPending(true), + highQAccepted(true), + speed(1), + rejectedThreshold(10), + pendingThreshold(40), + acceptedThreshold(60), + blurWeight(100), + noiseWeight(100), + compressionWeight(100) { - enableSorter = false; - detectBlur = true; - detectNoise = true; - detectCompression = true; - detectExposure = true; - lowQRejected = true; - mediumQPending = true; - highQAccepted = true; - rejectedThreshold = 10; - pendingThreshold = 40; - acceptedThreshold = 60; - blurWeight = 100; - noiseWeight = 100; - compressionWeight = 100; - speed = 1; } ImageQualityContainer::ImageQualityContainer(const ImageQualityContainer& other) + : enableSorter(other.enableSorter), + detectBlur(other.detectBlur), + detectNoise(other.detectNoise), + detectCompression(other.detectCompression), + detectExposure(other.detectExposure), + lowQRejected(other.lowQRejected), + mediumQPending(other.mediumQPending), + highQAccepted(other.highQAccepted), + speed(other.speed), + rejectedThreshold(other.rejectedThreshold), + pendingThreshold(other.pendingThreshold), + acceptedThreshold(other.acceptedThreshold), + blurWeight(other.blurWeight), + noiseWeight(other.noiseWeight), + compressionWeight(other.compressionWeight) { - enableSorter = other.enableSorter; - detectBlur = other.detectBlur; - detectNoise = other.detectNoise; - detectCompression = other.detectCompression; - detectExposure = other.detectExposure; - lowQRejected = other.lowQRejected; - mediumQPending = other.mediumQPending; - highQAccepted = other.highQAccepted; - rejectedThreshold = other.rejectedThreshold; - pendingThreshold = other.pendingThreshold; - acceptedThreshold = other.acceptedThreshold; - blurWeight = other.blurWeight; - noiseWeight = other.noiseWeight; - compressionWeight = other.compressionWeight; - speed = other.speed; } ImageQualityContainer& ImageQualityContainer::operator=(const ImageQualityContainer& other) { enableSorter = other.enableSorter; detectBlur = other.detectBlur; detectNoise = other.detectNoise; detectCompression = other.detectCompression; detectExposure = other.detectExposure; lowQRejected = other.lowQRejected; mediumQPending = other.mediumQPending; highQAccepted = other.highQAccepted; rejectedThreshold = other.rejectedThreshold; pendingThreshold = other.pendingThreshold; acceptedThreshold = other.acceptedThreshold; blurWeight = other.blurWeight; noiseWeight = other.noiseWeight; compressionWeight = other.compressionWeight; speed = other.speed; return *this; } ImageQualityContainer::~ImageQualityContainer() { } void ImageQualityContainer::readFromConfig() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("Image Quality Settings"); enableSorter = group.readEntry("Enable Sorter", false); detectBlur = group.readEntry("Detect Blur", true); detectNoise = group.readEntry("Detect Noise", true); detectCompression = group.readEntry("Detect Compression", true); detectExposure = group.readEntry("Detect Exposure", true); lowQRejected = group.readEntry("LowQ Rejected", true); mediumQPending = group.readEntry("MediumQ Pending", true); highQAccepted = group.readEntry("HighQ Accepted", true); speed = group.readEntry("Speed", 1); rejectedThreshold = group.readEntry("Rejected Threshold", 10); pendingThreshold = group.readEntry("Pending Threshold", 40); acceptedThreshold = group.readEntry("Accepted Threshold", 60); blurWeight = group.readEntry("Blur Weight", 100); noiseWeight = group.readEntry("Noise Weight", 100); compressionWeight = group.readEntry("Compression Weight", 100); } void ImageQualityContainer::writeToConfig() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("Image Quality Settings"); group.writeEntry("Enable Sorter", enableSorter); group.writeEntry("Detect Blur", detectBlur); group.writeEntry("Detect Noise", detectNoise); group.writeEntry("Detect Compression", detectCompression); group.writeEntry("Detect Exposure", detectExposure); group.writeEntry("LowQ Rejected", lowQRejected); group.writeEntry("MediumQ Pending", mediumQPending); group.writeEntry("HighQ Accepted", highQAccepted); group.writeEntry("Speed", speed); group.writeEntry("Rejected Threshold", rejectedThreshold); group.writeEntry("Pending Threshold", pendingThreshold); group.writeEntry("Accepted Threshold", acceptedThreshold); group.writeEntry("Blur Weight", blurWeight); group.writeEntry("Noise Weight", noiseWeight); group.writeEntry("Compression Weight", compressionWeight); } QDebug operator<<(QDebug dbg, const ImageQualityContainer& s) { dbg.nospace() << endl; dbg.nospace() << "EnableSorter :" << s.enableSorter << endl; dbg.nospace() << "DetectBlur :" << s.detectBlur << endl; dbg.nospace() << "DetectNoise :" << s.detectNoise << endl; dbg.nospace() << "DetectCompression :" << s.detectCompression << endl; dbg.nospace() << "DetectExposure :" << s.detectExposure << endl; dbg.nospace() << "LowQRejected :" << s.lowQRejected << endl; dbg.nospace() << "MediumQPending :" << s.mediumQPending << endl; dbg.nospace() << "HighQAccepted :" << s.highQAccepted << endl; dbg.nospace() << "Speed :" << s.speed << endl; dbg.nospace() << "Rejected Threshold :" << s.rejectedThreshold << endl; dbg.nospace() << "Pending Threshold :" << s.pendingThreshold << endl; dbg.nospace() << "Accepted Threshold :" << s.acceptedThreshold << endl; dbg.nospace() << "Blur Weight :" << s.blurWeight << endl; dbg.nospace() << "Noise Weight :" << s.noiseWeight << endl; dbg.nospace() << "Compression Weight :" << s.compressionWeight << endl; return dbg.space(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/imgqsort/imagequalitycontainer.h b/core/libs/dimg/filters/imgqsort/imagequalitycontainer.h index 8f3af3c485..f48e071481 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalitycontainer.h +++ b/core/libs/dimg/filters/imgqsort/imagequalitycontainer.h @@ -1,80 +1,80 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2013-08-19 * Description : Image quality Settings Container. * * Copyright (C) 2013-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_IMAGE_QUALITY_CONTAINER_H #define DIGIKAM_IMAGE_QUALITY_CONTAINER_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT ImageQualityContainer { public: ImageQualityContainer(); ImageQualityContainer(const ImageQualityContainer& other); ~ImageQualityContainer(); ImageQualityContainer& operator=(const ImageQualityContainer& other); public: void readFromConfig(); void writeToConfig(); public: - bool enableSorter; /// Global quality dectection enabler/disabler. - - bool detectBlur; /// Enable image blur detection. - bool detectNoise; /// Enable image noise detection. - bool detectCompression; /// Enable image compression detection. - bool detectExposure; /// Enable image over and under exposure detection. - - bool lowQRejected; /// Assign Rejected property to low quality. - bool mediumQPending; /// Assign Pending property to medium quality. - bool highQAccepted; /// Assign Accepted property to high quality. - - int speed; /// Calculation speed. - int rejectedThreshold; /// Item rejection threshold. - int pendingThreshold; /// Item pending threshold. - int acceptedThreshold; /// Item accepted threshold. - int blurWeight; /// Item blur level. - int noiseWeight; /// Item noise level. - int compressionWeight; /// Item compression level. + bool enableSorter; ///< Global quality dectection enabler/disabler. + + bool detectBlur; ///< Enable image blur detection. + bool detectNoise; ///< Enable image noise detection. + bool detectCompression; ///< Enable image compression detection. + bool detectExposure; ///< Enable image over and under exposure detection. + + bool lowQRejected; ///< Assign Rejected property to low quality. + bool mediumQPending; ///< Assign Pending property to medium quality. + bool highQAccepted; ///< Assign Accepted property to high quality. + + int speed; ///< Calculation speed. + int rejectedThreshold; ///< Item rejection threshold. + int pendingThreshold; ///< Item pending threshold. + int acceptedThreshold; ///< Item accepted threshold. + int blurWeight; ///< Item blur level. + int noiseWeight; ///< Item noise level. + int compressionWeight; ///< Item compression level. }; //! qDebug() stream operator. Writes property @a s to the debug output in a nicely formatted way. DIGIKAM_EXPORT QDebug operator<<(QDebug dbg, const ImageQualityContainer& s); } // namespace Digikam #endif // DIGIKAM_IMAGE_QUALITY_CONTAINER_H diff --git a/core/libs/dimg/filters/imgqsort/imagequalityparser.cpp b/core/libs/dimg/filters/imgqsort/imagequalityparser.cpp index a364ec2eb6..2258b2db11 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalityparser.cpp +++ b/core/libs/dimg/filters/imgqsort/imagequalityparser.cpp @@ -1,230 +1,238 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 25/08/2013 * Description : Image Quality Parser * * Copyright (C) 2013-2020 by Gilles Caulier * Copyright (C) 2013-2014 by Gowtham Ashok * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "imagequalityparser_p.h" namespace Digikam { ImageQualityParser::ImageQualityParser(const DImg& image, const ImageQualityContainer& settings, PickLabel* const label) : d(new Private) { d->imq = settings; d->image = image; d->neimage = image; d->label = label; } ImageQualityParser::~ImageQualityParser() { delete d; } void ImageQualityParser::readImage() const { DColor col; int j = 0; d->img8 = d->image; d->img8.convertToEightBit(); // Convert to 8 bits color depth. // grayscale image creation for noise detector d->src_gray = Mat(d->img8.numPixels(), 1, CV_8UC1); // Create a matrix containing the pixel values of grayscaled image for (uint x = 0 ; d->running && (x < d->img8.width()) ; ++x) { for (uint y = 0 ; d->running && (y < d->img8.height()) ; ++y) { col = d->img8.getPixelColor(x, y); d->src_gray.at(x, y) = (col.red() + col.green() + col.blue()) / 3; } } // For Noise detection if (d->imq.detectNoise) { for (int c = 0 ; d->running && (c < 3) ; ++c) { d->fimg[c] = new float[d->neimage.numPixels()]; } j = 0; for (uint y = 0 ; d->running && (y < d->neimage.height()) ; ++y) { for (uint x = 0 ; d->running && (x < d->neimage.width()) ; ++x) { col = d->neimage.getPixelColor(x, y); d->fimg[0][j] = col.red(); d->fimg[1][j] = col.green(); d->fimg[2][j] = col.blue(); ++j; } } } } void ImageQualityParser::startAnalyse() { // For Noise Estimation // Use the Top/Left corner of 256x256 pixels to analyze noise contents from image. // This will speed-up computation time with OpenCV. readImage(); double blur = 0.0; short blur2 = 0; double noise = 0.0; int compressionLevel = 0; double finalQuality = 0.0; double underLevel = 0.0; double overLevel = 0.0; // If blur option is selected in settings, run the blur detection algorithms + if (d->running && d->imq.detectBlur) { // Returns blur value between 0 and 1. // If NaN is returned just assign NoPickLabel + blur = blurDetector(); qCDebug(DIGIKAM_DIMG_LOG) << "Amount of Blur present in image is:" << blur; // Returns blur value between 1 and 32767. // If 1 is returned just assign NoPickLabel + blur2 = blurDetector2(); qCDebug(DIGIKAM_DIMG_LOG) << "Amount of Blur present in image [using LoG Filter] is:" << blur2; } if (d->running && d->imq.detectNoise) { // Some images give very low noise value. Assign NoPickLabel in that case. // Returns noise value between 0 and 1. + noise = noiseDetector(); qCDebug(DIGIKAM_DIMG_LOG) << "Amount of Noise present in image is:" << noise; } if (d->running && d->imq.detectCompression) { // Returns number of blocks in the image. + compressionLevel = compressionDetector(); qCDebug(DIGIKAM_DIMG_LOG) << "Amount of compression artifacts present in image is:" << compressionLevel; } if (d->running && d->imq.detectExposure) { // Returns percents of over-exposure in the image + exposureAmount(underLevel, overLevel); qCDebug(DIGIKAM_DIMG_LOG) << "Under-exposure percents in image is: " << underLevel; qCDebug(DIGIKAM_DIMG_LOG) << "Over-exposure percents in image is: " << overLevel; } #ifdef TRACE QFile filems("imgqsortresult.txt"); if (filems.open(QIODevice::Append | QIODevice::Text)) { QTextStream oms(&filems); oms << "File:" << d->image.originalFilePath() << endl; if (d->imq.detectBlur) { oms << "Blur Present:" << blur << endl; oms << "Blur Present(using LoG filter):"<< blur2 << endl; } if (d->imq.detectNoise) { oms << "Noise Present:" << noise << endl; } if (d->imq.detectCompression) { oms << "Compression Present:" << compressionLevel << endl; } if (d->imq.detectExposure) { oms << "Under-exposure Percents:" << underLevel << endl; oms << "Over-exposure Percents:" << overLevel << endl; } filems.close(); } #endif // TRACE // Calculating finalquality if (d->running) { // All the results to have a range of 1 to 100. + double finalBlur = (blur * 100.0) + ((blur2 / 32767) * 100.0); double finalNoise = noise * 100.0; - double finalCompression = (compressionLevel / 1024.0) * 100.0; // we are processing 1024 pixels size image + double finalCompression = (compressionLevel / 1024.0) * 100.0; // we are processing 1024 pixels size image double finalExposure = 100.0 - (underLevel + overLevel) * 100.0; finalQuality = finalBlur * d->imq.blurWeight + finalNoise * d->imq.noiseWeight + finalCompression * d->imq.compressionWeight + finalExposure; qCDebug(DIGIKAM_DIMG_LOG) << "Final Quality estimated: " << finalQuality; // Assigning PickLabels - if (finalQuality == 0.0) + if (finalQuality == 0.0) { // Algorithms have not been run. So return noPickLabel + *d->label = NoPickLabel; } else if ((int)finalQuality < d->imq.rejectedThreshold) { *d->label = RejectedLabel; } else if (((int)finalQuality > d->imq.rejectedThreshold) && ((int)finalQuality < d->imq.acceptedThreshold)) { *d->label = PendingLabel; } else { *d->label = AcceptedLabel; } } else { *d->label = NoPickLabel; } } void ImageQualityParser::cancelAnalyse() { d->running = false; } } // namespace Digikam diff --git a/core/libs/dimg/filters/imgqsort/imagequalityparser.h b/core/libs/dimg/filters/imgqsort/imagequalityparser.h index 00369ea460..3584967134 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalityparser.h +++ b/core/libs/dimg/filters/imgqsort/imagequalityparser.h @@ -1,89 +1,92 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 25/08/2013 * Description : Image Quality Parser * * Copyright (C) 2013-2020 by Gilles Caulier * Copyright (C) 2013-2014 by Gowtham Ashok * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_IMAGE_QUALITY_PARSER_H #define DIGIKAM_IMAGE_QUALITY_PARSER_H // Local includes #include "dimg.h" #include "digikam_export.h" #include "digikam_globals.h" #include "imagequalitycontainer.h" namespace Digikam { class DIGIKAM_EXPORT ImageQualityParser { public: /** Standard constructor with picklabel container to fill at end of analyze. */ explicit ImageQualityParser(const DImg& image, const ImageQualityContainer& settings, PickLabel* const label); ~ImageQualityParser(); - /** Perform quality estimation and fill Pick Label value accordingly. + /** + * Perform quality estimation and fill Pick Label value accordingly. */ void startAnalyse(); void cancelAnalyse(); private: - /** Internal method dedicated to convert DImg pixels from integer values to float values. - * These ones will by used internally by ImageQualityParser through OpenCV API. + /** + * Internal method dedicated to convert DImg pixels from integer values to float values. + * These ones will by used internally by ImageQualityParser through OpenCV API. */ - void readImage() const; + void readImage() const; /** * @function cannyThreshold * @brief Trackbar callback - Canny thresholds input with a ratio 1:3 */ - void cannyThreshold(int, void*) const; + void cannyThreshold(int, void*) const; - double blurDetector() const; - short blurDetector2() const; - double noiseDetector() const; - int compressionDetector() const; + double blurDetector() const; + short blurDetector2() const; + double noiseDetector() const; + int compressionDetector() const; - /** Detect under and over exposure amount in image. A pure white mask is computed and - * a count of pure color pixels is used to evaluate the over-exposition of shot. + /** + * Detect under and over exposure amount in image. A pure white mask is computed and + * a count of pure color pixels is used to evaluate the over-exposition of shot. */ - void exposureAmount(double& under, double& over) const; + void exposureAmount(double& under, double& over) const; private: // Hidden copy constructor and assignment operator. ImageQualityParser(const ImageQualityParser&); ImageQualityParser& operator=(const ImageQualityParser&); class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_IMAGE_QUALITY_PARSER_H diff --git a/core/libs/dimg/filters/imgqsort/imagequalityparser_blur.cpp b/core/libs/dimg/filters/imgqsort/imagequalityparser_blur.cpp index e375984a16..057a8da284 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalityparser_blur.cpp +++ b/core/libs/dimg/filters/imgqsort/imagequalityparser_blur.cpp @@ -1,109 +1,114 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 25/08/2013 * Description : Image Quality Parser - blur detection * * Copyright (C) 2013-2020 by Gilles Caulier * Copyright (C) 2013-2014 by Gowtham Ashok * * References : https://stackoverflow.com/questions/7765810/is-there-a-way-to-detect-if-an-image-is-blurry * https://stackoverflow.com/questions/35768416/is-here-any-way-to-find-out-whether-an-image-is-blurry-or-not-using-laplacian-op * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "imagequalityparser_p.h" namespace Digikam { void ImageQualityParser::cannyThreshold(int, void*) const { // Reduce noise with a kernel 3x3. + blur(d->src_gray, d->detected_edges, Size(3, 3)); // Canny detector. + Canny(d->detected_edges, d->detected_edges, d->lowThreshold, d->lowThreshold * d->ratio, d->kernel_size); } double ImageQualityParser::blurDetector() const { d->lowThreshold = 0.4; d->ratio = 3; double maxval = 0.0; cannyThreshold(0, nullptr); double average = mean(d->detected_edges)[0]; int* const maxIdx = new int[sizeof(d->detected_edges)]; minMaxIdx(d->detected_edges, nullptr, &maxval, nullptr, maxIdx); double blurresult = average / maxval; qCDebug(DIGIKAM_DIMG_LOG) << "The average of the edge intensity is " << average; qCDebug(DIGIKAM_DIMG_LOG) << "The maximum of the edge intensity is " << maxval; qCDebug(DIGIKAM_DIMG_LOG) << "The result of the edge intensity is " << blurresult; delete [] maxIdx; return blurresult; } short ImageQualityParser::blurDetector2() const { // Algorithm using Laplacian of Gaussian Filter to detect blur. + Mat out; Mat noise_free; qCDebug(DIGIKAM_DIMG_LOG) << "Algorithm using LoG Filter started"; // To remove noise from the image. + GaussianBlur(d->src_gray, noise_free, Size(3, 3), 0, 0, BORDER_DEFAULT); // Aperture size of 1 corresponds to the correct matrix. + int kernel_size = 3; int scale = 1; int delta = 0; int ddepth = CV_16S; Laplacian(noise_free, out, ddepth, kernel_size, scale, delta, BORDER_DEFAULT); // noise_free: The input image without noise. // out: Destination (output) image. // ddepth: Depth of the destination image. Since our input is CV_8U we define ddepth = CV_16S to avoid overflow. // kernel_size: The kernel size of the Sobel operator to be applied internally. We use 3 here. short maxLap = -32767; for (int i = 0 ; i < out.rows ; ++i) { for (int j = 0 ; j < out.cols ; ++j) { short value = out.at(i, j); if (value > maxLap) { maxLap = value; } } } return maxLap; } } // namespace Digikam diff --git a/core/libs/dimg/filters/imgqsort/imagequalityparser_compression.cpp b/core/libs/dimg/filters/imgqsort/imagequalityparser_compression.cpp index d7ccd93853..dd43d33c15 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalityparser_compression.cpp +++ b/core/libs/dimg/filters/imgqsort/imagequalityparser_compression.cpp @@ -1,182 +1,183 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 25/08/2013 * Description : Image Quality Parser - compression detector. * * Copyright (C) 2013-2020 by Gilles Caulier * Copyright (C) 2013-2014 by Gowtham Ashok * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "imagequalityparser_p.h" namespace Digikam { int ImageQualityParser::compressionDetector() const { // FIXME: set threshold value to an acceptable standard to get the number of blocking artifacts + const int THRESHOLD = 30; const int block_size = 8; int countblocks = 0; int number_of_blocks = 0; float sum = 0.0; QList average_bottom; QList average_middle; QList average_top; DColor col; // Go through 8 blocks at a time horizontally // iterating through columns. for (uint i = 0 ; d->running && (i < d->img8.height()) ; ++i) { // Calculating intensity of top column. for (uint j = 0 ; j < d->img8.width() ; j += 8) { sum = 0.0; for (int k = j ; k < block_size ; ++k) { col = d->img8.getPixelColor(i, j); sum += (col.red() + col.green() + col.blue()) / 3.0; } average_top.push_back(sum / 8.0); } // Calculating intensity of middle column. for (uint j = 0 ; j < d->img8.width() ; j += 8) { sum = 0.0; for (uint k = j ; k < block_size ; ++k) { col = d->img8.getPixelColor(i + 1, j); sum += (col.red() + col.green() + col.blue()) / 3.0; } average_middle.push_back(sum / 8.0); } // Calculating intensity of bottom column. countblocks = 0; for (uint j = 0 ; j < d->img8.width() ; j += 8) { sum = 0.0; for (uint k = j ; k < block_size ; ++k) { col = d->img8.getPixelColor(i + 2, j); sum += (col.red() + col.green() + col.blue()) / 3.0; } average_bottom.push_back(sum / 8.0); ++countblocks; } // Check if the average intensity of 8 blocks in the top, middle and bottom rows are equal. // If so increment number_of_blocks. for (int j = 0 ; j < countblocks ; ++j) { if ((average_middle[j] == (average_top[j] + average_bottom[j]) / 2.0) && average_middle[j] > THRESHOLD) { ++number_of_blocks; } } } average_bottom.clear(); average_middle.clear(); average_top.clear(); // Iterating through rows. for (uint j = 0 ; d->running && (j < d->img8.width()) ; ++j) { // Calculating intensity of top row. for (uint i = 0 ; i < d->img8.height() ; i += 8) { sum = 0.0; for (int k = i ; k < block_size ; ++k) { col = d->img8.getPixelColor(i, j); sum += (col.red() + col.green() + col.blue()) / 3.0; } average_top.push_back(sum / 8.0); } // Calculating intensity of middle row. for (uint i = 0 ; i < d->img8.height() ; i += 8) { sum = 0.0; for (uint k = i ; k < block_size ; ++k) { col = d->img8.getPixelColor(i, j + 1); sum += (col.red() + col.green() + col.blue()) / 3.0; } average_middle.push_back(sum / 8.0); } // Calculating intensity of bottom row. countblocks = 0; for (uint i = 0 ; i < d->img8.height() ; i += 8) { sum = 0.0; for (uint k = i ; k < block_size ; ++k) { col = d->img8.getPixelColor(i, j + 2); sum += (col.red() + col.green() + col.blue()) / 3.0; } average_bottom.push_back(sum / 8.0); ++countblocks; } // Check if the average intensity of 8 blocks in the top, middle and bottom rows are equal. // If so increment number_of_blocks. for (int i = 0 ; i < countblocks ; ++i) { if ((average_middle[i] == (average_top[i] + average_bottom[i]) / 2.0) && average_middle[i] > THRESHOLD) { ++number_of_blocks; } } } return number_of_blocks; } } // namespace Digikam diff --git a/core/libs/dimg/filters/imgqsort/imagequalityparser_noise.cpp b/core/libs/dimg/filters/imgqsort/imagequalityparser_noise.cpp index 267176d7c7..25efafbbd7 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalityparser_noise.cpp +++ b/core/libs/dimg/filters/imgqsort/imagequalityparser_noise.cpp @@ -1,300 +1,307 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 25/08/2013 * Description : Image Quality Parser * * Copyright (C) 2013-2020 by Gilles Caulier * Copyright (C) 2013-2014 by Gowtham Ashok * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "imagequalityparser_p.h" namespace Digikam { double ImageQualityParser::noiseDetector() const { double noiseresult = 0.0; //--convert fimg to CvMat*------------------------------------------------------------------------------- // Convert the image into YCrCb color model. + NRFilter::srgb2ycbcr(d->fimg, d->neimage.numPixels()); // One dimensional CvMat which stores the image. + CvMat* points = cvCreateMat(d->neimage.numPixels(), 3, CV_32FC1); // Matrix to store the index of the clusters. + CvMat* clusters = cvCreateMat(d->neimage.numPixels(), 1, CV_32SC1); // Pointer variable to handle the CvMat* points (the image in CvMat format). + float* pointsPtr = reinterpret_cast(points->data.ptr); for (uint x = 0 ; d->running && (x < d->neimage.numPixels()) ; ++x) { for (int y = 0 ; d->running && (y < 3) ; ++y) { *pointsPtr++ = (float)d->fimg[y][x]; } } // Array to store the centers of the clusters. + CvArr* centers = nullptr; qCDebug(DIGIKAM_DIMG_LOG) << "Everything ready for the cvKmeans2 or as it seems to"; //-- KMEANS --------------------------------------------------------------------------------------------- if (d->running) { cvKMeans2(points, d->clusterCount, clusters, cvTermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0), 3, nullptr, 0, centers, nullptr); } qCDebug(DIGIKAM_DIMG_LOG) << "cvKmeans2 successfully run"; //-- Divide into cluster->columns, sample->rows, in matrix standard deviation --------------------------- QScopedArrayPointer rowPosition(new int[d->clusterCount]); // The row position array would just make the hold the number of elements in each cluster. for (uint i = 0 ; d->running && (i < d->clusterCount) ; ++i) { // Initializing the cluster count array. + rowPosition[i] = 0; } int rowIndex, columnIndex; for (uint i = 0 ; d->running && (i < d->neimage.numPixels()) ; ++i) { columnIndex = clusters->data.i[i]; rowPosition[columnIndex]++; } qCDebug(DIGIKAM_DIMG_LOG) << "array indexed, and ready to find maximum"; //-- Finding maximum of the rowPosition array ------------------------------------------------------------ int max = rowPosition[0]; for (uint i = 1 ; d->running && (i < d->clusterCount) ; ++i) { if (rowPosition[i] > max) { max = rowPosition[i]; } } QString maxString; maxString.append(QString::number(max)); qCDebug(DIGIKAM_DIMG_LOG) << QString::fromUtf8("maximum declared = %1").arg(maxString); //-- Divide and conquer --------------------------------------------------------------------------------- CvMat* sd = nullptr; if (d->running) { sd = cvCreateMat(max, (d->clusterCount * points->cols), CV_32FC1); } //-- Initialize the rowPosition array ------------------------------------------------------------------- QScopedArrayPointer rPosition(new int[d->clusterCount]); for (uint i = 0 ; d->running && (i < d->clusterCount) ; ++i) { rPosition[i] = 0; } float* ptr = nullptr; qCDebug(DIGIKAM_DIMG_LOG) << "The rowPosition array is ready!"; for (uint i = 0 ; d->running && (i < d->neimage.numPixels()) ; ++i) { columnIndex = clusters->data.i[i]; rowIndex = rPosition[columnIndex]; // Moving to the right row. ptr = reinterpret_cast(sd->data.ptr + (rowIndex * sd->step)); // Moving to the right column. for (int j = 0 ; d->running && (j < columnIndex) ; ++j) { for (int z = 0 ; d->running && (z < (points->cols)) ; ++z) { ptr++; } } for (int z = 0 ; d->running && (z < (points->cols)) ; ++z) { *ptr++ = cvGet2D(points, i, z).val[0]; } rPosition[columnIndex] = rPosition[columnIndex] + 1; } qCDebug(DIGIKAM_DIMG_LOG) << "sd matrix creation over!"; //-- This part of the code would involve the sd matrix and make the mean and the std of the data ------------- CvScalar std; CvScalar mean; CvMat* meanStore = nullptr; CvMat* stdStore = nullptr; float* meanStorePtr = nullptr; float* stdStorePtr = nullptr; - int totalcount = 0; // Number of non-empty clusters. + int totalcount = 0; // Number of non-empty clusters. if (d->running) { meanStore = cvCreateMat(d->clusterCount, points->cols, CV_32FC1); stdStore = cvCreateMat(d->clusterCount, points->cols, CV_32FC1); meanStorePtr = reinterpret_cast(meanStore->data.ptr); stdStorePtr = reinterpret_cast(stdStore->data.ptr); } for (int i = 0 ; d->running && (i < sd->cols) ; ++i) { if (d->running && (rowPosition[(i / points->cols)] >= 1)) { CvMat* workingArr = cvCreateMat(rowPosition[(i / points->cols)], 1, CV_32FC1); ptr = reinterpret_cast(workingArr->data.ptr); for (int j = 0 ; d->running && (j < rowPosition[(i / (points->cols))]) ; ++j) { *ptr++ = cvGet2D(sd, j, i).val[0]; } cvAvgSdv(workingArr, &mean, &std); *meanStorePtr++ = (float)mean.val[0]; *stdStorePtr++ = (float)std.val[0]; ++totalcount; cvReleaseMat(&workingArr); } } qCDebug(DIGIKAM_DIMG_LOG) << "Make the mean and the std of the data"; // ----------------------------------------------------------------------------------------------------------------- if (d->running) { meanStorePtr = reinterpret_cast(meanStore->data.ptr); stdStorePtr = reinterpret_cast(stdStore->data.ptr); } // Remove clang warnings. + (void)meanStorePtr; (void)stdStorePtr; qCDebug(DIGIKAM_DIMG_LOG) << "Done with the basic work of storing the mean and the std"; //-- Calculating weighted mean, and weighted std ----------------------------------------------------------- QString info; float weightedMean = 0.0F; float weightedStd = 0.0F; float datasd[3] = { 0.0F, 0.0F, 0.0F }; for (int j = 0 ; d->running && (j < points->cols) ; ++j) { meanStorePtr = reinterpret_cast(meanStore->data.ptr); stdStorePtr = reinterpret_cast(stdStore->data.ptr); for (int moveToChannel = 0 ; moveToChannel <= j ; ++moveToChannel) { meanStorePtr++; stdStorePtr++; } for (uint i = 0 ; i < d->clusterCount ; ++i) { if (rowPosition[i] >= 1) { weightedMean += (*meanStorePtr) * rowPosition[i]; weightedStd += (*stdStorePtr) * rowPosition[i]; meanStorePtr += points->cols; stdStorePtr += points->cols; } } weightedMean = weightedMean / (d->neimage.numPixels()); weightedStd = weightedStd / (d->neimage.numPixels()); datasd[j] = weightedStd; info.append(QLatin1String("\n\nChannel: ")); info.append(QString::number(j)); info.append(QLatin1String("\nWeighted Mean: ")); info.append(QString::number(weightedMean)); info.append(QLatin1String("\nWeighted Standard Deviation: ")); info.append(QString::number(weightedStd)); } qCDebug(DIGIKAM_DIMG_LOG) << "Info : " << info; // -- adaptation --------------------------------------------------------------------------------------- if (d->running) { // For 16 bits images only. if (d->neimage.sixteenBit()) { for (int i = 0 ; i < points->cols ; ++i) { datasd[i] = datasd[i] / 256; } } noiseresult = ((datasd[0] / 2) + (datasd[1] / 2) + (datasd[2] / 2)) / 3; qCDebug(DIGIKAM_DIMG_LOG) << "All is completed"; //-- releasing matrices and closing files ---------------------------------------------------------------------- cvReleaseMat(&sd); cvReleaseMat(&stdStore); cvReleaseMat(&meanStore); cvReleaseMat(&points); cvReleaseMat(&clusters); for (uint i = 0 ; i < 3 ; ++i) { delete [] d->fimg[i]; } } return noiseresult; } } // namespace Digikam diff --git a/core/libs/dimg/filters/imgqsort/imagequalityparser_p.h b/core/libs/dimg/filters/imgqsort/imagequalityparser_p.h index a70a8c7467..5a3c21c733 100644 --- a/core/libs/dimg/filters/imgqsort/imagequalityparser_p.h +++ b/core/libs/dimg/filters/imgqsort/imagequalityparser_p.h @@ -1,122 +1,122 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 25/08/2013 * Description : Image Quality Parser - private container * * Copyright (C) 2013-2020 by Gilles Caulier * Copyright (C) 2013-2014 by Gowtham Ashok * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_IMAGE_QUALITY_PARSER_P_H #define DIGIKAM_IMAGE_QUALITY_PARSER_P_H #include "imagequalityparser.h" // C++ includes #include #include #include // Qt includes #include #include #include // Local includes #include "digikam_opencv.h" #include "digikam_debug.h" #include "mixerfilter.h" #include "nrfilter.h" #include "nrestimate.h" #include "exposurecontainer.h" // To switch on/off log trace file. // #define TRACE 1 using namespace cv; namespace Digikam { class Q_DECL_HIDDEN ImageQualityParser::Private { public: explicit Private() - : clusterCount(30), // used for k-means clustering algorithm in noise detection + : clusterCount(30), ///< used for k-means clustering algorithm in noise detection size(512) { for (int c = 0 ; c < 3 ; ++c) { fimg[c] = nullptr; } // Setting the default values edgeThresh = 1; lowThreshold = 0.4; ratio = 3; kernel_size = 3; blurrejected = 0.0; blur = 0.0; acceptedThreshold = 0.0; pendingThreshold = 0.0; rejectedThreshold = 0.0; label = nullptr; running = true; } float* fimg[3]; const uint clusterCount; - const uint size; // Size of squared original image. + const uint size; ///< Size of squared original image. - Mat src_gray; // Matrix of the grayscaled source image - Mat detected_edges; // Matrix containing only edges in the image + Mat src_gray; ///< Matrix of the grayscaled source image + Mat detected_edges; ///< Matrix containing only edges in the image - int edgeThresh; // threshold above which we say that edges are present at a point - int ratio; // lower:upper threshold for canny edge detector algorithm - int kernel_size; // kernel size for the Sobel operations to be performed internally by the edge detector + int edgeThresh; ///< threshold above which we say that edges are present at a point + int ratio; ///< lower:upper threshold for canny edge detector algorithm + int kernel_size; ///< kernel size for the Sobel operations to be performed internally by the edge detector double lowThreshold; - DImg image; // original image - DImg neimage; // noise estimation image[for color] - DImg img8; // compression detector image on 8 bits + DImg image; ///< original image + DImg neimage; ///< noise estimation image[for color] + DImg img8; ///< compression detector image on 8 bits ImageQualityContainer imq; double blurrejected; double blur; double acceptedThreshold; double pendingThreshold; double rejectedThreshold; - QString path; // Path to host result file + QString path; ///< Path to host result file PickLabel* label; volatile bool running; }; } // namespace Digikam #endif // DIGIKAM_IMAGE_QUALITY_PARSER_P_H