From 15989e3a89bb10fe93e9dfe139132144001b6e66 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 28 Feb 2019 11:43:16 -0800 Subject: [PATCH] WIP. --- tools/nitpick/src/ImageComparer.cpp | 20 +++++++++++++++-- tools/nitpick/src/ImageComparer.h | 11 +++++++++- tools/nitpick/src/MismatchWindow.cpp | 32 ++++++++++++++++++++++++++++ tools/nitpick/src/MismatchWindow.h | 2 ++ tools/nitpick/src/Nitpick.cpp | 2 +- tools/nitpick/src/Test.cpp | 18 ++++++++++++---- tools/nitpick/src/Test.h | 4 ++-- tools/nitpick/src/common.h | 8 +++++++ 8 files changed, 87 insertions(+), 10 deletions(-) diff --git a/tools/nitpick/src/ImageComparer.cpp b/tools/nitpick/src/ImageComparer.cpp index fa73f97887..b9d556e544 100644 --- a/tools/nitpick/src/ImageComparer.cpp +++ b/tools/nitpick/src/ImageComparer.cpp @@ -12,9 +12,17 @@ #include +ImageComparer::ImageComparer() { + _ssimResults = new SSIMResults(); +} + +ImageComparer::~ImageComparer() { + delete _ssimResults; +} + // Computes SSIM - see https://en.wikipedia.org/wiki/Structural_similarity // The value is computed for the luminance component and the average value is returned -double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) const { +double ImageComparer::compareImages(const QImage& resultImage, const QImage& expectedImage) const { const int L = 255; // (2^number of bits per pixel) - 1 const double K1 { 0.01 }; @@ -96,6 +104,7 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co double numerator = (2.0 * mP * mQ + c1) * (2.0 * sigPQ + c2); double denominator = (mP * mP + mQ * mQ + c1) * (sigsqP + sigsqQ + c2); + _ssimResults->results.push_back(numerator / denominator); ssim += numerator / denominator; ++windowCounter; @@ -106,5 +115,12 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co y = 0; } + _ssimResults->width = (int)(expectedImage.width() / WIN_SIZE); + _ssimResults->height = (int)(expectedImage.height() / WIN_SIZE); + return ssim / windowCounter; -}; \ No newline at end of file +}; + +SSIMResults* ImageComparer::getSSIMResults() { + return _ssimResults; +} diff --git a/tools/nitpick/src/ImageComparer.h b/tools/nitpick/src/ImageComparer.h index 7b7b8b0b74..5bea8151ec 100644 --- a/tools/nitpick/src/ImageComparer.h +++ b/tools/nitpick/src/ImageComparer.h @@ -10,12 +10,21 @@ #ifndef hifi_ImageComparer_h #define hifi_ImageComparer_h +#include "common.h" + #include #include class ImageComparer { public: - double compareImages(QImage resultImage, QImage expectedImage) const; + ImageComparer(); + ~ImageComparer(); + + double compareImages(const QImage& resultImage, const QImage& expectedImage) const; + SSIMResults* getSSIMResults(); + +private: + SSIMResults* _ssimResults; }; #endif // hifi_ImageComparer_h diff --git a/tools/nitpick/src/MismatchWindow.cpp b/tools/nitpick/src/MismatchWindow.cpp index 58189b4795..7649929551 100644 --- a/tools/nitpick/src/MismatchWindow.cpp +++ b/tools/nitpick/src/MismatchWindow.cpp @@ -99,3 +99,35 @@ void MismatchWindow::on_abortTestsButton_clicked() { QPixmap MismatchWindow::getComparisonImage() { return _diffPixmap; } + +QPixmap MismatchWindow::getSSIMResultsImage(SSIMResults* ssimResults) { + // This is an optimization, as QImage.setPixel() is embarrassingly slow + const int ELEMENT_SIZE { 8 }; + unsigned char* buffer = new unsigned char[(ssimResults->height * ELEMENT_SIZE) * (ssimResults->width * ELEMENT_SIZE ) * 3]; + + + // loop over each SSIM result (a double in [-1.0 .. 1.0] + int i { 0 }; + for (int y = 0; y < ssimResults->height; ++y) { + for (int x = 0; x < ssimResults->width; ++x) { + ////QRgb pixelP = expectedImage.pixel(QPoint(x, y)); + + ////// Convert to luminance + ////double p = R_Y * qRed(pixelP) + G_Y * qGreen(pixelP) + B_Y * qBlue(pixelP); + + ////// The intensity value is modified to increase the brightness of the displayed image + ////double absoluteDifference = fabs(p - q) / 255.0; + ////double modifiedDifference = sqrt(absoluteDifference); + + ////int difference = (int)(modifiedDifference * 255.0); + + ////buffer[3 * (x + y * expectedImage.width()) + 0] = difference; + ////buffer[3 * (x + y * expectedImage.width()) + 1] = difference; + ////buffer[3 * (x + y * expectedImage.width()) + 2] = difference; + + ++i; + } + } + + return QPixmap(); +} diff --git a/tools/nitpick/src/MismatchWindow.h b/tools/nitpick/src/MismatchWindow.h index 040e0b8bf1..d80e371ba0 100644 --- a/tools/nitpick/src/MismatchWindow.h +++ b/tools/nitpick/src/MismatchWindow.h @@ -25,7 +25,9 @@ public: UserResponse getUserResponse() { return _userResponse; } QPixmap computeDiffPixmap(QImage expectedImage, QImage resultImage); + QPixmap getComparisonImage(); + QPixmap getSSIMResultsImage(SSIMResults* ssimResults); private slots: void on_passTestButton_clicked(); diff --git a/tools/nitpick/src/Nitpick.cpp b/tools/nitpick/src/Nitpick.cpp index 39800c6bc6..e17ea94634 100644 --- a/tools/nitpick/src/Nitpick.cpp +++ b/tools/nitpick/src/Nitpick.cpp @@ -40,7 +40,7 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) { _ui.plainTextEdit->setReadOnly(true); - setWindowTitle("Nitpick - v3.0.0"); + setWindowTitle("Nitpick - v3.0.1"); clientProfiles << "VR-High" << "Desktop-High" << "Desktop-Low" << "Mobile-Touch" << "VR-Standalone"; _ui.clientProfileComboBox->insertItems(0, clientProfiles); diff --git a/tools/nitpick/src/Test.cpp b/tools/nitpick/src/Test.cpp index e8e284bf32..a1f1bf92eb 100644 --- a/tools/nitpick/src/Test.cpp +++ b/tools/nitpick/src/Test.cpp @@ -100,12 +100,14 @@ int Test::compareImageLists() { }; _mismatchWindow.setTestResult(testResult); + + QPixmap ssimResultsPixMap = _mismatchWindow.getSSIMResultsImage(_imageComparer.getSSIMResults()); if (similarityIndex < THRESHOLD) { ++numberOfFailures; if (!isInteractiveMode) { - appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), true); + appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), ssimResultsPixMap, true); } else { _mismatchWindow.exec(); @@ -113,7 +115,7 @@ int Test::compareImageLists() { case USER_RESPONSE_PASS: break; case USE_RESPONSE_FAIL: - appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), true); + appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), ssimResultsPixMap, true); break; case USER_RESPONSE_ABORT: keepOn = false; @@ -124,7 +126,7 @@ int Test::compareImageLists() { } } } else { - appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), false); + appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), ssimResultsPixMap, false); } _progressBar->setValue(i); @@ -156,7 +158,7 @@ int Test::checkTextResults() { return testsFailed.length(); } -void Test::appendTestResultsToFile(TestResult testResult, QPixmap comparisonImage, bool hasFailed) { +void Test::appendTestResultsToFile(const TestResult& testResult, const QPixmap& comparisonImage, const QPixmap& ssimResultsImage, bool hasFailed) { // Critical error if Test Results folder does not exist if (!QDir().exists(_testResultsFolderPath)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + _testResultsFolderPath + " not found"); @@ -216,6 +218,14 @@ void Test::appendTestResultsToFile(TestResult testResult, QPixmap comparisonImag exit(-1); } + // Create the SSIM results image + sourceFile = testResult._pathname + testResult._actualImageFilename; + destinationFile = resultFolderPath + "/" + "SSIM results.png"; + if (!QFile::copy(sourceFile, destinationFile)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile); + exit(-1); + } + comparisonImage.save(resultFolderPath + "/" + "Difference Image.png"); } diff --git a/tools/nitpick/src/Test.h b/tools/nitpick/src/Test.h index 23011d0c31..752dcbc5af 100644 --- a/tools/nitpick/src/Test.h +++ b/tools/nitpick/src/Test.h @@ -87,7 +87,7 @@ public: void includeTest(QTextStream& textStream, const QString& testPathname); - void appendTestResultsToFile(TestResult testResult, QPixmap comparisonImage, bool hasFailed); + void appendTestResultsToFile(const TestResult& testResult, const QPixmap& comparisonImage, const QPixmap& ssimResultsImage, bool hasFailed); void appendTestResultsToFile(QString testResultFilename, bool hasFailed); bool createTestResultsFolderPath(const QString& directory); @@ -116,7 +116,7 @@ private: const QString TEST_RESULTS_FOLDER { "TestResults" }; const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; - const double THRESHOLD{ 0.935 }; + const double THRESHOLD{ 0.98 }; QDir _imageDirectory; diff --git a/tools/nitpick/src/common.h b/tools/nitpick/src/common.h index 5df4e9c921..ac776995b7 100644 --- a/tools/nitpick/src/common.h +++ b/tools/nitpick/src/common.h @@ -10,6 +10,7 @@ #ifndef hifi_common_h #define hifi_common_h +#include #include class TestResult { @@ -39,4 +40,11 @@ const double R_Y = 0.212655f; const double G_Y = 0.715158f; const double B_Y = 0.072187f; +class SSIMResults { +public: + int width; + int height; + std::vector results; +}; + #endif // hifi_common_h \ No newline at end of file