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 size_t end_pos = line.find("\"", start_pos);
204 yuv_specs = line.substr(start_pos, end_pos - start_pos);
205 }
206 break;
207 }
208 }
209}
210
212// Write the run_ojph_expand command line for test_executables.cpp
213void write_expand_test(std::ofstream& file,
214 const std::string& base_filename,
215 const std::string& src_ext,
216 const std::string& out_ext,
217 const std::string& ref_filename,
218 const std::string& yuv_specs,
219 std::string comment,
220 int num_components, double* mse, int* pae)
221{
222
223 std::string wavelet, cb_dims;
224 size_t start_pos = base_filename.find("_irv97_");
225 if (start_pos != std::string::npos)
226 wavelet = "irv97";
227 else
228 wavelet = "rev53";
229
230 file << "/////////////////////////////////////////////////////////"
231 << "//////////////////////" << std::endl;
232 file << "// Test ojph_expand with " << "codeblocks when the "
233 << wavelet << " wavelet is used." << std::endl;
234 if (out_ext.compare("yuv") == 0)
235 file << "// and the color components are subsampled." << std::endl;
236 file << "// Command-line options used to obtain this file is:" << std::endl;
237 while (comment.length() > 0)
238 {
239 const int len = 75;
240 size_t pos = comment.rfind(' ', len);
241 if (comment.length() > len && pos != std::string::npos) {
242 file << "// " << comment.substr(0, pos) << std::endl;
243 comment.erase(0, pos + 1);
244 }
245 else {
246 file << "// " << comment << std::endl;
247 comment.clear();
248 }
249 }
250
251 file << "TEST(TestExecutables, " << prepare_test_name(base_filename) << ") {"
252 << std::endl;
253 file << " double mse[" << num_components << "] = { ";
254 for (int i = 0; i < num_components; ++i) {
255 file << std::setprecision(6) << mse[i];
256 if (i < num_components - 1)
257 file << ", ";
258 }
259 file << "};" << std::endl;
260 file << " int pae[" << num_components << "] = { ";
261 for (int i = 0; i < num_components; ++i) {
262 file << pae[i];
263 if (i < num_components - 1)
264 file << ", ";
265 }
266 file << "};" << std::endl;
267 file << " run_ojph_expand(\"" << base_filename << "\", \""
268 << src_ext << "\", \"" << out_ext << "\");" << std::endl;
269 file << " run_mse_pae(\"" << base_filename << "\", \""
270 << out_ext << "\", \"" << ref_filename << "\"," << std::endl;
271 file << " \"" << yuv_specs << "\", "
272 << num_components << ", mse, pae);" << std::endl;
273 file << "}" << std::endl << std::endl;
274}
275
277// Write the run_ojph_expand command line for test_executables.cpp
278void write_compress_test(std::ofstream& file,
279 const std::string& src_filename,
280 const std::string& ref_filename,
281 const std::string& base_filename,
282 const std::string& out_ext,
283 const std::string& decode_ext,
284 const std::string& yuv_specs,
285 std::string comment,
286 std::string extra_cmd_options,
287 int num_components, double* mse, int* pae)
288{
289 std::string wavelet, cb_dims;
290 size_t start_pos = base_filename.find("_irv97_");
291 if (start_pos != std::string::npos)
292 wavelet = "irv97";
293 else
294 wavelet = "rev53";
295
296 // comment
297 file << "/////////////////////////////////////////////////////////"
298 << "//////////////////////" << std::endl;
299 file << "// Test ojph_compress with " << "codeblocks when the "
300 << wavelet << " wavelet is used";
301 if (out_ext.compare("yuv") == 0) {
302 file << "," << std::endl << "// and the color components are subsampled.";
303 file << std::endl;
304 }
305 else
306 file << "." << std::endl;
307 file << "// We test by comparing MSE and PAE of decoded images. ";
308 file << std::endl;
309
310 file << "// The compressed file is obtained using these command-line "
311 "options:" << std::endl;
312 while (comment.length() > 0)
313 {
314 const int len = 75;
315 size_t pos = comment.rfind(' ', len);
316 if (comment.length() > len && pos < comment.length()) {
317 file << "// " << comment.substr(0, pos) << std::endl;
318 comment.erase(0, pos + 1);
319 }
320 else {
321 file << "// " << comment << std::endl;
322 comment.clear();
323 }
324 }
325
326 // test
327 file << "TEST(TestExecutables, " << prepare_test_name(base_filename) << ") {"
328 << std::endl;
329 file << " double mse[" << num_components << "] = { ";
330 for (int i = 0; i < num_components; ++i) {
331 file << std::setprecision(6) << mse[i];
332 if (i < num_components - 1)
333 file << ", ";
334 }
335 file << "};" << std::endl;
336 file << " int pae[" << num_components << "] = { ";
337 for (int i = 0; i < num_components; ++i) {
338 file << pae[i];
339 if (i < num_components - 1)
340 file << ", ";
341 }
342 file << "};" << std::endl;
343
344 // compress
345 file << " run_ojph_compress(\"" << ref_filename << "\"," << std::endl;
346 file << " ";
347 file << " \"" << base_filename << "\", \"\", \"" << "j2c" << "\",";
348 file << std::endl;
349
350 while (extra_cmd_options.length() > 0)
351 {
352 const int len = 54;
353 size_t pos = extra_cmd_options.rfind(' ', len);
354 if (extra_cmd_options.length() > len && pos != std::string::npos) {
355 file << " \"";
356 file << extra_cmd_options.substr(0, pos) << "\"";
357 extra_cmd_options.erase(0, pos);
358 if (extra_cmd_options.empty())
359 file << ");" << std::endl;
360 else
361 file << std::endl;
362 }
363 else {
364 file << " \"";
365 file << extra_cmd_options << "\");" << std::endl;
366 extra_cmd_options.clear();
367 }
368 }
369
370 // expand
371 file << " run_ojph_compress_expand(\"" << base_filename << "\", \""
372 << "j2c" << "\", \"" << decode_ext << "\");" << std::endl;
373
374 // error
375 file << " run_mse_pae(\"" << base_filename << "\", \""
376 << decode_ext << "\"," << std::endl;
377 file << " \"" << ref_filename << "\", \"" << yuv_specs << "\", "
378 << num_components << ", mse, pae);" << std::endl;
379
380 // end function
381 file << "}" << std::endl << std::endl;
382}
383
385// Write the run_compare_files command line for test_executables.cpp
386void write_file_compare(std::ofstream& file,
387 const std::string& ref_filename,
388 const std::string& base_filename,
389 const std::string& out_ext,
390 std::string comment,
391 std::string extra_cmd_options)
392{
393 std::string wavelet, cb_dims;
394 size_t start_pos = base_filename.find("_irv97_");
395 if (start_pos != std::string::npos)
396 wavelet = "irv97";
397 else
398 wavelet = "rev53";
399
400 // comment
401 file << "/////////////////////////////////////////////////////////"
402 << "//////////////////////" << std::endl;
403 file << "// Test ojph_compress with " << "codeblocks when the "
404 << wavelet << " wavelet is used";
405 if (out_ext.compare("yuv") == 0) {
406 file << "," << std::endl << "// and the color components are subsampled.";
407 file << std::endl;
408 }
409 else
410 file << "." << std::endl;
411 file << "// We test by comparing coded images, ignoring comments. ";
412 file << std::endl;
413
414 file << "// The compressed file is obtained using these command-line "
415 "options:" << std::endl;
416
417 while (comment.length() > 0)
418 {
419 const int len = 75;
420 size_t pos = comment.rfind(' ', len);
421 if (comment.length() > len && pos < comment.length()) {
422 file << "// " << comment.substr(0, pos) << std::endl;
423 comment.erase(0, pos + 1);
424 }
425 else {
426 file << "// " << comment << std::endl;
427 comment.clear();
428 }
429 }
430
431 // test
432 file << "TEST(TestExecutables, " << prepare_test_name(base_filename);
433 file << "Compare) {" << std::endl;
434
435 // compress
436 file << " run_ojph_compress(\"" << ref_filename << "\"," << std::endl;
437 file << " ";
438 file << " \"" << base_filename << "\", \"_compare\", \"" << "j2c";
439 file << "\"," << std::endl;
440
441 while (extra_cmd_options.length() > 0)
442 {
443 const int len = 54;
444 size_t pos = extra_cmd_options.rfind(' ', len);
445 if (extra_cmd_options.length() > len && pos != std::string::npos) {
446 file << " \"";
447 file << extra_cmd_options.substr(0, pos) << "\"";
448 extra_cmd_options.erase(0, pos);
449 if (extra_cmd_options.empty())
450 file << ");" << std::endl;
451 else
452 file << std::endl;
453 }
454 else {
455 file << " \"";
456 file << extra_cmd_options << "\");" << std::endl;
457 extra_cmd_options.clear();
458 }
459 }
460
461 // call compare files
462 file << " compare_files(\"" << base_filename << "\", \"_compare\", \"";
463 file << "j2c" << "\");" << std::endl;
464
465 // end function
466 file << "}" << std::endl << std::endl;
467}
468
470// main
471int main(int argc, char *argv[])
472{
473 const char mse_pae_filename[] =
474 "../../build/tests/jp2k_test_codestreams/openjph/mse_pae.txt";
475 const char cmdlines_filename[] = "ht_cmdlines.txt";
476 const char out_filename[] = "openjph_tests.cpp";
477
478 std::ifstream mse_pae_file;
479 mse_pae_file.open(mse_pae_filename, std::ios_base::in);
480 if (mse_pae_file.fail()) {
481 std::cerr << "Failed to open " << mse_pae_filename << "." << std::endl;
482 return -1;
483 }
484
485 std::ifstream cmdlines_file;
486 cmdlines_file.open(cmdlines_filename, std::ios_base::in);
487 if (cmdlines_file.fail()) {
488 std::cerr << "Failed to open " << cmdlines_filename << "." << std::endl;
489 return -1;
490 }
491
492 std::ofstream out_file;
493 out_file.open(out_filename, std::ios_base::out);
494 if (out_file.fail()) {
495 std::cerr << "Failed to open " << out_filename << "." << std::endl;
496 return -1;
497 }
498
499 while (mse_pae_file.good())
500 {
501 // read files line and process it
502 std::string ht_filename, src_filename, ref_filename;
503 mse_pae_file >> ht_filename;
504 std::string base_filename = ht_filename.substr(0, ht_filename.rfind("."));
505 std::string src_ext = ht_filename.substr(ht_filename.rfind(".") + 1);
506 mse_pae_file >> ref_filename;
507 std::string out_ext = ref_filename.substr(ref_filename.rfind(".") + 1);
508 ref_filename = ref_filename.substr(ref_filename.rfind("/") + 1);
509
510 // Uncomment to print values
511 std::cout << "base_filename = " << base_filename << std::endl;
512 std::cout << "src_ext = " << src_ext << std::endl;
513 std::cout << "out_ext = " << out_ext << std::endl;
514 std::cout << "ref_filename = " << ref_filename << std::endl;
515
516 constexpr int max_components = 10;
517 int num_components = 0;
518 double mse[max_components];
519 int pae[max_components];
520 eat_white_spaces(mse_pae_file);
521 int c = mse_pae_file.peek(); // check if next we have a number
522 while (mse_pae_file.good() && std::isdigit(c))
523 {
524 if (num_components >= max_components)
525 std::cerr << "More than " << max_components << " were found in "
526 << mse_pae_filename << "; this is not supported." << std::endl;
527 mse_pae_file >> mse[num_components];
528 mse_pae_file >> pae[num_components];
529 std::cout << "mse = " << mse[num_components] << std::endl;
530 std::cout << "pae = " << pae[num_components] << std::endl;
531 ++num_components;
532 eat_white_spaces(mse_pae_file);
533 c = mse_pae_file.peek();
534 }
535
536 // write tests for write_expand_test
537 if (base_filename.find("_dec_") != std::string::npos)
538 {
539 std::string yuv_specs, comment, extra_cmd_options;
540 process_cmdlines(cmdlines_file, base_filename, src_filename, comment,
541 yuv_specs, extra_cmd_options);
542 write_expand_test(out_file, base_filename, src_ext, out_ext,
543 ref_filename, yuv_specs, comment, num_components, mse, pae);
544 }
545
546 // write tests for test_ojph_compress and test_compare_files
547 // to be written
548 if (base_filename.find("_enc_") != std::string::npos)
549 {
550 std::string yuv_specs, comment, extra_cmd_options;
551 process_cmdlines(cmdlines_file, base_filename, src_filename, comment,
552 yuv_specs, extra_cmd_options);
553 std::cout << "src_filename = " << src_filename << std::endl;
554 write_compress_test(out_file, src_filename, ref_filename, base_filename,
555 src_ext, out_ext, yuv_specs, comment, extra_cmd_options, num_components,
556 mse, pae);
557 // write_file_compare(out_file, ref_filename, base_filename, out_ext,
558 // comment, extra_cmd_options);
559 }
560
561 }
562
563 return 0;
564}
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)