ssim.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2017 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. // distortion calculation
  11. //
  12. // Author: Skal (pascal.massimino@gmail.com)
  13. #include <assert.h>
  14. #include <stdlib.h> // for abs()
  15. #include "./dsp.h"
  16. #if !defined(WEBP_REDUCE_SIZE)
  17. //------------------------------------------------------------------------------
  18. // SSIM / PSNR
  19. // hat-shaped filter. Sum of coefficients is equal to 16.
  20. static const uint32_t kWeight[2 * VP8_SSIM_KERNEL + 1] = {
  21. 1, 2, 3, 4, 3, 2, 1
  22. };
  23. static const uint32_t kWeightSum = 16 * 16; // sum{kWeight}^2
  24. static WEBP_INLINE double SSIMCalculation(
  25. const VP8DistoStats* const stats, uint32_t N /*num samples*/) {
  26. const uint32_t w2 = N * N;
  27. const uint32_t C1 = 20 * w2;
  28. const uint32_t C2 = 60 * w2;
  29. const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
  30. const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
  31. const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
  32. if (xmxm + ymym >= C3) {
  33. const int64_t xmym = (int64_t)stats->xm * stats->ym;
  34. const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
  35. const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
  36. const uint64_t syy = (uint64_t)stats->yym * N - ymym;
  37. // we descale by 8 to prevent overflow during the fnum/fden multiply.
  38. const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
  39. const uint64_t den_S = (sxx + syy + C2) >> 8;
  40. const uint64_t fnum = (2 * xmym + C1) * num_S;
  41. const uint64_t fden = (xmxm + ymym + C1) * den_S;
  42. const double r = (double)fnum / fden;
  43. assert(r >= 0. && r <= 1.0);
  44. return r;
  45. }
  46. return 1.; // area is too dark to contribute meaningfully
  47. }
  48. double VP8SSIMFromStats(const VP8DistoStats* const stats) {
  49. return SSIMCalculation(stats, kWeightSum);
  50. }
  51. double VP8SSIMFromStatsClipped(const VP8DistoStats* const stats) {
  52. return SSIMCalculation(stats, stats->w);
  53. }
  54. static double SSIMGetClipped_C(const uint8_t* src1, int stride1,
  55. const uint8_t* src2, int stride2,
  56. int xo, int yo, int W, int H) {
  57. VP8DistoStats stats = { 0, 0, 0, 0, 0, 0 };
  58. const int ymin = (yo - VP8_SSIM_KERNEL < 0) ? 0 : yo - VP8_SSIM_KERNEL;
  59. const int ymax = (yo + VP8_SSIM_KERNEL > H - 1) ? H - 1
  60. : yo + VP8_SSIM_KERNEL;
  61. const int xmin = (xo - VP8_SSIM_KERNEL < 0) ? 0 : xo - VP8_SSIM_KERNEL;
  62. const int xmax = (xo + VP8_SSIM_KERNEL > W - 1) ? W - 1
  63. : xo + VP8_SSIM_KERNEL;
  64. int x, y;
  65. src1 += ymin * stride1;
  66. src2 += ymin * stride2;
  67. for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
  68. for (x = xmin; x <= xmax; ++x) {
  69. const uint32_t w = kWeight[VP8_SSIM_KERNEL + x - xo]
  70. * kWeight[VP8_SSIM_KERNEL + y - yo];
  71. const uint32_t s1 = src1[x];
  72. const uint32_t s2 = src2[x];
  73. stats.w += w;
  74. stats.xm += w * s1;
  75. stats.ym += w * s2;
  76. stats.xxm += w * s1 * s1;
  77. stats.xym += w * s1 * s2;
  78. stats.yym += w * s2 * s2;
  79. }
  80. }
  81. return VP8SSIMFromStatsClipped(&stats);
  82. }
  83. static double SSIMGet_C(const uint8_t* src1, int stride1,
  84. const uint8_t* src2, int stride2) {
  85. VP8DistoStats stats = { 0, 0, 0, 0, 0, 0 };
  86. int x, y;
  87. for (y = 0; y <= 2 * VP8_SSIM_KERNEL; ++y, src1 += stride1, src2 += stride2) {
  88. for (x = 0; x <= 2 * VP8_SSIM_KERNEL; ++x) {
  89. const uint32_t w = kWeight[x] * kWeight[y];
  90. const uint32_t s1 = src1[x];
  91. const uint32_t s2 = src2[x];
  92. stats.xm += w * s1;
  93. stats.ym += w * s2;
  94. stats.xxm += w * s1 * s1;
  95. stats.xym += w * s1 * s2;
  96. stats.yym += w * s2 * s2;
  97. }
  98. }
  99. return VP8SSIMFromStats(&stats);
  100. }
  101. #endif // !defined(WEBP_REDUCE_SIZE)
  102. //------------------------------------------------------------------------------
  103. #if !defined(WEBP_DISABLE_STATS)
  104. static uint32_t AccumulateSSE_C(const uint8_t* src1,
  105. const uint8_t* src2, int len) {
  106. int i;
  107. uint32_t sse2 = 0;
  108. assert(len <= 65535); // to ensure that accumulation fits within uint32_t
  109. for (i = 0; i < len; ++i) {
  110. const int32_t diff = src1[i] - src2[i];
  111. sse2 += diff * diff;
  112. }
  113. return sse2;
  114. }
  115. #endif
  116. //------------------------------------------------------------------------------
  117. #if !defined(WEBP_REDUCE_SIZE)
  118. VP8SSIMGetFunc VP8SSIMGet;
  119. VP8SSIMGetClippedFunc VP8SSIMGetClipped;
  120. #endif
  121. #if !defined(WEBP_DISABLE_STATS)
  122. VP8AccumulateSSEFunc VP8AccumulateSSE;
  123. #endif
  124. extern void VP8SSIMDspInitSSE2(void);
  125. WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
  126. #if !defined(WEBP_REDUCE_SIZE)
  127. VP8SSIMGetClipped = SSIMGetClipped_C;
  128. VP8SSIMGet = SSIMGet_C;
  129. #endif
  130. #if !defined(WEBP_DISABLE_STATS)
  131. VP8AccumulateSSE = AccumulateSSE_C;
  132. #endif
  133. if (VP8GetCPUInfo != NULL) {
  134. #if defined(WEBP_HAVE_SSE2)
  135. if (VP8GetCPUInfo(kSSE2)) {
  136. VP8SSIMDspInitSSE2();
  137. }
  138. #endif
  139. }
  140. }