Implement fuzzy image matching in readtest

Images are converted to ARGB32 format, then each byte (ie: each pixel
channel) in the read image is allowed to deviate by some specified
amount from the corresponding byte in the expected image, to allow for
rounding errors etc.

By default, no deviation is permitted, but the XCF tests are allowed a
deviation of 1, as the alpha blending can result in rounding errors
(depending on whether hardware acceleration is used, for example).  In
the end, we are not too concerned about a small deviation that is
invisible to the human eye.

REVIEW: 116567
This commit is contained in:
Alex Merry 2014-03-03 12:57:50 +00:00
parent 895305050c
commit 0f795e6625
2 changed files with 91 additions and 13 deletions

View File

@ -1,11 +1,18 @@
#find_package(Qt5Test ${REQUIRED_QT_VERSION} NO_MODULE) #find_package(Qt5Test ${REQUIRED_QT_VERSION} NO_MODULE)
include(ECMMarkAsTest) include(ECMMarkAsTest)
include(CMakeParseArguments)
add_definitions(-DPLUGIN_DIR="${CMAKE_CURRENT_BINARY_DIR}/../src") add_definitions(-DPLUGIN_DIR="${CMAKE_CURRENT_BINARY_DIR}/../src")
remove_definitions(-DQT_NO_CAST_FROM_ASCII) remove_definitions(-DQT_NO_CAST_FROM_ASCII)
macro(kimageformats_read_tests) macro(kimageformats_read_tests)
cmake_parse_arguments(KIF_RT "" "FUZZ" "" ${ARGN})
set(_fuzzarg)
if (KIF_RT_FUZZ)
set(_fuzzarg -f ${KIF_RT_FUZZ})
endif()
if (NOT TARGET readtest) if (NOT TARGET readtest)
add_executable(readtest readtest.cpp) add_executable(readtest readtest.cpp)
target_link_libraries(readtest Qt5::Gui) target_link_libraries(readtest Qt5::Gui)
@ -13,10 +20,11 @@ macro(kimageformats_read_tests)
PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/read") PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/read")
ecm_mark_as_test(readtest) ecm_mark_as_test(readtest)
endif() endif()
foreach(_testname ${ARGN})
foreach(_testname ${KIF_RT_UNPARSED_ARGUMENTS})
add_test( add_test(
NAME kimageformats-read-${_testname} NAME kimageformats-read-${_testname}
COMMAND readtest ${_testname} COMMAND readtest ${_fuzzarg} ${_testname}
) )
endforeach(_testname) endforeach(_testname)
endmacro() endmacro()
@ -53,6 +61,10 @@ kimageformats_read_tests(
ras ras
rgb rgb
tga tga
)
# Allow some fuzziness when reading this formats, to allow for
# rounding errors (eg: in alpha blending).
kimageformats_read_tests(FUZZ 1
xcf xcf
) )

View File

@ -29,6 +29,8 @@
#include <QImageReader> #include <QImageReader>
#include <QTextStream> #include <QTextStream>
#include "../tests/format-enum.h"
static void writeImageData(const char *name, const QString &filename, const QImage &image) static void writeImageData(const char *name, const QString &filename, const QImage &image)
{ {
QFile file(filename); QFile file(filename);
@ -49,6 +51,27 @@ static void writeImageData(const char *name, const QString &filename, const QIma
} }
} }
// allow each byte to be different by up to 1, to allow for rounding errors
static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness)
{
const int height = im1.height();
const int width = im1.width();
for (int i = 0; i < height; ++i) {
const uchar *line1 = im1.scanLine(i);
const uchar *line2 = im2.scanLine(i);
for (int j = 0; j < width; ++j) {
if (line1[j] > line2[j]) {
if (line1[j] - line2[j] > fuzziness)
return false;
} else {
if (line2[j] - line1[j] > fuzziness)
return false;
}
}
}
return true;
}
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
@ -61,6 +84,11 @@ int main(int argc, char ** argv)
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
parser.addPositionalArgument(QStringLiteral("format"), QStringLiteral("format to test")); parser.addPositionalArgument(QStringLiteral("format"), QStringLiteral("format to test"));
QCommandLineOption fuzz(
QStringList() << QStringLiteral("f") << QStringLiteral("fuzz"),
QStringLiteral("Allow for some deviation in ARGB data."),
QStringLiteral("max"));
parser.addOption(fuzz);
parser.process(app); parser.process(app);
@ -73,6 +101,17 @@ int main(int argc, char ** argv)
parser.showHelp(1); parser.showHelp(1);
} }
uchar fuzziness = 0;
if (parser.isSet(fuzz)) {
bool ok;
uint fuzzarg = parser.value(fuzz).toUInt(&ok);
if (!ok || fuzzarg > 255) {
QTextStream(stderr) << "Error: max fuzz argument must be a number between 0 and 255\n";
parser.showHelp(1);
}
fuzziness = uchar(fuzzarg);
}
QString suffix = args.at(0); QString suffix = args.at(0);
QByteArray format = suffix.toLatin1(); QByteArray format = suffix.toLatin1();
@ -115,20 +154,47 @@ int main(int argc, char ** argv)
++failed; ++failed;
continue; continue;
} }
inputImage = inputImage.convertToFormat(expImage.format()); if (expImage.width() != inputImage.width()) {
if (expImage != inputImage) {
QTextStream(stdout) << "FAIL : " << fi.fileName() QTextStream(stdout) << "FAIL : " << fi.fileName()
<< ": differs from " << expfilename << "\n"; << ": width was " << inputImage.width()
writeImageData("expected data", << " but " << expfilename << " width was "
fi.fileName() + QLatin1String("-expected.data"), << expImage.width() << "\n";
expImage); ++failed;
writeImageData("actual data", } else if (expImage.height() != inputImage.height()) {
fi.fileName() + QLatin1String("-actual.data"), QTextStream(stdout) << "FAIL : " << fi.fileName()
inputImage); << ": height was " << inputImage.height()
<< " but " << expfilename << " height was "
<< expImage.height() << "\n";
++failed; ++failed;
} else { } else {
QTextStream(stdout) << "PASS : " << fi.fileName() << "\n"; if (inputImage.format() != QImage::Format_ARGB32) {
++passed; QTextStream(stdout) << "INFO : " << fi.fileName()
<< ": converting " << fi.fileName()
<< " from " << formatToString(inputImage.format())
<< " to ARGB32\n";
inputImage = inputImage.convertToFormat(QImage::Format_ARGB32);
}
if (expImage.format() != QImage::Format_ARGB32) {
QTextStream(stdout) << "INFO : " << fi.fileName()
<< ": converting " << expfilename
<< " from " << formatToString(expImage.format())
<< " to ARGB32\n";
expImage = expImage.convertToFormat(QImage::Format_ARGB32);
}
if (fuzzyeq(inputImage, expImage, fuzziness)) {
QTextStream(stdout) << "PASS : " << fi.fileName() << "\n";
++passed;
} else {
QTextStream(stdout) << "FAIL : " << fi.fileName()
<< ": differs from " << expfilename << "\n";
writeImageData("expected data",
fi.fileName() + QLatin1String("-expected.data"),
expImage);
writeImageData("actual data",
fi.fileName() + QLatin1String("-actual.data"),
inputImage);
++failed;
}
} }
} }