• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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