1 // Copyright 2019 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <iostream>
16 #include <vector>
17
18 #include "src/buffer.h"
19 #include "src/format.h"
20 #include "src/type_parser.h"
21
22 #pragma clang diagnostic push
23 #pragma clang diagnostic ignored "-Wweak-vtables"
24 #include "third_party/lodepng/lodepng.h"
25 #pragma clang diagnostic pop
26
27 namespace {
28
29 enum class CompareAlgorithm { kRMSE = 0, kHISTOGRAM_EMD = 1 };
30
31 struct Options {
32 std::vector<std::string> input_filenames;
33 bool show_help = false;
34 float tolerance = 1.0f;
35 CompareAlgorithm compare_algorithm = CompareAlgorithm::kRMSE;
36 };
37
38 const char kUsage[] = R"(Usage: image_diff [options] image1.png image2.png
39
40 options:
41 --rmse -- Compare using RMSE algorithm (default).
42 --histogram_emd -- Compare using histogram EMD algorithm.
43 -t | --tolerance <float> -- Tolerance value for comparison.
44 -h | --help -- This help text.
45 )";
46
ParseArgs(const std::vector<std::string> & args,Options * opts)47 bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
48 for (size_t i = 1; i < args.size(); ++i) {
49 const std::string& arg = args[i];
50 if (arg == "-h" || arg == "--help") {
51 opts->show_help = true;
52 return true;
53 } else if (arg == "--rmse") {
54 opts->compare_algorithm = CompareAlgorithm::kRMSE;
55 } else if (arg == "--histogram_emd") {
56 opts->compare_algorithm = CompareAlgorithm::kHISTOGRAM_EMD;
57 } else if (arg == "-t" || arg == "--tolerance") {
58 ++i;
59 if (i >= args.size()) {
60 std::cerr << "Missing value for " << args[i - 1] << " argument."
61 << std::endl;
62 return false;
63 }
64 opts->tolerance = std::stof(std::string(args[i]));
65 if (opts->tolerance < 0) {
66 std::cerr << "Tolerance must be non-negative." << std::endl;
67 return false;
68 }
69 } else if (!arg.empty()) {
70 opts->input_filenames.push_back(arg);
71 }
72 }
73
74 return true;
75 }
76
LoadPngToBuffer(const std::string & filename,amber::Buffer * buffer)77 amber::Result LoadPngToBuffer(const std::string& filename,
78 amber::Buffer* buffer) {
79 std::vector<unsigned char> image;
80 uint32_t width;
81 uint32_t height;
82 uint32_t error = lodepng::decode(image, width, height, filename.c_str());
83
84 if (error) {
85 std::string result = "PNG decode error: ";
86 result += lodepng_error_text(error);
87 return amber::Result(result);
88 }
89
90 std::vector<amber::Value> values;
91 values.resize(image.size());
92 for (size_t i = 0; i < image.size(); ++i) {
93 values[i].SetIntValue(image[i]);
94 }
95
96 buffer->SetData(values);
97
98 return {};
99 }
100
101 } // namespace
102
main(int argc,const char ** argv)103 int main(int argc, const char** argv) {
104 std::vector<std::string> args(argv, argv + argc);
105 Options options;
106
107 if (!ParseArgs(args, &options)) {
108 return 1;
109 }
110
111 if (options.show_help) {
112 std::cout << kUsage << std::endl;
113 return 0;
114 }
115
116 if (options.input_filenames.size() != 2) {
117 std::cerr << "Two input file names are required." << std::endl;
118 return 1;
119 }
120
121 amber::TypeParser parser;
122 auto type = parser.Parse("R8G8B8A8_UNORM");
123 amber::Format fmt(type.get());
124
125 amber::Buffer buffers[2];
126 for (size_t i = 0; i < 2; ++i) {
127 buffers[i].SetFormat(&fmt);
128 amber::Result res =
129 LoadPngToBuffer(options.input_filenames[i], &buffers[i]);
130 if (!res.IsSuccess()) {
131 std::cerr << "Error loading " << options.input_filenames[i] << ": "
132 << res.Error() << std::endl;
133 return 1;
134 }
135 }
136
137 amber::Result res;
138 if (options.compare_algorithm == CompareAlgorithm::kRMSE)
139 res = buffers[0].CompareRMSE(&buffers[1], options.tolerance);
140 else if (options.compare_algorithm == CompareAlgorithm::kHISTOGRAM_EMD)
141 res = buffers[0].CompareHistogramEMD(&buffers[1], options.tolerance);
142
143 if (res.IsSuccess())
144 std::cout << "Images similar" << std::endl;
145 else
146 std::cout << "Images differ: " << res.Error() << std::endl;
147
148 return !res.IsSuccess();
149 }
150