aboutsummaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
authorasuonpaa <34128694+asuonpaa@users.noreply.github.com>2019-09-30 16:58:07 +0300
committerdan sinclair <dsinclair@google.com>2019-09-30 09:58:07 -0400
commit3624da9a117643a3d6cb12f80838f668f763c89e (patch)
tree4e86c0949bfcaca204ec1b8d8ecc9c46f5029007 /samples
parentd65b37551cc61bbc489064e22db2d1faa319c298 (diff)
downloadamber-3624da9a117643a3d6cb12f80838f668f763c89e.tar.gz
Added standalone tool for doing image comparison. (#673)
Currently only root mean square error comparison is supported. More comparison algorithms will follow as requested in #620.
Diffstat (limited to 'samples')
-rw-r--r--samples/CMakeLists.txt9
-rw-r--r--samples/image_diff.cc142
2 files changed, 151 insertions, 0 deletions
diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt
index d41ee21..3fa1882 100644
--- a/samples/CMakeLists.txt
+++ b/samples/CMakeLists.txt
@@ -65,3 +65,12 @@ add_custom_command(
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Update build-versions.h in the build directory"
)
+
+set(IMAGE_DIFF_SOURCES
+ image_diff.cc
+)
+add_executable(image_diff ${IMAGE_DIFF_SOURCES})
+target_include_directories(image_diff PRIVATE "${CMAKE_BINARY_DIR}")
+target_link_libraries(image_diff libamber "lodepng")
+amber_default_compile_options(image_diff)
+set_target_properties(image_diff PROPERTIES OUTPUT_NAME "image_diff")
diff --git a/samples/image_diff.cc b/samples/image_diff.cc
new file mode 100644
index 0000000..bba657e
--- /dev/null
+++ b/samples/image_diff.cc
@@ -0,0 +1,142 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <iostream>
+#include <vector>
+
+#include "src/buffer.h"
+#include "src/format_parser.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#include "third_party/lodepng/lodepng.h"
+#pragma clang diagnostic pop
+
+namespace {
+
+enum class CompareAlgorithm {
+ kRMSE = 0,
+};
+
+struct Options {
+ std::vector<std::string> input_filenames;
+ bool show_help = false;
+ float tolerance = 1.0f;
+ CompareAlgorithm compare_algorithm = CompareAlgorithm::kRMSE;
+};
+
+const char kUsage[] = R"(Usage: image_diff [options] image1.png image2.png
+
+ options:
+ --rmse -- Compare using RMSE algorithm (default).
+ -t | --tolerance <float> -- Tolerance value for RMSE comparison.
+ -h | --help -- This help text.
+)";
+
+bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
+ for (size_t i = 1; i < args.size(); ++i) {
+ const std::string& arg = args[i];
+ if (arg == "-h" || arg == "--help") {
+ opts->show_help = true;
+ return true;
+ } else if (arg == "--rmse") {
+ opts->compare_algorithm = CompareAlgorithm::kRMSE;
+ } else if (arg == "-t" || arg == "--tolerance") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for " << args[i - 1] << " argument."
+ << std::endl;
+ return false;
+ }
+ opts->tolerance = std::stof(std::string(args[i]));
+ if (opts->tolerance < 0) {
+ std::cerr << "Tolerance must be non-negative." << std::endl;
+ return false;
+ }
+ } else if (!arg.empty()) {
+ opts->input_filenames.push_back(arg);
+ }
+ }
+
+ return true;
+}
+
+amber::Result LoadPngToBuffer(const std::string& filename,
+ amber::Buffer* buffer) {
+ std::vector<unsigned char> image;
+ uint32_t width;
+ uint32_t height;
+ uint32_t error = lodepng::decode(image, width, height, filename.c_str());
+
+ if (error) {
+ std::string result = "PNG decode error: ";
+ result += lodepng_error_text(error);
+ return amber::Result(result);
+ }
+
+ std::vector<amber::Value> values;
+ values.resize(image.size());
+ for (size_t i = 0; i < image.size(); ++i) {
+ values[i].SetIntValue(image[i]);
+ }
+
+ buffer->SetData(values);
+
+ return {};
+}
+
+} // namespace
+
+int main(int argc, const char** argv) {
+ std::vector<std::string> args(argv, argv + argc);
+ Options options;
+
+ if (!ParseArgs(args, &options)) {
+ return 1;
+ }
+
+ if (options.show_help) {
+ std::cout << kUsage << std::endl;
+ return 0;
+ }
+
+ if (options.input_filenames.size() != 2) {
+ std::cerr << "Two input file names are required." << std::endl;
+ return 1;
+ }
+
+ amber::Buffer buffers[2];
+
+ for (size_t i = 0; i < 2; ++i) {
+ amber::FormatParser fp;
+ buffers[i].SetFormat(fp.Parse("R8G8B8A8_UNORM"));
+ amber::Result res =
+ LoadPngToBuffer(options.input_filenames[i], &buffers[i]);
+ if (!res.IsSuccess()) {
+ std::cerr << "Error loading " << options.input_filenames[i] << ": "
+ << res.Error() << std::endl;
+ return 1;
+ }
+ }
+
+ amber::Result res;
+ if (options.compare_algorithm == CompareAlgorithm::kRMSE)
+ res = buffers[0].CompareRMSE(&buffers[1], options.tolerance);
+
+ if (res.IsSuccess())
+ std::cout << "Images similar" << std::endl;
+ else
+ std::cout << "Images differ: " << res.Error() << std::endl;
+
+ return !res.IsSuccess();
+}