Path: blob/master/thirdparty/libwebp/src/enc/picture_psnr_enc.c
9913 views
// Copyright 2014 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// WebPPicture tools for measuring distortion10//11// Author: Skal ([email protected])1213#include "src/webp/encode.h"1415#if !(defined(WEBP_DISABLE_STATS) || defined(WEBP_REDUCE_SIZE))1617#include <math.h>18#include <stdlib.h>1920#include "src/dsp/dsp.h"21#include "src/enc/vp8i_enc.h"22#include "src/utils/utils.h"2324typedef double (*AccumulateFunc)(const uint8_t* src, int src_stride,25const uint8_t* ref, int ref_stride,26int w, int h);2728//------------------------------------------------------------------------------29// local-min distortion30//31// For every pixel in the *reference* picture, we search for the local best32// match in the compressed image. This is not a symmetrical measure.3334#define RADIUS 2 // search radius. Shouldn't be too large.3536static double AccumulateLSIM(const uint8_t* src, int src_stride,37const uint8_t* ref, int ref_stride,38int w, int h) {39int x, y;40double total_sse = 0.;41for (y = 0; y < h; ++y) {42const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;43const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;44for (x = 0; x < w; ++x) {45const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;46const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;47double best_sse = 255. * 255.;48const double value = (double)ref[y * ref_stride + x];49int i, j;50for (j = y_0; j < y_1; ++j) {51const uint8_t* const s = src + j * src_stride;52for (i = x_0; i < x_1; ++i) {53const double diff = s[i] - value;54const double sse = diff * diff;55if (sse < best_sse) best_sse = sse;56}57}58total_sse += best_sse;59}60}61return total_sse;62}63#undef RADIUS6465static double AccumulateSSE(const uint8_t* src, int src_stride,66const uint8_t* ref, int ref_stride,67int w, int h) {68int y;69double total_sse = 0.;70for (y = 0; y < h; ++y) {71total_sse += VP8AccumulateSSE(src, ref, w);72src += src_stride;73ref += ref_stride;74}75return total_sse;76}7778//------------------------------------------------------------------------------7980static double AccumulateSSIM(const uint8_t* src, int src_stride,81const uint8_t* ref, int ref_stride,82int w, int h) {83const int w0 = (w < VP8_SSIM_KERNEL) ? w : VP8_SSIM_KERNEL;84const int w1 = w - VP8_SSIM_KERNEL - 1;85const int h0 = (h < VP8_SSIM_KERNEL) ? h : VP8_SSIM_KERNEL;86const int h1 = h - VP8_SSIM_KERNEL - 1;87int x, y;88double sum = 0.;89for (y = 0; y < h0; ++y) {90for (x = 0; x < w; ++x) {91sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);92}93}94for (; y < h1; ++y) {95for (x = 0; x < w0; ++x) {96sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);97}98for (; x < w1; ++x) {99const int off1 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * src_stride;100const int off2 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * ref_stride;101sum += VP8SSIMGet(src + off1, src_stride, ref + off2, ref_stride);102}103for (; x < w; ++x) {104sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);105}106}107for (; y < h; ++y) {108for (x = 0; x < w; ++x) {109sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);110}111}112return sum;113}114115//------------------------------------------------------------------------------116// Distortion117118// Max value returned in case of exact similarity.119static const double kMinDistortion_dB = 99.;120121static double GetPSNR(double v, double size) {122return (v > 0. && size > 0.) ? -4.3429448 * log(v / (size * 255 * 255.))123: kMinDistortion_dB;124}125126static double GetLogSSIM(double v, double size) {127v = (size > 0.) ? v / size : 1.;128return (v < 1.) ? -10.0 * log10(1. - v) : kMinDistortion_dB;129}130131int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,132const uint8_t* ref, size_t ref_stride,133int width, int height, size_t x_step,134int type, float* distortion, float* result) {135uint8_t* allocated = NULL;136const AccumulateFunc metric = (type == 0) ? AccumulateSSE :137(type == 1) ? AccumulateSSIM :138AccumulateLSIM;139if (src == NULL || ref == NULL ||140src_stride < x_step * width || ref_stride < x_step * width ||141result == NULL || distortion == NULL) {142return 0;143}144145VP8SSIMDspInit();146if (x_step != 1) { // extract a packed plane if needed147int x, y;148uint8_t* tmp1;149uint8_t* tmp2;150allocated =151(uint8_t*)WebPSafeMalloc(2ULL * width * height, sizeof(*allocated));152if (allocated == NULL) return 0;153tmp1 = allocated;154tmp2 = tmp1 + (size_t)width * height;155for (y = 0; y < height; ++y) {156for (x = 0; x < width; ++x) {157tmp1[x + y * width] = src[x * x_step + y * src_stride];158tmp2[x + y * width] = ref[x * x_step + y * ref_stride];159}160}161src = tmp1;162ref = tmp2;163}164*distortion = (float)metric(src, width, ref, width, width, height);165WebPSafeFree(allocated);166167*result = (type == 1) ? (float)GetLogSSIM(*distortion, (double)width * height)168: (float)GetPSNR(*distortion, (double)width * height);169return 1;170}171172#ifdef WORDS_BIGENDIAN173#define BLUE_OFFSET 3 // uint32_t 0x000000ff is 0x00,00,00,ff in memory174#else175#define BLUE_OFFSET 0 // uint32_t 0x000000ff is 0xff,00,00,00 in memory176#endif177178int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,179int type, float results[5]) {180int w, h, c;181int ok = 0;182WebPPicture p0, p1;183double total_size = 0., total_distortion = 0.;184if (src == NULL || ref == NULL ||185src->width != ref->width || src->height != ref->height ||186results == NULL) {187return 0;188}189190VP8SSIMDspInit();191if (!WebPPictureInit(&p0) || !WebPPictureInit(&p1)) return 0;192w = src->width;193h = src->height;194if (!WebPPictureView(src, 0, 0, w, h, &p0)) goto Error;195if (!WebPPictureView(ref, 0, 0, w, h, &p1)) goto Error;196197// We always measure distortion in ARGB space.198if (p0.use_argb == 0 && !WebPPictureYUVAToARGB(&p0)) goto Error;199if (p1.use_argb == 0 && !WebPPictureYUVAToARGB(&p1)) goto Error;200for (c = 0; c < 4; ++c) {201float distortion;202const size_t stride0 = 4 * (size_t)p0.argb_stride;203const size_t stride1 = 4 * (size_t)p1.argb_stride;204// results are reported as BGRA205const int offset = c ^ BLUE_OFFSET;206if (!WebPPlaneDistortion((const uint8_t*)p0.argb + offset, stride0,207(const uint8_t*)p1.argb + offset, stride1,208w, h, 4, type, &distortion, results + c)) {209goto Error;210}211total_distortion += distortion;212total_size += w * h;213}214215results[4] = (type == 1) ? (float)GetLogSSIM(total_distortion, total_size)216: (float)GetPSNR(total_distortion, total_size);217ok = 1;218219Error:220WebPPictureFree(&p0);221WebPPictureFree(&p1);222return ok;223}224225#undef BLUE_OFFSET226227#else // defined(WEBP_DISABLE_STATS)228int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,229const uint8_t* ref, size_t ref_stride,230int width, int height, size_t x_step,231int type, float* distortion, float* result) {232(void)src;233(void)src_stride;234(void)ref;235(void)ref_stride;236(void)width;237(void)height;238(void)x_step;239(void)type;240if (distortion == NULL || result == NULL) return 0;241*distortion = 0.f;242*result = 0.f;243return 1;244}245246int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,247int type, float results[5]) {248int i;249(void)src;250(void)ref;251(void)type;252if (results == NULL) return 0;253for (i = 0; i < 5; ++i) results[i] = 0.f;254return 1;255}256257#endif // !defined(WEBP_DISABLE_STATS)258259260