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