OpenJPH
Open-source implementation of JPEG2000 Part-15
convert_mse_pae_to_tests.cpp
Go to the documentation of this file.
1//***************************************************************************/
2// This software is released under the 2-Clause BSD license, included
3// below.
4//
5// Copyright (c) 2019, Aous Naman
6// Copyright (c) 2019, Kakadu Software Pty Ltd, Australia
7// Copyright (c) 2019, The University of New South Wales, Australia
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// 1. Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//
16// 2. Redistributions in binary form must reproduce the above copyright
17// notice, this list of conditions and the following disclaimer in the
18// documentation and/or other materials provided with the distribution.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//***************************************************************************/
32// This file is part of the OpenJPH software implementation.
33// File: convert_mse_pae_to_test.cpp
34// Author: Aous Naman
35// Date: 30 December 2022
36//***************************************************************************/
37
38#include <iostream>
39#include <fstream>
40#include <iomanip>
41
42/******************************************************************************/
43// This code is used to generate the tests in test_exceutables.cpp.
44// It uses data from earlier tests, which have been retired, for this purpose.
45/******************************************************************************/
46
48// Removes white space in the file buffer
49void eat_white_spaces(std::ifstream& file) {
50 int c = file.get();
51 while(1)
52 {
53 if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
54 c = file.get();
55 else if (c == '#')
56 {
57 while (c != '\n')
58 c = file.get();
59 }
60 else
61 {
62 file.unget();
63 break;
64 }
65 }
66}
67
69// Replaces double space with a single space
70void remove_double_spaces(std::string& str)
71{
72 size_t pos = str.find(" ");
73 while (pos != std::string::npos)
74 {
75 str.erase(pos, 1);
76 pos = str.find(" ");
77 }
78}
79
81// Removes the back slash in \{ and \} sequences in strings
82void remove_back_slashes(std::string& str)
83{
84 size_t pos;
85 pos = str.find("\\{");
86 while (pos != std::string::npos)
87 {
88 str.erase(pos, 1);
89 pos = str.find("\\{");
90 }
91 pos = str.find("\\}");
92 while (pos != std::string::npos)
93 {
94 str.erase(pos, 1);
95 pos = str.find("\\}");
96 }
97}
98
100// Convert { and } to \"{ and }\"
101void insert_quotes(std::string& str)
102{
103 size_t pos;
104 pos = str.find("{");
105 while (pos != std::string::npos)
106 {
107 str.insert(pos, "\\\"");
108 pos = str.find("{", pos + 4);
109 }
110 pos = str.find("}");
111 while (pos != std::string::npos)
112 {
113 str.insert(pos + 1, "\\\"");
114 pos = str.find("}", pos + 5);
115 }
116}
117
119// Removes underscores from name, and replaces the following letter with
120// the capital letter version of it
121std::string prepare_test_name(std::string name)
122{
123 name[0] = toupper(name[0]);
124 size_t pos = name.find("_");
125 while (pos != std::string::npos)
126 {
127 name.erase(pos, 1);
128 name[pos] = toupper(name[pos]);
129 pos = name.find("_", pos);
130 }
131 return name;
132}
133
135// Reads and processes ht_cmdlines.txt to extract the command line for
136// base_filename, extracting the command line and yuv_specs if they exist.
137// Extra_cmd_options are only useful for encoding (ojph_compress)
138void process_cmdlines(std::ifstream& file,
139 const std::string base_filename,
140 std::string& src_filename,
141 std::string& comment, std::string& yuv_specs,
142 std::string& extra_cmd_options)
143{
144 file.seekg(std::ios_base::beg);
145 while (file.good())
146 {
147 std::string line;
148 std::getline(file, line);
150
151 size_t pos = line.find(base_filename);
152 if (pos != std::string::npos)
153 {
154 size_t start_pos = line.find("-i");
155 if (start_pos != std::string::npos) {
156 start_pos = line.find("/", start_pos);
157 if (start_pos == std::string::npos) {
158 printf("Formatting error in cmdlines file, pos -1\n");
159 exit(-1);
160 }
161 size_t end_pos = line.find(" ", start_pos);
162 if (start_pos == std::string::npos) {
163 printf("Formatting error in cmdlines file, pos 0\n");
164 exit(-1);
165 }
166 src_filename = line.substr(start_pos + 1, end_pos - start_pos - 1);
167 }
168
169 start_pos = line.find("-o");
170 if (start_pos != std::string::npos) {
171 size_t end_pos = line.find("\"", start_pos);
172 if (end_pos == std::string::npos) {
173 printf("Formatting error in cmdlines file, pos 1\n");
174 exit(-1);
175 }
176 // comment
177 comment = line.substr(start_pos, end_pos - start_pos);
178 remove_back_slashes(comment);
179
180 // extra_cmd_options
181 start_pos = 0;
182 extra_cmd_options = comment;
183 insert_quotes(extra_cmd_options);
184 for (int i = 0; i < 2; ++i) // skip two spaces ("-o filename ")
185 if (start_pos < extra_cmd_options.length())
186 start_pos = extra_cmd_options.find(" ", start_pos) + 1;
187 else
188 {
189 printf("Formatting error in cmdlines file, pos 2\n");
190 exit(-1);
191 }
192 if (start_pos < extra_cmd_options.length())
193 extra_cmd_options.erase(0, start_pos);
194 else
195 {
196 printf("Formatting error in cmdlines file, pos 3\n");
197 exit(-1);
198 }
199 }
200
201 start_pos = line.find(":");
202 if (start_pos != std::string::npos) {
203 if (std::isdigit(line.at(start_pos + 1)))
204 {
205 size_t end_pos = line.find("\"", start_pos);
206 yuv_specs = line.substr(start_pos, end_pos - start_pos);
207 }
208 }
209 break;
210 }
211 }
212}
213
215// Write the run_ojph_expand command line for test_executables.cpp
216void write_expand_test(std::ofstream& file,
217 const std::string& base_filename,
218 const std::string& src_ext,
219 const std::string& out_ext,
220 const std::string& ref_filename,
221 const std::string& yuv_specs,
222 std::string comment,
223 int num_components, double* mse, int* pae)
224{
225
226 std::string wavelet, cb_dims;
227 size_t start_pos = base_filename.find("_irv97_");
228 if (start_pos != std::string::npos)
229 wavelet = "irv97";
230 else
231 wavelet = "rev53";
232
233 file << "/////////////////////////////////////////////////////////"
234 << "//////////////////////" << std::endl;
235 file << "// Test ojph_expand with " << "codeblocks when the "
236 << wavelet << " wavelet is used." << std::endl;
237 if (out_ext.compare("yuv") == 0)
238 file << "// and the color components are subsampled." << std::endl;
239 file << "// Command-line options used to obtain this file is:" << std::endl;
240 while (comment.length() > 0)
241 {
242 const int len = 75;
243 size_t pos = comment.rfind(' ', len);
244 if (comment.length() > len && pos != std::string::npos) {
245 file << "// " << comment.substr(0, pos) << std::endl;
246 comment.erase(0, pos + 1);
247 }
248 else {
249 file << "// " << comment << std::endl;
250 comment.clear();
251 }
252 }
253
254 file << "TEST(TestExecutables, " << prepare_test_name(base_filename) << ") {"
255 << std::endl;
256 file << " double mse[" << num_components << "] = { ";
257 for (int i = 0; i < num_components; ++i) {
258 file << std::setprecision(6) << mse[i];
259 if (i < num_components - 1)
260 file << ", ";
261 }
262 file << "};" << std::endl;
263 file << " int pae[" << num_components << "] = { ";
264 for (int i = 0; i < num_components; ++i) {
265 file << pae[i];
266 if (i < num_components - 1)
267 file << ", ";
268 }
269 file << "};" << std::endl;
270 file << " run_ojph_expand(\"" << base_filename << "\", \""
271 << src_ext << "\", \"" << out_ext << "\");" << std::endl;
272 file << " run_mse_pae(\"" << base_filename << "\", \""
273 << out_ext << "\", \"" << ref_filename << "\"," << std::endl;
274 file << " \"" << yuv_specs << "\", "
275 << num_components << ", mse, pae);" << std::endl;
276 file << "}" << std::endl << std::endl;
277}
278
280// Write the run_ojph_expand command line for test_executables.cpp
281void write_compress_test(std::ofstream& file,
282 const std::string& src_filename,
283 const std::string& ref_filename,
284 const std::string& base_filename,
285 const std::string& out_ext,
286 const std::string& decode_ext,
287 const std::string& yuv_specs,
288 std::string comment,
289 std::string extra_cmd_options,
290 int num_components, double* mse, int* pae)
291{
292 std::string wavelet, cb_dims;
293 size_t start_pos = base_filename.find("_irv97_");
294 if (start_pos != std::string::npos)
295 wavelet = "irv97";
296 else
297 wavelet = "rev53";
298
299 // comment
300 file << "/////////////////////////////////////////////////////////"
301 << "//////////////////////" << std::endl;
302 file << "// Test ojph_compress with " << "codeblocks when the "
303 << wavelet << " wavelet is used";
304 if (out_ext.compare("yuv") == 0) {
305 file << "," << std::endl << "// and the color components are subsampled.";
306 file << std::endl;
307 }
308 else
309 file << "." << std::endl;
310 file << "// We test by comparing MSE and PAE of decoded images. ";
311 file << std::endl;
312
313 file << "// The compressed file is obtained using these command-line "
314 "options:" << std::endl;
315 while (comment.length() > 0)
316 {
317 const int len = 75;
318 size_t pos = comment.rfind(' ', len);
319 if (comment.length() > len && pos < comment.length()) {
320 file << "// " << comment.substr(0, pos) << std::endl;
321 comment.erase(0, pos + 1);
322 }
323 else {
324 file << "// " << comment << std::endl;
325 comment.clear();
326 }
327 }
328
329 // test
330 file << "TEST(TestExecutables, " << prepare_test_name(base_filename) << ") {"
331 << std::endl;
332 file << " double mse[" << num_components << "] = { ";
333 for (int i = 0; i < num_components; ++i) {
334 file << std::setprecision(6) << mse[i];
335 if (i < num_components - 1)
336 file << ", ";
337 }
338 file << "};" << std::endl;
339 file << " int pae[" << num_components << "] = { ";
340 for (int i = 0; i < num_components; ++i) {
341 file << pae[i];
342 if (i < num_components - 1)
343 file << ", ";
344 }
345 file << "};" << std::endl;
346
347 // compress
348 file << " run_ojph_compress(\"" << ref_filename << "\"," << std::endl;
349 file << " ";
350 file << " \"" << base_filename << "\", \"\", \"" << "j2c" << "\",";
351 file << std::endl;
352
353 while (extra_cmd_options.length() > 0)
354 {
355 const int len = 54;
356 size_t pos = extra_cmd_options.rfind(' ', len);
357 if (extra_cmd_options.length() > len && pos != std::string::npos) {
358 file << " \"";
359 file << extra_cmd_options.substr(0, pos) << "\"";
360 extra_cmd_options.erase(0, pos);
361 if (extra_cmd_options.empty())
362 file << ");" << std::endl;
363 else
364 file << std::endl;
365 }
366 else {
367 file << " \"";
368 file << extra_cmd_options << "\");" << std::endl;
369 extra_cmd_options.clear();
370 }
371 }
372
373 // expand
374 file << " run_ojph_compress_expand(\"" << base_filename << "\", \""
375 << "j2c" << "\", \"" << decode_ext << "\");" << std::endl;
376
377 // error
378 file << " run_mse_pae(\"" << base_filename << "\", \""
379 << decode_ext << "\"," << std::endl;
380 file << " \"" << ref_filename << "\", \"" << yuv_specs << "\", "
381 << num_components << ", mse, pae);" << std::endl;
382
383 // end function
384 file << "}" << std::endl << std::endl;
385}
386
388// Write the run_compare_files command line for test_executables.cpp
389void write_file_compare(std::ofstream& file,
390 const std::string& ref_filename,
391 const std::string& base_filename,
392 const std::string& out_ext,
393 std::string comment,
394 std::string extra_cmd_options)
395{
396 std::string wavelet, cb_dims;
397 size_t start_pos = base_filename.find("_irv97_");
398 if (start_pos != std::string::npos)
399 wavelet = "irv97";
400 else
401 wavelet = "rev53";
402
403 // comment
404 file << "/////////////////////////////////////////////////////////"
405 << "//////////////////////" << std::endl;
406 file << "// Test ojph_compress with " << "codeblocks when the "
407 << wavelet << " wavelet is used";
408 if (out_ext.compare("yuv") == 0) {
409 file << "," << std::endl << "// and the color components are subsampled.";
410 file << std::endl;
411 }
412 else
413 file << "." << std::endl;
414 file << "// We test by comparing coded images, ignoring comments. ";
415 file << std::endl;
416
417 file << "// The compressed file is obtained using these command-line "
418 "options:" << std::endl;
419
420 while (comment.length() > 0)
421 {
422 const int len = 75;
423 size_t pos = comment.rfind(' ', len);
424 if (comment.length() > len && pos < comment.length()) {
425 file << "// " << comment.substr(0, pos) << std::endl;
426 comment.erase(0, pos + 1);
427 }
428 else {
429 file << "// " << comment << std::endl;
430 comment.clear();
431 }
432 }
433
434 // test
435 file << "TEST(TestExecutables, " << prepare_test_name(base_filename);
436 file << "Compare) {" << std::endl;
437
438 // compress
439 file << " run_ojph_compress(\"" << ref_filename << "\"," << std::endl;
440 file << " ";
441 file << " \"" << base_filename << "\", \"_compare\", \"" << "j2c";
442 file << "\"," << std::endl;
443
444 while (extra_cmd_options.length() > 0)
445 {
446 const int len = 54;
447 size_t pos = extra_cmd_options.rfind(' ', len);
448 if (extra_cmd_options.length() > len && pos != std::string::npos) {
449 file << " \"";
450 file << extra_cmd_options.substr(0, pos) << "\"";
451 extra_cmd_options.erase(0, pos);
452 if (extra_cmd_options.empty())
453 file << ");" << std::endl;
454 else
455 file << std::endl;
456 }
457 else {
458 file << " \"";
459 file << extra_cmd_options << "\");" << std::endl;
460 extra_cmd_options.clear();
461 }
462 }
463
464 // call compare files
465 file << " compare_files(\"" << base_filename << "\", \"_compare\", \"";
466 file << "j2c" << "\");" << std::endl;
467
468 // end function
469 file << "}" << std::endl << std::endl;
470}
471
473// main
474int main(int argc, char *argv[])
475{
476 const char mse_pae_filename[] =
477 "../../build/tests/jp2k_test_codestreams/openjph/mse_pae.txt";
478 const char cmdlines_filename[] = "ht_cmdlines.txt";
479 const char out_filename[] = "openjph_tests.cpp";
480
481 std::ifstream mse_pae_file;
482 mse_pae_file.open(mse_pae_filename, std::ios_base::in);
483 if (mse_pae_file.fail()) {
484 std::cerr << "Failed to open " << mse_pae_filename << "." << std::endl;
485 return -1;
486 }
487
488 std::ifstream cmdlines_file;
489 cmdlines_file.open(cmdlines_filename, std::ios_base::in);
490 if (cmdlines_file.fail()) {
491 std::cerr << "Failed to open " << cmdlines_filename << "." << std::endl;
492 return -1;
493 }
494
495 std::ofstream out_file;
496 out_file.open(out_filename, std::ios_base::out);
497 if (out_file.fail()) {
498 std::cerr << "Failed to open " << out_filename << "." << std::endl;
499 return -1;
500 }
501
502 while (mse_pae_file.good())
503 {
504 // read files line and process it
505 std::string ht_filename, src_filename, ref_filename;
506 mse_pae_file >> ht_filename;
507 std::string base_filename = ht_filename.substr(0, ht_filename.rfind("."));
508 std::string src_ext = ht_filename.substr(ht_filename.rfind(".") + 1);
509 mse_pae_file >> ref_filename;
510 std::string out_ext = ref_filename.substr(ref_filename.rfind(".") + 1);
511 ref_filename = ref_filename.substr(ref_filename.rfind("/") + 1);
512
513 // Uncomment to print values
514 std::cout << "base_filename = " << base_filename << std::endl;
515 std::cout << "src_ext = " << src_ext << std::endl;
516 std::cout << "out_ext = " << out_ext << std::endl;
517 std::cout << "ref_filename = " << ref_filename << std::endl;
518
519 constexpr int max_components = 10;
520 int num_components = 0;
521 double mse[max_components];
522 int pae[max_components];
523 eat_white_spaces(mse_pae_file);
524 int c = mse_pae_file.peek(); // check if next we have a number
525 while (mse_pae_file.good() && std::isdigit(c))
526 {
527 if (num_components >= max_components)
528 std::cerr << "More than " << max_components << " were found in "
529 << mse_pae_filename << "; this is not supported." << std::endl;
530 mse_pae_file >> mse[num_components];
531 mse_pae_file >> pae[num_components];
532 std::cout << "mse = " << mse[num_components] << std::endl;
533 std::cout << "pae = " << pae[num_components] << std::endl;
534 ++num_components;
535 eat_white_spaces(mse_pae_file);
536 c = mse_pae_file.peek();
537 }
538
539 // write tests for write_expand_test
540 if (base_filename.find("_dec_") != std::string::npos)
541 {
542 std::string yuv_specs, comment, extra_cmd_options;
543 process_cmdlines(cmdlines_file, base_filename, src_filename, comment,
544 yuv_specs, extra_cmd_options);
545 write_expand_test(out_file, base_filename, src_ext, out_ext,
546 ref_filename, yuv_specs, comment, num_components, mse, pae);
547 }
548
549 // write tests for test_ojph_compress and test_compare_files
550 // to be written
551 if (base_filename.find("_enc_") != std::string::npos)
552 {
553 std::string yuv_specs, comment, extra_cmd_options;
554 process_cmdlines(cmdlines_file, base_filename, src_filename, comment,
555 yuv_specs, extra_cmd_options);
556 std::cout << "src_filename = " << src_filename << std::endl;
557 write_compress_test(out_file, src_filename, ref_filename, base_filename,
558 src_ext, out_ext, yuv_specs, comment, extra_cmd_options, num_components,
559 mse, pae);
560 // write_file_compare(out_file, ref_filename, base_filename, out_ext,
561 // comment, extra_cmd_options);
562 }
563
564 }
565
566 return 0;
567}
int main(int argc, char *argv[])
void process_cmdlines(std::ifstream &file, const std::string base_filename, std::string &src_filename, std::string &comment, std::string &yuv_specs, std::string &extra_cmd_options)
std::string prepare_test_name(std::string name)
void write_expand_test(std::ofstream &file, const std::string &base_filename, const std::string &src_ext, const std::string &out_ext, const std::string &ref_filename, const std::string &yuv_specs, std::string comment, int num_components, double *mse, int *pae)
void insert_quotes(std::string &str)
void remove_double_spaces(std::string &str)
void eat_white_spaces(std::ifstream &file)
void remove_back_slashes(std::string &str)
void write_file_compare(std::ofstream &file, const std::string &ref_filename, const std::string &base_filename, const std::string &out_ext, std::string comment, std::string extra_cmd_options)
void write_compress_test(std::ofstream &file, const std::string &src_filename, const std::string &ref_filename, const std::string &base_filename, const std::string &out_ext, const std::string &decode_ext, const std::string &yuv_specs, std::string comment, std::string extra_cmd_options, int num_components, double *mse, int *pae)