diff options
author | asuonpaa <34128694+asuonpaa@users.noreply.github.com> | 2019-09-30 16:58:07 +0300 |
---|---|---|
committer | dan sinclair <dsinclair@google.com> | 2019-09-30 09:58:07 -0400 |
commit | 3624da9a117643a3d6cb12f80838f668f763c89e (patch) | |
tree | 4e86c0949bfcaca204ec1b8d8ecc9c46f5029007 /samples | |
parent | d65b37551cc61bbc489064e22db2d1faa319c298 (diff) | |
download | amber-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.txt | 9 | ||||
-rw-r--r-- | samples/image_diff.cc | 142 |
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(); +} |