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 
45 using namespace ojph;
46 using namespace std;
47 
48 enum : ui32 {
49  UNDEFINED = 0,
50  FORMAT444 = 1,
51  FORMAT422 = 2,
52  FORMAT420 = 3,
53  FORMAT400 = 4,
54 };
55 
56 struct img_info {
57  img_info() {
58  num_comps = 0;
59  width = height = 0;
60  comps[0] = comps[1] = comps[2] = 0;
61  format = UNDEFINED;
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,
73  ui32 format=FORMAT444)
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;
113  point downsampling[3];
114  si32 *comps[3];
117 };
118 
119 bool 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 
130 void 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 
160 bool 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 
169 void 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 
283 void 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 
322 int 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)
Definition: ojph_img_io.cpp:90
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
@ 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 load_yuv(const char *filename, img_info &img)
Definition: psnr_pae.cpp:169
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