OLD | NEW |
1 // Copyright 2011 Google Inc. All Rights Reserved. | 1 // Copyright 2011 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // This code is licensed under the same terms as WebM: | 3 // This code is licensed under the same terms as WebM: |
4 // Software License Agreement: http://www.webmproject.org/license/software/ | 4 // Software License Agreement: http://www.webmproject.org/license/software/ |
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ | 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ |
6 // ----------------------------------------------------------------------------- | 6 // ----------------------------------------------------------------------------- |
7 // | 7 // |
8 // WebPPicture utils: colorspace conversion, crop, ... | 8 // WebPPicture utils: colorspace conversion, crop, ... |
9 // | 9 // |
10 // Author: Skal (pascal.massimino@gmail.com) | 10 // Author: Skal (pascal.massimino@gmail.com) |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
283 | 283 |
284 if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'. | 284 if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'. |
285 WebPPictureGrabSpecs(src, dst); | 285 WebPPictureGrabSpecs(src, dst); |
286 } | 286 } |
287 dst->width = width; | 287 dst->width = width; |
288 dst->height = height; | 288 dst->height = height; |
289 if (!src->use_argb) { | 289 if (!src->use_argb) { |
290 dst->y = src->y + top * src->y_stride + left; | 290 dst->y = src->y + top * src->y_stride + left; |
291 dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); | 291 dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); |
292 dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); | 292 dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); |
| 293 dst->y_stride = src->y_stride; |
| 294 dst->uv_stride = src->uv_stride; |
293 if (src->a != NULL) { | 295 if (src->a != NULL) { |
294 dst->a = src->a + top * src->a_stride + left; | 296 dst->a = src->a + top * src->a_stride + left; |
| 297 dst->a_stride = src->a_stride; |
295 } | 298 } |
296 #ifdef WEBP_EXPERIMENTAL_FEATURES | 299 #ifdef WEBP_EXPERIMENTAL_FEATURES |
297 if (src->u0 != NULL) { | 300 if (src->u0 != NULL) { |
298 const int left_pos = | 301 const int left_pos = |
299 IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left; | 302 IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left; |
300 dst->u0 = src->u0 + top * src->uv0_stride + left_pos; | 303 dst->u0 = src->u0 + top * src->uv0_stride + left_pos; |
301 dst->v0 = src->v0 + top * src->uv0_stride + left_pos; | 304 dst->v0 = src->v0 + top * src->uv0_stride + left_pos; |
| 305 dst->uv0_stride = src->uv0_stride; |
302 } | 306 } |
303 #endif | 307 #endif |
304 } else { | 308 } else { |
305 dst->argb = src->argb + top * src->argb_stride + left; | 309 dst->argb = src->argb + top * src->argb_stride + left; |
| 310 dst->argb_stride = src->argb_stride; |
306 } | 311 } |
307 return 1; | 312 return 1; |
308 } | 313 } |
309 | 314 |
310 //------------------------------------------------------------------------------ | 315 //------------------------------------------------------------------------------ |
311 // Picture cropping | 316 // Picture cropping |
312 | 317 |
313 int WebPPictureCrop(WebPPicture* pic, | 318 int WebPPictureCrop(WebPPicture* pic, |
314 int left, int top, int width, int height) { | 319 int left, int top, int width, int height) { |
315 WebPPicture tmp; | 320 WebPPicture tmp; |
(...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
794 cur_y += 2 * picture->y_stride; | 799 cur_y += 2 * picture->y_stride; |
795 dst += 2 * argb_stride; | 800 dst += 2 * argb_stride; |
796 } | 801 } |
797 // Last row (if needed), with replicated bottom samples. | 802 // Last row (if needed), with replicated bottom samples. |
798 if (height > 1 && !(height & 1)) { | 803 if (height > 1 && !(height & 1)) { |
799 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); | 804 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); |
800 } | 805 } |
801 // Insert alpha values if needed, in replacement for the default 0xff ones. | 806 // Insert alpha values if needed, in replacement for the default 0xff ones. |
802 if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { | 807 if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { |
803 for (y = 0; y < height; ++y) { | 808 for (y = 0; y < height; ++y) { |
804 uint32_t* const dst = picture->argb + y * picture->argb_stride; | 809 uint32_t* const argb_dst = picture->argb + y * picture->argb_stride; |
805 const uint8_t* const src = picture->a + y * picture->a_stride; | 810 const uint8_t* const src = picture->a + y * picture->a_stride; |
806 int x; | 811 int x; |
807 for (x = 0; x < width; ++x) { | 812 for (x = 0; x < width; ++x) { |
808 dst[x] = (dst[x] & 0x00ffffffu) | (src[x] << 24); | 813 argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | (src[x] << 24); |
809 } | 814 } |
810 } | 815 } |
811 } | 816 } |
812 } | 817 } |
813 return 1; | 818 return 1; |
814 } | 819 } |
815 | 820 |
816 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { | 821 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { |
817 if (picture == NULL) return 0; | 822 if (picture == NULL) return 0; |
818 if (picture->argb == NULL) { | 823 if (picture->argb == NULL) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
899 need_reset = 1; | 904 need_reset = 1; |
900 } | 905 } |
901 } | 906 } |
902 // ignore the left-overs on right/bottom | 907 // ignore the left-overs on right/bottom |
903 } | 908 } |
904 } | 909 } |
905 | 910 |
906 #undef SIZE | 911 #undef SIZE |
907 #undef SIZE2 | 912 #undef SIZE2 |
908 | 913 |
| 914 //------------------------------------------------------------------------------ |
| 915 // local-min distortion |
| 916 // |
| 917 // For every pixel in the *reference* picture, we search for the local best |
| 918 // match in the compressed image. This is not a symmetrical measure. |
| 919 |
| 920 // search radius. Shouldn't be too large. |
| 921 #define RADIUS 2 |
| 922 |
| 923 static float AccumulateLSIM(const uint8_t* src, int src_stride, |
| 924 const uint8_t* ref, int ref_stride, |
| 925 int w, int h) { |
| 926 int x, y; |
| 927 double total_sse = 0.; |
| 928 for (y = 0; y < h; ++y) { |
| 929 const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS; |
| 930 const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1; |
| 931 for (x = 0; x < w; ++x) { |
| 932 const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS; |
| 933 const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1; |
| 934 double best_sse = 255. * 255.; |
| 935 const double value = (double)ref[y * ref_stride + x]; |
| 936 int i, j; |
| 937 for (j = y_0; j < y_1; ++j) { |
| 938 const uint8_t* s = src + j * src_stride; |
| 939 for (i = x_0; i < x_1; ++i) { |
| 940 const double sse = (double)(s[i] - value) * (s[i] - value); |
| 941 if (sse < best_sse) best_sse = sse; |
| 942 } |
| 943 } |
| 944 total_sse += best_sse; |
| 945 } |
| 946 } |
| 947 return (float)total_sse; |
| 948 } |
| 949 #undef RADIUS |
909 | 950 |
910 //------------------------------------------------------------------------------ | 951 //------------------------------------------------------------------------------ |
911 // Distortion | 952 // Distortion |
912 | 953 |
913 // Max value returned in case of exact similarity. | 954 // Max value returned in case of exact similarity. |
914 static const double kMinDistortion_dB = 99.; | 955 static const double kMinDistortion_dB = 99.; |
| 956 static float GetPSNR(const double v) { |
| 957 return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) |
| 958 : kMinDistortion_dB); |
| 959 } |
915 | 960 |
916 int WebPPictureDistortion(const WebPPicture* pic1, const WebPPicture* pic2, | 961 int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, |
917 int type, float result[5]) { | 962 int type, float result[5]) { |
918 int c; | |
919 DistoStats stats[5]; | 963 DistoStats stats[5]; |
920 int has_alpha; | 964 int has_alpha; |
| 965 int uv_w, uv_h; |
921 | 966 |
922 if (pic1 == NULL || pic2 == NULL || | 967 if (src == NULL || ref == NULL || |
923 pic1->width != pic2->width || pic1->height != pic2->height || | 968 src->width != ref->width || src->height != ref->height || |
924 pic1->y == NULL || pic2->y == NULL || | 969 src->y == NULL || ref->y == NULL || |
925 pic1->u == NULL || pic2->u == NULL || | 970 src->u == NULL || ref->u == NULL || |
926 pic1->v == NULL || pic2->v == NULL || | 971 src->v == NULL || ref->v == NULL || |
927 result == NULL) { | 972 result == NULL) { |
928 return 0; | 973 return 0; |
929 } | 974 } |
930 // TODO(skal): provide distortion for ARGB too. | 975 // TODO(skal): provide distortion for ARGB too. |
931 if (pic1->use_argb == 1 || pic1->use_argb != pic2->use_argb) { | 976 if (src->use_argb == 1 || src->use_argb != ref->use_argb) { |
932 return 0; | 977 return 0; |
933 } | 978 } |
934 | 979 |
935 has_alpha = !!(pic1->colorspace & WEBP_CSP_ALPHA_BIT); | 980 has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT); |
936 if (has_alpha != !!(pic2->colorspace & WEBP_CSP_ALPHA_BIT) || | 981 if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) || |
937 (has_alpha && (pic1->a == NULL || pic2->a == NULL))) { | 982 (has_alpha && (src->a == NULL || ref->a == NULL))) { |
938 return 0; | 983 return 0; |
939 } | 984 } |
940 | 985 |
941 memset(stats, 0, sizeof(stats)); | 986 memset(stats, 0, sizeof(stats)); |
942 VP8SSIMAccumulatePlane(pic1->y, pic1->y_stride, | 987 |
943 pic2->y, pic2->y_stride, | 988 uv_w = HALVE(src->width); |
944 pic1->width, pic1->height, &stats[0]); | 989 uv_h = HALVE(src->height); |
945 VP8SSIMAccumulatePlane(pic1->u, pic1->uv_stride, | 990 if (type >= 2) { |
946 pic2->u, pic2->uv_stride, | 991 float sse[4]; |
947 (pic1->width + 1) >> 1, (pic1->height + 1) >> 1, | 992 sse[0] = AccumulateLSIM(src->y, src->y_stride, |
948 &stats[1]); | 993 ref->y, ref->y_stride, src->width, src->height); |
949 VP8SSIMAccumulatePlane(pic1->v, pic1->uv_stride, | 994 sse[1] = AccumulateLSIM(src->u, src->uv_stride, |
950 pic2->v, pic2->uv_stride, | 995 ref->u, ref->uv_stride, uv_w, uv_h); |
951 (pic1->width + 1) >> 1, (pic1->height + 1) >> 1, | 996 sse[2] = AccumulateLSIM(src->v, src->uv_stride, |
952 &stats[2]); | 997 ref->v, ref->uv_stride, uv_w, uv_h); |
953 if (has_alpha) { | 998 sse[3] = has_alpha ? AccumulateLSIM(src->a, src->a_stride, |
954 VP8SSIMAccumulatePlane(pic1->a, pic1->a_stride, | 999 ref->a, ref->a_stride, |
955 pic2->a, pic2->a_stride, | 1000 src->width, src->height) |
956 pic1->width, pic1->height, &stats[3]); | 1001 : 0.f; |
957 } | 1002 result[0] = GetPSNR(sse[0] / (src->width * src->height)); |
958 for (c = 0; c <= 4; ++c) { | 1003 result[1] = GetPSNR(sse[1] / (uv_w * uv_h)); |
959 if (type == 1) { | 1004 result[2] = GetPSNR(sse[2] / (uv_w * uv_h)); |
960 const double v = VP8SSIMGet(&stats[c]); | 1005 result[3] = GetPSNR(sse[3] / (src->width * src->height)); |
961 result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) | 1006 { |
962 : kMinDistortion_dB); | 1007 double total_sse = sse[0] + sse[1] + sse[2]; |
963 } else { | 1008 int total_pixels = src->width * src->height + 2 * uv_w * uv_h; |
964 const double v = VP8SSIMGetSquaredError(&stats[c]); | 1009 if (has_alpha) { |
965 result[c] = (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) | 1010 total_pixels += src->width * src->height; |
966 : kMinDistortion_dB); | 1011 total_sse += sse[3]; |
| 1012 } |
| 1013 result[4] = GetPSNR(total_sse / total_pixels); |
967 } | 1014 } |
968 // Accumulate forward | 1015 } else { |
969 if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); | 1016 int c; |
| 1017 VP8SSIMAccumulatePlane(src->y, src->y_stride, |
| 1018 ref->y, ref->y_stride, |
| 1019 src->width, src->height, &stats[0]); |
| 1020 VP8SSIMAccumulatePlane(src->u, src->uv_stride, |
| 1021 ref->u, ref->uv_stride, |
| 1022 uv_w, uv_h, &stats[1]); |
| 1023 VP8SSIMAccumulatePlane(src->v, src->uv_stride, |
| 1024 ref->v, ref->uv_stride, |
| 1025 uv_w, uv_h, &stats[2]); |
| 1026 if (has_alpha) { |
| 1027 VP8SSIMAccumulatePlane(src->a, src->a_stride, |
| 1028 ref->a, ref->a_stride, |
| 1029 src->width, src->height, &stats[3]); |
| 1030 } |
| 1031 for (c = 0; c <= 4; ++c) { |
| 1032 if (type == 1) { |
| 1033 const double v = VP8SSIMGet(&stats[c]); |
| 1034 result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) |
| 1035 : kMinDistortion_dB); |
| 1036 } else { |
| 1037 const double v = VP8SSIMGetSquaredError(&stats[c]); |
| 1038 result[c] = GetPSNR(v); |
| 1039 } |
| 1040 // Accumulate forward |
| 1041 if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); |
| 1042 } |
970 } | 1043 } |
971 return 1; | 1044 return 1; |
972 } | 1045 } |
973 | 1046 |
974 //------------------------------------------------------------------------------ | 1047 //------------------------------------------------------------------------------ |
975 // Simplest high-level calls: | 1048 // Simplest high-level calls: |
976 | 1049 |
977 typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); | 1050 typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); |
978 | 1051 |
979 static size_t Encode(const uint8_t* rgba, int width, int height, int stride, | 1052 static size_t Encode(const uint8_t* rgba, int width, int height, int stride, |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1032 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA); | 1105 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA); |
1033 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA); | 1106 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA); |
1034 | 1107 |
1035 #undef LOSSLESS_ENCODE_FUNC | 1108 #undef LOSSLESS_ENCODE_FUNC |
1036 | 1109 |
1037 //------------------------------------------------------------------------------ | 1110 //------------------------------------------------------------------------------ |
1038 | 1111 |
1039 #if defined(__cplusplus) || defined(c_plusplus) | 1112 #if defined(__cplusplus) || defined(c_plusplus) |
1040 } // extern "C" | 1113 } // extern "C" |
1041 #endif | 1114 #endif |
OLD | NEW |