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