Skip to content

Commit d31e3fd

Browse files
committed
#34 added ASCII art based image comparison of cropped image data. Images are considered matching when there's less than 2% of image pixels that differ by less than 0x20 across each channel. Note that this is really optimistic when talking images with a lot of edges, as those are subject to very slight scaling artefacts, and the two images compared are created by two different algorithms.
1 parent 061fe26 commit d31e3fd

File tree

1 file changed

+97
-8
lines changed

1 file changed

+97
-8
lines changed

qtgui/SourceImage.cpp

+97-8
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ class SourceImageTester
342342
QMouseEvent mouseReleased(QEvent::Type::MouseButtonRelease, pos, Qt::MouseButton::LeftButton, (canceled ? Qt::MouseButton::RightButton : Qt::MouseButton::NoButton), Qt::KeyboardModifier::NoModifier);
343343
m_imageToTest.mouseReleaseEvent(&mouseReleased);
344344
}
345+
346+
inline QImage& fullImage() { return m_imageToTest.image; }
345347
private:
346348
SourceImage& m_imageToTest;
347349
};
@@ -443,6 +445,71 @@ TEST_CASE("test initial image load") {
443445
testFileLoad(resourceUrl, testImage, QString("Use 0, 0 (w:400, h:400)"), expectedSize);
444446
}
445447

448+
const std::string qrgbToHexString(const QRgb& in_color)
449+
{
450+
std::stringstream formatted{};
451+
formatted << "#" << std::setfill('0') << std::hex;
452+
for (auto i = 0; i < 4; ++i) {
453+
formatted << std::setw(2) << (in_color >> 8*(3-i))%256;
454+
}
455+
return formatted.str();
456+
}
457+
458+
unsigned compareQRgb(const QRgb& value1, const QRgb& value2, uchar maxDifference = 10)
459+
{
460+
auto cv1 = value1;
461+
auto cv2 = value2;
462+
unsigned rv = 0;
463+
for (auto i = 0; i < 4; ++i) {
464+
uchar byte1 = cv1 % 256;
465+
uchar byte2 = cv2 % 256;
466+
unsigned diff = std::abs(byte1 - byte2);
467+
if (diff > rv) rv = diff;
468+
}
469+
return rv;
470+
//return true;
471+
}
472+
473+
struct RangeSelectionTestSet
474+
{
475+
QPoint topLeft;
476+
QPoint bottomRight;
477+
QPoint topLeftOriginal;
478+
QSize sizeOriginal;
479+
QSize sizeScaled() const { return QSize{ bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1 }; }
480+
QString toClippingString() const {
481+
return QString("Use " + QString::number(topLeftOriginal.x()) + ", " + QString::number(topLeftOriginal.y()) + " (w:" + QString::number(sizeOriginal.width()) + ", h:" + QString::number(sizeOriginal.height()) + ")");
482+
}
483+
};
484+
485+
void compareSourceImageData(SourceImage& testImage, SourceImageTester& tester, const RangeSelectionTestSet& testArgs)
486+
{
487+
auto testCroppedSection = testImage.data();
488+
auto testRectangle = tester.fullImage().copy({ testArgs.topLeftOriginal, testArgs.sizeOriginal }).scaledToWidth(testArgs.bottomRight.x() - testArgs.topLeft.x() + 1);
489+
CHECK_EQ(testCroppedSection.width(), testRectangle.width());
490+
CHECK_EQ(testCroppedSection.height(), testRectangle.height());
491+
std::cout << "The comparison isn't 100% accurate around edges. Compare manually!" << std::endl;
492+
std::cout << "The following gives an ASCII art representation of the diff between the two image sections by pixel channel values." << std::endl;
493+
unsigned criticalCount = 0;
494+
for (int y = 0; y < testCroppedSection.height(); y++) {
495+
QRgb* lineToTest = (QRgb*)testCroppedSection.scanLine(y);
496+
QRgb* lineToMatch = (QRgb*)testRectangle.scanLine(y);
497+
std::cout << "|";
498+
for (int x = 0; x < testCroppedSection.width(); x++) {
499+
auto frame = compareQRgb(lineToTest[x], lineToMatch[x]);
500+
char pixel = ' ';
501+
if (frame >= 32) { pixel = 'X'; ++criticalCount; }
502+
else if (frame >= 16) pixel = 'x';
503+
else if (frame >= 8) pixel = '_';
504+
else if (frame >= 4) pixel = '.';
505+
std::cout << pixel;
506+
}
507+
std::cout << "|" << std::endl;
508+
}
509+
auto size = testArgs.sizeScaled();
510+
CHECK_LT(criticalCount, size.width() * size.height() / 50); // less than 1% actually at high contrast by pixel
511+
}
512+
446513
TEST_CASE("test mouse based range selection")
447514
{
448515
SourceImage testImage;
@@ -458,14 +525,36 @@ TEST_CASE("test mouse based range selection")
458525

459526
SourceImageTester tester{ testImage };
460527

461-
// drag rectangle from 10, 10 to 200, 200 computationally
462-
tester.mouseDown({ 10, 10 });
463-
for (qreal pos = 20; pos < 200; pos += 10) tester.mouseMoved({ pos, pos });
464-
tester.mouseUp({ 200, 200 });
465-
466-
// this mouse action should result in a rectangle with 191x191 displayed pixels starting at 10x10 displayed
467-
// this rectangle, if resized to match the overall image size of 925x925, scales to a 441x441 rectangle starting at pixel 23x23.
468-
fileSizeCheck(testImage, QString{"Use 23, 23 (w:441, h:441)"}, QSize{191, 191});
528+
std::vector<RangeSelectionTestSet> movements{
529+
{
530+
{10, 10},
531+
{100, 100},
532+
{23, 23},
533+
{209, 209},
534+
},{
535+
{150, 150},
536+
{300, 300},
537+
{347, 347},
538+
{349, 349},
539+
},{
540+
{250, 250},
541+
{350, 350},
542+
{579, 579},
543+
{233, 233},
544+
}
545+
};
546+
for (auto& parameters : movements)
547+
{
548+
// drag rectangle from 10, 10 to 100, 100 computationally
549+
tester.mouseDown(parameters.topLeft);
550+
for (qreal pos = parameters.topLeft.x() + 10; pos < parameters.bottomRight.x(); pos += 10) tester.mouseMoved({ pos, pos });
551+
tester.mouseUp(parameters.bottomRight);
552+
553+
// this mouse action should result in a rectangle with 191x191 displayed pixels starting at 10x10 displayed
554+
// this rectangle, if resized to match the overall image size of 925x925, scales to a 441x441 rectangle starting at pixel 23x23.
555+
fileSizeCheck(testImage, parameters.toClippingString(), parameters.sizeScaled());
556+
compareSourceImageData(testImage, tester, parameters);
557+
}
469558
}
470559

471560
#endif

0 commit comments

Comments
 (0)