picture_psnr_enc.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style license
  4. // that can be found in the COPYING file in the root of the source
  5. // tree. An additional intellectual property rights grant can be found
  6. // in the file PATENTS. All contributing project authors may
  7. // be found in the AUTHORS file in the root of the source tree.
  8. // -----------------------------------------------------------------------------
  9. //
  10. // WebPPicture tools for measuring distortion
  11. //
  12. // Author: Skal (pascal.massimino@gmail.com)
  13. #include "../webp/encode.h"
  14. #if !(defined(WEBP_DISABLE_STATS) || defined(WEBP_REDUCE_SIZE))
  15. #include <math.h>
  16. #include <stdlib.h>
  17. #include "../dsp/dsp.h"
  18. #include "./vp8i_enc.h"
  19. #include "../utils/utils.h"
  20. typedef double (*AccumulateFunc)(const uint8_t* src, int src_stride,
  21. const uint8_t* ref, int ref_stride,
  22. int w, int h);
  23. //------------------------------------------------------------------------------
  24. // local-min distortion
  25. //
  26. // For every pixel in the *reference* picture, we search for the local best
  27. // match in the compressed image. This is not a symmetrical measure.
  28. #define RADIUS 2 // search radius. Shouldn't be too large.
  29. static double AccumulateLSIM(const uint8_t* src, int src_stride,
  30. const uint8_t* ref, int ref_stride,
  31. int w, int h) {
  32. int x, y;
  33. double total_sse = 0.;
  34. for (y = 0; y < h; ++y) {
  35. const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;
  36. const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;
  37. for (x = 0; x < w; ++x) {
  38. const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;
  39. const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;
  40. double best_sse = 255. * 255.;
  41. const double value = (double)ref[y * ref_stride + x];
  42. int i, j;
  43. for (j = y_0; j < y_1; ++j) {
  44. const uint8_t* const s = src + j * src_stride;
  45. for (i = x_0; i < x_1; ++i) {
  46. const double diff = s[i] - value;
  47. const double sse = diff * diff;
  48. if (sse < best_sse) best_sse = sse;
  49. }
  50. }
  51. total_sse += best_sse;
  52. }
  53. }
  54. return total_sse;
  55. }
  56. #undef RADIUS
  57. static double AccumulateSSE(const uint8_t* src, int src_stride,
  58. const uint8_t* ref, int ref_stride,
  59. int w, int h) {
  60. int y;
  61. double total_sse = 0.;
  62. for (y = 0; y < h; ++y) {
  63. total_sse += VP8AccumulateSSE(src, ref, w);
  64. src += src_stride;
  65. ref += ref_stride;
  66. }
  67. return total_sse;
  68. }
  69. //------------------------------------------------------------------------------
  70. static double AccumulateSSIM(const uint8_t* src, int src_stride,
  71. const uint8_t* ref, int ref_stride,
  72. int w, int h) {
  73. const int w0 = (w < VP8_SSIM_KERNEL) ? w : VP8_SSIM_KERNEL;
  74. const int w1 = w - VP8_SSIM_KERNEL - 1;
  75. const int h0 = (h < VP8_SSIM_KERNEL) ? h : VP8_SSIM_KERNEL;
  76. const int h1 = h - VP8_SSIM_KERNEL - 1;
  77. int x, y;
  78. double sum = 0.;
  79. for (y = 0; y < h0; ++y) {
  80. for (x = 0; x < w; ++x) {
  81. sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
  82. }
  83. }
  84. for (; y < h1; ++y) {
  85. for (x = 0; x < w0; ++x) {
  86. sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
  87. }
  88. for (; x < w1; ++x) {
  89. const int off1 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * src_stride;
  90. const int off2 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * ref_stride;
  91. sum += VP8SSIMGet(src + off1, src_stride, ref + off2, ref_stride);
  92. }
  93. for (; x < w; ++x) {
  94. sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
  95. }
  96. }
  97. for (; y < h; ++y) {
  98. for (x = 0; x < w; ++x) {
  99. sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
  100. }
  101. }
  102. return sum;
  103. }
  104. //------------------------------------------------------------------------------
  105. // Distortion
  106. // Max value returned in case of exact similarity.
  107. static const double kMinDistortion_dB = 99.;
  108. static double GetPSNR(double v, double size) {
  109. return (v > 0. && size > 0.) ? -4.3429448 * log(v / (size * 255 * 255.))
  110. : kMinDistortion_dB;
  111. }
  112. static double GetLogSSIM(double v, double size) {
  113. v = (size > 0.) ? v / size : 1.;
  114. return (v < 1.) ? -10.0 * log10(1. - v) : kMinDistortion_dB;
  115. }
  116. int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
  117. const uint8_t* ref, size_t ref_stride,
  118. int width, int height, size_t x_step,
  119. int type, float* distortion, float* result) {
  120. uint8_t* allocated = NULL;
  121. const AccumulateFunc metric = (type == 0) ? AccumulateSSE :
  122. (type == 1) ? AccumulateSSIM :
  123. AccumulateLSIM;
  124. if (src == NULL || ref == NULL ||
  125. src_stride < x_step * width || ref_stride < x_step * width ||
  126. result == NULL || distortion == NULL) {
  127. return 0;
  128. }
  129. VP8SSIMDspInit();
  130. if (x_step != 1) { // extract a packed plane if needed
  131. int x, y;
  132. uint8_t* tmp1;
  133. uint8_t* tmp2;
  134. allocated =
  135. (uint8_t*)WebPSafeMalloc(2ULL * width * height, sizeof(*allocated));
  136. if (allocated == NULL) return 0;
  137. tmp1 = allocated;
  138. tmp2 = tmp1 + (size_t)width * height;
  139. for (y = 0; y < height; ++y) {
  140. for (x = 0; x < width; ++x) {
  141. tmp1[x + y * width] = src[x * x_step + y * src_stride];
  142. tmp2[x + y * width] = ref[x * x_step + y * ref_stride];
  143. }
  144. }
  145. src = tmp1;
  146. ref = tmp2;
  147. }
  148. *distortion = (float)metric(src, width, ref, width, width, height);
  149. WebPSafeFree(allocated);
  150. *result = (type == 1) ? (float)GetLogSSIM(*distortion, (double)width * height)
  151. : (float)GetPSNR(*distortion, (double)width * height);
  152. return 1;
  153. }
  154. #ifdef WORDS_BIGENDIAN
  155. #define BLUE_OFFSET 3 // uint32_t 0x000000ff is 0x00,00,00,ff in memory
  156. #else
  157. #define BLUE_OFFSET 0 // uint32_t 0x000000ff is 0xff,00,00,00 in memory
  158. #endif
  159. int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
  160. int type, float results[5]) {
  161. int w, h, c;
  162. int ok = 0;
  163. WebPPicture p0, p1;
  164. double total_size = 0., total_distortion = 0.;
  165. if (src == NULL || ref == NULL ||
  166. src->width != ref->width || src->height != ref->height ||
  167. results == NULL) {
  168. return 0;
  169. }
  170. VP8SSIMDspInit();
  171. if (!WebPPictureInit(&p0) || !WebPPictureInit(&p1)) return 0;
  172. w = src->width;
  173. h = src->height;
  174. if (!WebPPictureView(src, 0, 0, w, h, &p0)) goto Error;
  175. if (!WebPPictureView(ref, 0, 0, w, h, &p1)) goto Error;
  176. // We always measure distortion in ARGB space.
  177. if (p0.use_argb == 0 && !WebPPictureYUVAToARGB(&p0)) goto Error;
  178. if (p1.use_argb == 0 && !WebPPictureYUVAToARGB(&p1)) goto Error;
  179. for (c = 0; c < 4; ++c) {
  180. float distortion;
  181. const size_t stride0 = 4 * (size_t)p0.argb_stride;
  182. const size_t stride1 = 4 * (size_t)p1.argb_stride;
  183. // results are reported as BGRA
  184. const int offset = c ^ BLUE_OFFSET;
  185. if (!WebPPlaneDistortion((const uint8_t*)p0.argb + offset, stride0,
  186. (const uint8_t*)p1.argb + offset, stride1,
  187. w, h, 4, type, &distortion, results + c)) {
  188. goto Error;
  189. }
  190. total_distortion += distortion;
  191. total_size += w * h;
  192. }
  193. results[4] = (type == 1) ? (float)GetLogSSIM(total_distortion, total_size)
  194. : (float)GetPSNR(total_distortion, total_size);
  195. ok = 1;
  196. Error:
  197. WebPPictureFree(&p0);
  198. WebPPictureFree(&p1);
  199. return ok;
  200. }
  201. #undef BLUE_OFFSET
  202. #else // defined(WEBP_DISABLE_STATS)
  203. int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
  204. const uint8_t* ref, size_t ref_stride,
  205. int width, int height, size_t x_step,
  206. int type, float* distortion, float* result) {
  207. (void)src;
  208. (void)src_stride;
  209. (void)ref;
  210. (void)ref_stride;
  211. (void)width;
  212. (void)height;
  213. (void)x_step;
  214. (void)type;
  215. if (distortion == NULL || result == NULL) return 0;
  216. *distortion = 0.f;
  217. *result = 0.f;
  218. return 1;
  219. }
  220. int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
  221. int type, float results[5]) {
  222. int i;
  223. (void)src;
  224. (void)ref;
  225. (void)type;
  226. if (results == NULL) return 0;
  227. for (i = 0; i < 5; ++i) results[i] = 0.f;
  228. return 1;
  229. }
  230. #endif // !defined(WEBP_DISABLE_STATS)