OpenJPH
Open-source implementation of JPEG2000 Part-15
mse_pae.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 testing routines for the OpenJPH software
33// implementation.
34// File: mse_pae.cpp
35// Author: Aous Naman
36// Date: 18 March 2021
37//***************************************************************************/
38
39#include <cstdio>
40#include <cstdlib>
41#include <stdexcept>
42#include <cctype>
43#include "ojph_img_io.h"
44#include "ojph_mem.h"
45
46using namespace ojph;
47using namespace std;
48
49enum : ui32 {
55};
56
57struct img_info {
59 num_comps = 0;
60 width = height = 0;
61 comps[0] = comps[1] = comps[2] = 0;
63 max_val = 0;
64 }
66 for (ui32 i = 0; i < num_comps; ++i)
67 {
68 if (comps[i]) delete[] comps[i];
69 comps[i] = NULL;
70 }
71 }
72
73 void init(ui32 num_comps, size_t width, size_t height, ui32 max_val,
75 {
76 assert(num_comps <= 3 && comps[0] == NULL);
77 this->num_comps = num_comps;
78 this->width = width;
79 this->height = height;
80 this->format = format;
81 this->max_val = max_val;
82 for (ui32 i = 0; i < num_comps; ++i)
83 switch (format)
84 {
85 case FORMAT444:
86 case FORMAT400:
87 downsampling[i].x = downsampling[i].y = 1;
88 break;
89 case FORMAT422:
90 downsampling[i].x = i == 0 ? 1 : 2;
91 downsampling[i].y = 1;
92 break;
93 case FORMAT420:
94 downsampling[i].x = i == 0 ? 1 : 2;
95 downsampling[i].y = i == 0 ? 1 : 2;
96 break;
97 default:
98 assert(0);
99 };
100 for (ui32 i = 0; i < num_comps; ++i)
101 {
102 size_t w = (this->width + downsampling[i].x - 1) / downsampling[i].x;
103 size_t h = (this->height + downsampling[i].x - 1) / downsampling[i].x;
104 comps[i] = new si32[w * h];
105 }
106 }
107
108 bool exist() {
109 return comps[0] != NULL;
110 }
111
113 size_t width, height;
118};
119
120bool is_pnm(const char *filename)
121{
122 size_t len = strlen(filename);
123 if (len >= 4 && filename[len - 4] == '.' &&
124 toupper(filename[len - 3]) == 'P' &&
125 (toupper(filename[len - 2])== 'P' || toupper(filename[len - 2]) == 'G') &&
126 toupper(filename[len - 1]) == 'M')
127 return true;
128 return false;
129}
130
131void load_ppm(const char *filename, img_info& img)
132{
133 ppm_in ppm;
134 ppm.set_planar(true);
135 ppm.open(filename);
136
137 ui32 num_comps = ppm.get_num_components();
138 size_t width = ppm.get_width();
139 size_t height = ppm.get_height();
140 img.init(num_comps, width, height, ppm.get_max_val());
141
142 width = calc_aligned_size<si32, byte_alignment>(width);
143 si32 *buffer = new si32[width];
144 line_buf line;
145 line.wrap(buffer, width, 0);
146
147 for (ui32 c = 0; c < num_comps; ++c)
148 {
149 si32 *p = img.comps[c];
150 for (ui32 h = 0; h < height; ++h)
151 {
152 ui32 w = ppm.read(&line, c);
153 memcpy(p, line.i32, w * sizeof(si32));
154 p += w;
155 }
156 }
157
158 delete[] buffer;
159}
160
161bool is_yuv(const char *filename)
162{
163 const char *p = strchr(filename, ':'); // p is either NULL or pointing to ':'
164 if (p != NULL && p - filename >= 4 && p[-4] == '.' &&
165 toupper(p[-3]) == 'Y' && toupper(p[-2])== 'U' && toupper(p[-1]) == 'V')
166 return true;
167 return false;
168}
169
170void load_yuv(const char *filename, img_info& img)
171{
172 const char *p = strchr(filename, ':'); // p is either NULL or pointing to ':'
173 const char *name_end = p;
174 if (p == NULL) {
175 printf("A .yuv that does not have the expected format, which is\n");
176 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
177 printf("either 444, 422, or 420\n");
178 exit(-1);
179 }
180
181 ojph::size s;
182 s.w = (ui32)atoi(++p);
183 p = strchr(p, 'x'); // p is either NULL or pointing to ':'
184 if (p == NULL) {
185 printf("Expecting image height.\n");
186 printf("A .yuv that does not have the expected format, which is\n");
187 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
188 printf("either 444, 422, or 420\n");
189 exit(-1);
190 }
191 s.h = (ui32)atoi(++p);
192 p = strchr(p, 'x'); // p is either NULL or pointing to ':'
193 if (p == NULL) {
194 printf("Expecting image bitdepth.\n");
195 printf("A .yuv that does not have the expected format, which is\n");
196 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
197 printf("either 444, 422, or 420\n");
198 exit(-1);
199 }
200 ui32 bit_depth = (ui32)atoi(++p);
201 p = strchr(p, 'x'); // p is either NULL or pointing to ':'
202 if (p == NULL) {
203 printf("Expecting color subsampling format.\n");
204 printf("A .yuv that does not have the expected format, which is\n");
205 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
206 printf("either 444, 422, or 420\n");
207 exit(-1);
208 }
209 // p must be pointing to color subsampling format
210 ++p;
211 size_t len = strlen(p);
212 if (len != 3)
213 {
214 printf("Image color format must have 3 characters, %s was supplied.\n", p);
215 printf("A .yuv that does not have the expected format, which is\n");
216 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
217 printf("either 444, 422, or 420\n");
218 exit(-1);
219 }
220 ui32 num_comps;
221 point downsampling[3] = { point(1,1), point(1,1), point(1,1)};
222 ui32 format;
223 if (strcmp(p, "444") == 0)
224 {
225 num_comps = 3;
226 format = FORMAT444;
227 }
228 else if (strcmp(p, "422") == 0)
229 {
230 num_comps = 3;
231 format = FORMAT422;
232 downsampling[1].x = downsampling[2].x = 2;
233 }
234 else if (strcmp(p, "420") == 0)
235 {
236 num_comps = 3;
237 format = FORMAT420;
238 downsampling[1].x = downsampling[2].x = 2;
239 downsampling[1].y = downsampling[2].y = 2;
240 }
241 else if (strcmp(p, "400") == 0)
242 {
243 num_comps = 1;
244 format = FORMAT400;
245 }
246 else {
247 printf("Unknown image color format, %s.\n", p);
248 exit(-1);
249 }
250
251 char name_buf[2048];
252 ptrdiff_t cpy_len = name_end - filename > 2047 ? 2047 : name_end - filename;
253 strncpy(name_buf, filename, (size_t)cpy_len);
254 name_buf[cpy_len] = 0;
255
256 yuv_in yuv;
257 ui32 depths[3] = {bit_depth, bit_depth, bit_depth};
258 yuv.set_bit_depth(num_comps, depths);
259 yuv.set_img_props(s, num_comps, num_comps, downsampling);
260 yuv.open(name_buf);
261
262 img.init(num_comps, s.w, s.h, (1 << bit_depth) - 1, format);
263
264 size_t w = calc_aligned_size<si32, byte_alignment>(s.w);
265 si32 *buffer = new si32[w];
266 line_buf line;
267 line.wrap(buffer, w, 0);
268
269 for (ui32 c = 0; c < num_comps; ++c)
270 {
271 si32 *p = img.comps[c];
272 ui32 height = (s.h + img.downsampling[c].y - 1) / img.downsampling[c].y;
273 for (ui32 h = 0; h < height; ++h)
274 {
275 ui32 w = yuv.read(&line, c);
276 memcpy(p, line.i32, w * sizeof(si32));
277 p += w;
278 }
279 }
280
281 delete[] buffer;
282}
283
284void find_mse_pae(const img_info& img1, const img_info& img2,
285 float mse[3], ui32 pae[3])
286{
287 if (img1.num_comps != img2.num_comps || img1.format != img2.format ||
288 img1.width != img2.width || img1.height != img2.height ||
289 img1.max_val != img2.max_val)
290 {
291 printf("Error: mismatching images\n");
292 exit(-1);
293 }
294 for (ui32 c = 0; c < img1.num_comps; ++c)
295 {
296 size_t w, h;
297 w = (img1.width + img1.downsampling[c].x - 1) / img1.downsampling[c].x;
298 h = (img1.height + img1.downsampling[c].x - 1) / img1.downsampling[c].x;
299 double se = 0;
300 ui32 lpae = 0;
301 for (ui32 v = 0; v < h; ++v)
302 {
303 si32 *p0 = img1.comps[c] + w * v;
304 si32 *p1 = img2.comps[c] + w * v;
305 for (ui32 s = 0; s < w; ++s)
306 {
307 si32 err = *p0++ - *p1++;
308 ui32 ae = (ui32)(err > 0 ? err : -err);
309 lpae = ae > lpae ? ae : lpae;
310 se += (double)err * (double)err;
311 }
312 }
313 mse[c] = (float)se / (float)(w * h);
314 pae[c] = lpae;
315 }
316 // float t = 0;
317 // for (ui32 c = 0; c < img1.num_comps; ++c)
318 // t += (float)mse[c];
319 // t /= (float)num_pixels;
320 // psnr = 10.0f * log10f((float)img1.max_val * (float)img1.max_val / t);
321}
322
323int main(int argc, char *argv[])
324{
325 if (argc < 3)
326 {
327 printf("mse_pae expects two arguments <filename1, filename2>\n");
328 exit(-1);
329 }
330
331 img_info img1, img2;
332 try {
333 if (is_pnm(argv[1]))
334 load_ppm(argv[1], img1);
335 else if (is_yuv(argv[1]))
336 load_yuv(argv[1], img1);
337 else {
338 printf("mse_pae does not know file format of %s\n", argv[1]);
339 printf("or a .yuv that does not have the expected format, which is\n");
340 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
341 printf("either 444, 422, or 420\n");
342 exit(-1);
343 }
344 }
345 catch (const std::exception& e)
346 {
347 const char *p = e.what();
348 if (strncmp(p, "ojph error", 10) != 0)
349 printf("%s\n", p);
350 exit(-1);
351 }
352
353 try {
354 if (is_pnm(argv[2]))
355 load_ppm(argv[2], img2);
356 else if (is_yuv(argv[2]))
357 load_yuv(argv[2], img2);
358 else {
359 printf("mse_pae does not know file format of %s\n", argv[2]);
360 printf("or a .yuv that does not have the expected format, which is\n");
361 printf(".yuv:widthxheightxbitdepthxformat, where format is\n");
362 printf("either 444, 422, or 420\n");
363 exit(-1);
364 }
365 }
366 catch (const std::exception& e)
367 {
368 const char *p = e.what();
369 if (strncmp(p, "ojph error", 10) != 0)
370 printf("%s\n", p);
371 exit(-1);
372 }
373
374 float mse[3]; ui32 pae[3];
375 find_mse_pae(img1, img2, mse, pae);
376
377 for (ui32 c = 0; c < img1.num_comps; ++c)
378 printf("%f %d\n", mse[c], pae[c]);
379
380 return 0;
381}
382
383
ui32 get_height()
Definition: ojph_img_io.h:117
void open(const char *filename)
ui32 get_num_components()
Definition: ojph_img_io.h:119
void set_planar(bool planar)
Definition: ojph_img_io.h:113
ui32 get_width()
Definition: ojph_img_io.h:116
ui32 get_max_val()
Definition: ojph_img_io.h:118
virtual ui32 read(const line_buf *line, ui32 comp_num)
virtual ui32 read(const line_buf *line, ui32 comp_num)
void open(const char *filename)
void set_img_props(const size &s, ui32 num_components, ui32 num_downsampling, const point *downsampling)
void set_bit_depth(ui32 num_bit_depths, ui32 *bit_depth)
void find_mse_pae(const img_info &img1, const img_info &img2, float mse[3], ui32 pae[3])
Definition: mse_pae.cpp:284
int main(int argc, char *argv[])
Definition: mse_pae.cpp:323
bool is_yuv(const char *filename)
Definition: mse_pae.cpp:161
bool is_pnm(const char *filename)
Definition: mse_pae.cpp:120
void load_ppm(const char *filename, img_info &img)
Definition: mse_pae.cpp:131
void load_yuv(const char *filename, img_info &img)
Definition: mse_pae.cpp:170
@ FORMAT444
Definition: mse_pae.cpp:51
@ UNDEFINED
Definition: mse_pae.cpp:50
@ FORMAT400
Definition: mse_pae.cpp:54
@ FORMAT420
Definition: mse_pae.cpp:53
@ FORMAT422
Definition: mse_pae.cpp:52
int32_t si32
Definition: ojph_defs.h:55
uint32_t ui32
Definition: ojph_defs.h:54
void init(ui32 num_comps, size_t width, size_t height, ui32 max_val, ui32 format=FORMAT444)
Definition: mse_pae.cpp:73
ui32 max_val
Definition: mse_pae.cpp:117
size_t height
Definition: mse_pae.cpp:113
img_info()
Definition: mse_pae.cpp:58
si32 * comps[3]
Definition: mse_pae.cpp:115
~img_info()
Definition: mse_pae.cpp:65
point downsampling[3]
Definition: mse_pae.cpp:114
ui32 num_comps
Definition: mse_pae.cpp:112
bool exist()
Definition: mse_pae.cpp:108
ui32 format
Definition: mse_pae.cpp:116
size_t width
Definition: mse_pae.cpp:113
void wrap(T *buffer, size_t num_ele, ui32 pre_size)
si32 * i32
Definition: ojph_mem.h:155
ui32 w
Definition: ojph_base.h:50
ui32 h
Definition: ojph_base.h:51