1e5c31af7Sopenharmony_ci// Copyright 2019 The Amber Authors. 2e5c31af7Sopenharmony_ci// 3e5c31af7Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4e5c31af7Sopenharmony_ci// you may not use this file except in compliance with the License. 5e5c31af7Sopenharmony_ci// You may obtain a copy of the License at 6e5c31af7Sopenharmony_ci// 7e5c31af7Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8e5c31af7Sopenharmony_ci// 9e5c31af7Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10e5c31af7Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11e5c31af7Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12e5c31af7Sopenharmony_ci// See the License for the specific language governing permissions and 13e5c31af7Sopenharmony_ci// limitations under the License. 14e5c31af7Sopenharmony_ci 15e5c31af7Sopenharmony_ci#include <iostream> 16e5c31af7Sopenharmony_ci#include <sstream> 17e5c31af7Sopenharmony_ci#include <vector> 18e5c31af7Sopenharmony_ci 19e5c31af7Sopenharmony_ci#include "src/buffer.h" 20e5c31af7Sopenharmony_ci#include "src/format.h" 21e5c31af7Sopenharmony_ci#include "src/type_parser.h" 22e5c31af7Sopenharmony_ci 23e5c31af7Sopenharmony_ci#pragma clang diagnostic push 24e5c31af7Sopenharmony_ci#pragma clang diagnostic ignored "-Wweak-vtables" 25e5c31af7Sopenharmony_ci#include "third_party/lodepng/lodepng.h" 26e5c31af7Sopenharmony_ci#pragma clang diagnostic pop 27e5c31af7Sopenharmony_ci 28e5c31af7Sopenharmony_cinamespace { 29e5c31af7Sopenharmony_ci 30e5c31af7Sopenharmony_cienum class CompareAlgorithm { kRMSE = 0, kHISTOGRAM_EMD = 1 }; 31e5c31af7Sopenharmony_ci 32e5c31af7Sopenharmony_cistruct Options { 33e5c31af7Sopenharmony_ci std::vector<std::string> input_filenames; 34e5c31af7Sopenharmony_ci bool show_help = false; 35e5c31af7Sopenharmony_ci float tolerance = 1.0f; 36e5c31af7Sopenharmony_ci CompareAlgorithm compare_algorithm = CompareAlgorithm::kRMSE; 37e5c31af7Sopenharmony_ci}; 38e5c31af7Sopenharmony_ci 39e5c31af7Sopenharmony_ciconst char kUsage[] = R"(Usage: image_diff [options] image1.png image2.png 40e5c31af7Sopenharmony_ci 41e5c31af7Sopenharmony_ciExactly one algorithm (and its parameters) must be specified. 42e5c31af7Sopenharmony_ci 43e5c31af7Sopenharmony_ciAlgorithms: 44e5c31af7Sopenharmony_ci 45e5c31af7Sopenharmony_ci --rmse TOLERANCE 46e5c31af7Sopenharmony_ci Compare using the Root Mean Square Error (RMSE) algorithm with 47e5c31af7Sopenharmony_ci a floating point TOLERANCE value in the range 0..255, where 0 48e5c31af7Sopenharmony_ci indicates identical images and 255 indicates images where every 49e5c31af7Sopenharmony_ci color channel has the maximum difference. 50e5c31af7Sopenharmony_ci 51e5c31af7Sopenharmony_ci --histogram_emd TOLERANCE 52e5c31af7Sopenharmony_ci Compare the per-channel color histograms of the images using a 53e5c31af7Sopenharmony_ci variant of the Earth Mover's Distance (EMD) algorithm with a 54e5c31af7Sopenharmony_ci floating point TOLERANCE value in the range 0.0..1.0, where 0.0 55e5c31af7Sopenharmony_ci indicates identical histograms and 1.0 indicates completely 56e5c31af7Sopenharmony_ci different histograms for at least one of the color channels. 57e5c31af7Sopenharmony_ci E.g. an image with red=255 for every pixel vs. an image with 58e5c31af7Sopenharmony_ci red=0 for every pixel. 59e5c31af7Sopenharmony_ci 60e5c31af7Sopenharmony_ciOther options: 61e5c31af7Sopenharmony_ci 62e5c31af7Sopenharmony_ci -h | --help This help text. 63e5c31af7Sopenharmony_ci)"; 64e5c31af7Sopenharmony_ci 65e5c31af7Sopenharmony_cibool ParseArgs(const std::vector<std::string>& args, Options* opts) { 66e5c31af7Sopenharmony_ci int num_algorithms = 0; 67e5c31af7Sopenharmony_ci for (size_t i = 1; i < args.size(); ++i) { 68e5c31af7Sopenharmony_ci const std::string& arg = args[i]; 69e5c31af7Sopenharmony_ci if (arg == "-h" || arg == "--help") { 70e5c31af7Sopenharmony_ci opts->show_help = true; 71e5c31af7Sopenharmony_ci return true; 72e5c31af7Sopenharmony_ci } else if (arg == "--rmse") { 73e5c31af7Sopenharmony_ci num_algorithms++; 74e5c31af7Sopenharmony_ci opts->compare_algorithm = CompareAlgorithm::kRMSE; 75e5c31af7Sopenharmony_ci ++i; 76e5c31af7Sopenharmony_ci if (i >= args.size()) { 77e5c31af7Sopenharmony_ci std::cerr << "Missing tolerance value for RMSE comparison." 78e5c31af7Sopenharmony_ci << std::endl; 79e5c31af7Sopenharmony_ci return false; 80e5c31af7Sopenharmony_ci } 81e5c31af7Sopenharmony_ci std::stringstream sstream(args[i]); 82e5c31af7Sopenharmony_ci sstream >> opts->tolerance; 83e5c31af7Sopenharmony_ci if (sstream.fail()) { 84e5c31af7Sopenharmony_ci std::cerr << "Invalid tolerance value " << args[i] << std::endl; 85e5c31af7Sopenharmony_ci return false; 86e5c31af7Sopenharmony_ci } 87e5c31af7Sopenharmony_ci if (opts->tolerance < 0 || opts->tolerance > 255) { 88e5c31af7Sopenharmony_ci std::cerr << "Tolerance must be in the range 0..255." << std::endl; 89e5c31af7Sopenharmony_ci return false; 90e5c31af7Sopenharmony_ci } 91e5c31af7Sopenharmony_ci 92e5c31af7Sopenharmony_ci } else if (arg == "--histogram_emd") { 93e5c31af7Sopenharmony_ci num_algorithms++; 94e5c31af7Sopenharmony_ci opts->compare_algorithm = CompareAlgorithm::kHISTOGRAM_EMD; 95e5c31af7Sopenharmony_ci ++i; 96e5c31af7Sopenharmony_ci if (i >= args.size()) { 97e5c31af7Sopenharmony_ci std::cerr << "Missing tolerance value for histogram EMD comparison." 98e5c31af7Sopenharmony_ci << std::endl; 99e5c31af7Sopenharmony_ci return false; 100e5c31af7Sopenharmony_ci } 101e5c31af7Sopenharmony_ci std::stringstream sstream(args[i]); 102e5c31af7Sopenharmony_ci sstream >> opts->tolerance; 103e5c31af7Sopenharmony_ci if (sstream.fail()) { 104e5c31af7Sopenharmony_ci std::cerr << "Invalid tolerance value " << args[i] << std::endl; 105e5c31af7Sopenharmony_ci return false; 106e5c31af7Sopenharmony_ci } 107e5c31af7Sopenharmony_ci if (opts->tolerance < 0 || opts->tolerance > 1) { 108e5c31af7Sopenharmony_ci std::cerr << "Tolerance must be in the range 0..1." << std::endl; 109e5c31af7Sopenharmony_ci return false; 110e5c31af7Sopenharmony_ci } 111e5c31af7Sopenharmony_ci } else if (!arg.empty()) { 112e5c31af7Sopenharmony_ci opts->input_filenames.push_back(arg); 113e5c31af7Sopenharmony_ci } 114e5c31af7Sopenharmony_ci } 115e5c31af7Sopenharmony_ci if (num_algorithms == 0) { 116e5c31af7Sopenharmony_ci std::cerr << "No comparison algorithm specified." << std::endl; 117e5c31af7Sopenharmony_ci return false; 118e5c31af7Sopenharmony_ci } else if (num_algorithms > 1) { 119e5c31af7Sopenharmony_ci std::cerr << "Only one comparison algorithm can be specified." << std::endl; 120e5c31af7Sopenharmony_ci return false; 121e5c31af7Sopenharmony_ci } 122e5c31af7Sopenharmony_ci 123e5c31af7Sopenharmony_ci return true; 124e5c31af7Sopenharmony_ci} 125e5c31af7Sopenharmony_ci 126e5c31af7Sopenharmony_ciamber::Result LoadPngToBuffer(const std::string& filename, 127e5c31af7Sopenharmony_ci amber::Buffer* buffer) { 128e5c31af7Sopenharmony_ci std::vector<unsigned char> image; 129e5c31af7Sopenharmony_ci uint32_t width; 130e5c31af7Sopenharmony_ci uint32_t height; 131e5c31af7Sopenharmony_ci uint32_t error = lodepng::decode(image, width, height, filename.c_str()); 132e5c31af7Sopenharmony_ci 133e5c31af7Sopenharmony_ci if (error) { 134e5c31af7Sopenharmony_ci std::string result = "PNG decode error: "; 135e5c31af7Sopenharmony_ci result += lodepng_error_text(error); 136e5c31af7Sopenharmony_ci return amber::Result(result); 137e5c31af7Sopenharmony_ci } 138e5c31af7Sopenharmony_ci 139e5c31af7Sopenharmony_ci std::vector<amber::Value> values; 140e5c31af7Sopenharmony_ci values.resize(image.size()); 141e5c31af7Sopenharmony_ci for (size_t i = 0; i < image.size(); ++i) { 142e5c31af7Sopenharmony_ci values[i].SetIntValue(image[i]); 143e5c31af7Sopenharmony_ci } 144e5c31af7Sopenharmony_ci 145e5c31af7Sopenharmony_ci buffer->SetData(values); 146e5c31af7Sopenharmony_ci 147e5c31af7Sopenharmony_ci return {}; 148e5c31af7Sopenharmony_ci} 149e5c31af7Sopenharmony_ci 150e5c31af7Sopenharmony_ci} // namespace 151e5c31af7Sopenharmony_ci 152e5c31af7Sopenharmony_ciint main(int argc, const char** argv) { 153e5c31af7Sopenharmony_ci std::vector<std::string> args(argv, argv + argc); 154e5c31af7Sopenharmony_ci Options options; 155e5c31af7Sopenharmony_ci 156e5c31af7Sopenharmony_ci if (!ParseArgs(args, &options)) { 157e5c31af7Sopenharmony_ci return 1; 158e5c31af7Sopenharmony_ci } 159e5c31af7Sopenharmony_ci 160e5c31af7Sopenharmony_ci if (options.show_help) { 161e5c31af7Sopenharmony_ci std::cout << kUsage << std::endl; 162e5c31af7Sopenharmony_ci return 0; 163e5c31af7Sopenharmony_ci } 164e5c31af7Sopenharmony_ci 165e5c31af7Sopenharmony_ci if (options.input_filenames.size() != 2) { 166e5c31af7Sopenharmony_ci std::cerr << "Two input file names are required." << std::endl; 167e5c31af7Sopenharmony_ci return 1; 168e5c31af7Sopenharmony_ci } 169e5c31af7Sopenharmony_ci 170e5c31af7Sopenharmony_ci amber::TypeParser parser; 171e5c31af7Sopenharmony_ci auto type = parser.Parse("R8G8B8A8_UNORM"); 172e5c31af7Sopenharmony_ci amber::Format fmt(type.get()); 173e5c31af7Sopenharmony_ci 174e5c31af7Sopenharmony_ci amber::Buffer buffers[2]; 175e5c31af7Sopenharmony_ci for (size_t i = 0; i < 2; ++i) { 176e5c31af7Sopenharmony_ci buffers[i].SetFormat(&fmt); 177e5c31af7Sopenharmony_ci amber::Result res = 178e5c31af7Sopenharmony_ci LoadPngToBuffer(options.input_filenames[i], &buffers[i]); 179e5c31af7Sopenharmony_ci if (!res.IsSuccess()) { 180e5c31af7Sopenharmony_ci std::cerr << "Error loading " << options.input_filenames[i] << ": " 181e5c31af7Sopenharmony_ci << res.Error() << std::endl; 182e5c31af7Sopenharmony_ci return 1; 183e5c31af7Sopenharmony_ci } 184e5c31af7Sopenharmony_ci } 185e5c31af7Sopenharmony_ci 186e5c31af7Sopenharmony_ci amber::Result res; 187e5c31af7Sopenharmony_ci if (options.compare_algorithm == CompareAlgorithm::kRMSE) 188e5c31af7Sopenharmony_ci res = buffers[0].CompareRMSE(&buffers[1], options.tolerance); 189e5c31af7Sopenharmony_ci else if (options.compare_algorithm == CompareAlgorithm::kHISTOGRAM_EMD) 190e5c31af7Sopenharmony_ci res = buffers[0].CompareHistogramEMD(&buffers[1], options.tolerance); 191e5c31af7Sopenharmony_ci 192e5c31af7Sopenharmony_ci if (res.IsSuccess()) 193e5c31af7Sopenharmony_ci std::cout << "Images similar" << std::endl; 194e5c31af7Sopenharmony_ci else 195e5c31af7Sopenharmony_ci std::cout << "Images differ: " << res.Error() << std::endl; 196e5c31af7Sopenharmony_ci 197e5c31af7Sopenharmony_ci return !res.IsSuccess(); 198e5c31af7Sopenharmony_ci} 199