| OLD | NEW |
| (Empty) |
| 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 <math.h> | |
| 15 #include <stdlib.h> | |
| 16 | |
| 17 #include "./vp8enci.h" | |
| 18 #include "../utils/utils.h" | |
| 19 | |
| 20 //------------------------------------------------------------------------------ | |
| 21 // local-min distortion | |
| 22 // | |
| 23 // For every pixel in the *reference* picture, we search for the local best | |
| 24 // match in the compressed image. This is not a symmetrical measure. | |
| 25 | |
| 26 #define RADIUS 2 // search radius. Shouldn't be too large. | |
| 27 | |
| 28 static void AccumulateLSIM(const uint8_t* src, int src_stride, | |
| 29 const uint8_t* ref, int ref_stride, | |
| 30 int w, int h, VP8DistoStats* stats) { | |
| 31 int x, y; | |
| 32 double total_sse = 0.; | |
| 33 for (y = 0; y < h; ++y) { | |
| 34 const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS; | |
| 35 const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1; | |
| 36 for (x = 0; x < w; ++x) { | |
| 37 const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS; | |
| 38 const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1; | |
| 39 double best_sse = 255. * 255.; | |
| 40 const double value = (double)ref[y * ref_stride + x]; | |
| 41 int i, j; | |
| 42 for (j = y_0; j < y_1; ++j) { | |
| 43 const uint8_t* const s = src + j * src_stride; | |
| 44 for (i = x_0; i < x_1; ++i) { | |
| 45 const double diff = s[i] - value; | |
| 46 const double sse = diff * diff; | |
| 47 if (sse < best_sse) best_sse = sse; | |
| 48 } | |
| 49 } | |
| 50 total_sse += best_sse; | |
| 51 } | |
| 52 } | |
| 53 stats->w = w * h; | |
| 54 stats->xm = 0; | |
| 55 stats->ym = 0; | |
| 56 stats->xxm = total_sse; | |
| 57 stats->yym = 0; | |
| 58 stats->xxm = 0; | |
| 59 } | |
| 60 #undef RADIUS | |
| 61 | |
| 62 //------------------------------------------------------------------------------ | |
| 63 // Distortion | |
| 64 | |
| 65 // Max value returned in case of exact similarity. | |
| 66 static const double kMinDistortion_dB = 99.; | |
| 67 static float GetPSNR(const double v) { | |
| 68 return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) | |
| 69 : kMinDistortion_dB); | |
| 70 } | |
| 71 | |
| 72 int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, | |
| 73 int type, float result[5]) { | |
| 74 VP8DistoStats stats[5]; | |
| 75 int w, h; | |
| 76 | |
| 77 memset(stats, 0, sizeof(stats)); | |
| 78 | |
| 79 VP8SSIMDspInit(); | |
| 80 | |
| 81 if (src == NULL || ref == NULL || | |
| 82 src->width != ref->width || src->height != ref->height || | |
| 83 src->use_argb != ref->use_argb || result == NULL) { | |
| 84 return 0; | |
| 85 } | |
| 86 w = src->width; | |
| 87 h = src->height; | |
| 88 | |
| 89 if (src->use_argb == 1) { | |
| 90 if (src->argb == NULL || ref->argb == NULL) { | |
| 91 return 0; | |
| 92 } else { | |
| 93 int i, j, c; | |
| 94 uint8_t* tmp1, *tmp2; | |
| 95 uint8_t* const tmp_plane = | |
| 96 (uint8_t*)WebPSafeMalloc(2ULL * w * h, sizeof(*tmp_plane)); | |
| 97 if (tmp_plane == NULL) return 0; | |
| 98 tmp1 = tmp_plane; | |
| 99 tmp2 = tmp_plane + w * h; | |
| 100 for (c = 0; c < 4; ++c) { | |
| 101 for (j = 0; j < h; ++j) { | |
| 102 for (i = 0; i < w; ++i) { | |
| 103 tmp1[j * w + i] = src->argb[i + j * src->argb_stride] >> (c * 8); | |
| 104 tmp2[j * w + i] = ref->argb[i + j * ref->argb_stride] >> (c * 8); | |
| 105 } | |
| 106 } | |
| 107 if (type >= 2) { | |
| 108 AccumulateLSIM(tmp1, w, tmp2, w, w, h, &stats[c]); | |
| 109 } else { | |
| 110 VP8SSIMAccumulatePlane(tmp1, w, tmp2, w, w, h, &stats[c]); | |
| 111 } | |
| 112 } | |
| 113 WebPSafeFree(tmp_plane); | |
| 114 } | |
| 115 } else { | |
| 116 int has_alpha, uv_w, uv_h; | |
| 117 if (src->y == NULL || ref->y == NULL || | |
| 118 src->u == NULL || ref->u == NULL || | |
| 119 src->v == NULL || ref->v == NULL) { | |
| 120 return 0; | |
| 121 } | |
| 122 has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT); | |
| 123 if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) || | |
| 124 (has_alpha && (src->a == NULL || ref->a == NULL))) { | |
| 125 return 0; | |
| 126 } | |
| 127 | |
| 128 uv_w = (src->width + 1) >> 1; | |
| 129 uv_h = (src->height + 1) >> 1; | |
| 130 if (type >= 2) { | |
| 131 AccumulateLSIM(src->y, src->y_stride, ref->y, ref->y_stride, | |
| 132 w, h, &stats[0]); | |
| 133 AccumulateLSIM(src->u, src->uv_stride, ref->u, ref->uv_stride, | |
| 134 uv_w, uv_h, &stats[1]); | |
| 135 AccumulateLSIM(src->v, src->uv_stride, ref->v, ref->uv_stride, | |
| 136 uv_w, uv_h, &stats[2]); | |
| 137 if (has_alpha) { | |
| 138 AccumulateLSIM(src->a, src->a_stride, ref->a, ref->a_stride, | |
| 139 w, h, &stats[3]); | |
| 140 } | |
| 141 } else { | |
| 142 VP8SSIMAccumulatePlane(src->y, src->y_stride, | |
| 143 ref->y, ref->y_stride, | |
| 144 w, h, &stats[0]); | |
| 145 VP8SSIMAccumulatePlane(src->u, src->uv_stride, | |
| 146 ref->u, ref->uv_stride, | |
| 147 uv_w, uv_h, &stats[1]); | |
| 148 VP8SSIMAccumulatePlane(src->v, src->uv_stride, | |
| 149 ref->v, ref->uv_stride, | |
| 150 uv_w, uv_h, &stats[2]); | |
| 151 if (has_alpha) { | |
| 152 VP8SSIMAccumulatePlane(src->a, src->a_stride, | |
| 153 ref->a, ref->a_stride, | |
| 154 w, h, &stats[3]); | |
| 155 } | |
| 156 } | |
| 157 } | |
| 158 // Final stat calculations. | |
| 159 { | |
| 160 int c; | |
| 161 for (c = 0; c <= 4; ++c) { | |
| 162 if (type == 1) { | |
| 163 const double v = VP8SSIMGet(&stats[c]); | |
| 164 result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) | |
| 165 : kMinDistortion_dB); | |
| 166 } else { | |
| 167 const double v = VP8SSIMGetSquaredError(&stats[c]); | |
| 168 result[c] = GetPSNR(v); | |
| 169 } | |
| 170 // Accumulate forward | |
| 171 if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); | |
| 172 } | |
| 173 } | |
| 174 return 1; | |
| 175 } | |
| 176 | |
| 177 //------------------------------------------------------------------------------ | |
| OLD | NEW |