ms_ssim.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Copyright (c) 2011, Tom Distler (http://tdistler.com)
  3. * All rights reserved.
  4. *
  5. * The BSD License
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. *
  10. * - Redistributions of source code must retain the above copyright notice,
  11. * this list of conditions and the following disclaimer.
  12. *
  13. * - Redistributions in binary form must reproduce the above copyright notice,
  14. * this list of conditions and the following disclaimer in the documentation
  15. * and/or other materials provided with the distribution.
  16. *
  17. * - Neither the name of the tdistler.com nor the names of its contributors may
  18. * be used to endorse or promote products derived from this software without
  19. * specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  25. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  28. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  29. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  30. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. * POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. #include "iqa.h"
  34. #include "ssim.h"
  35. #include "decimate.h"
  36. #include <math.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. /* Default number of scales */
  40. #define SCALES 5
  41. /* Low-pass filter for down-sampling (9/7 biorthogonal wavelet filter) */
  42. #define LPF_LEN 9
  43. static const float g_lpf[LPF_LEN][LPF_LEN] = {
  44. { 0.000714f,-0.000450f,-0.002090f, 0.007132f, 0.016114f, 0.007132f,-0.002090f,-0.000450f, 0.000714f},
  45. {-0.000450f, 0.000283f, 0.001316f,-0.004490f,-0.010146f,-0.004490f, 0.001316f, 0.000283f,-0.000450f},
  46. {-0.002090f, 0.001316f, 0.006115f,-0.020867f,-0.047149f,-0.020867f, 0.006115f, 0.001316f,-0.002090f},
  47. { 0.007132f,-0.004490f,-0.020867f, 0.071207f, 0.160885f, 0.071207f,-0.020867f,-0.004490f, 0.007132f},
  48. { 0.016114f,-0.010146f,-0.047149f, 0.160885f, 0.363505f, 0.160885f,-0.047149f,-0.010146f, 0.016114f},
  49. { 0.007132f,-0.004490f,-0.020867f, 0.071207f, 0.160885f, 0.071207f,-0.020867f,-0.004490f, 0.007132f},
  50. {-0.002090f, 0.001316f, 0.006115f,-0.020867f,-0.047149f,-0.020867f, 0.006115f, 0.001316f,-0.002090f},
  51. {-0.000450f, 0.000283f, 0.001316f,-0.004490f,-0.010146f,-0.004490f, 0.001316f, 0.000283f,-0.000450f},
  52. { 0.000714f,-0.000450f,-0.002090f, 0.007132f, 0.016114f, 0.007132f,-0.002090f,-0.000450f, 0.000714f},
  53. };
  54. /* Alpha, beta, and gamma values for each scale */
  55. static float g_alphas[] = { 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.1333f };
  56. static float g_betas[] = { 0.0448f, 0.2856f, 0.3001f, 0.2363f, 0.1333f };
  57. static float g_gammas[] = { 0.0448f, 0.2856f, 0.3001f, 0.2363f, 0.1333f };
  58. struct _context {
  59. double l; /* Luminance */
  60. double c; /* Contrast */
  61. double s; /* Structure */
  62. float alpha;
  63. float beta;
  64. float gamma;
  65. };
  66. /* Called for each pixel */
  67. int _ms_ssim_map(const struct _ssim_int *si, void *ctx)
  68. {
  69. struct _context *ms_ctx = (struct _context*)ctx;
  70. ms_ctx->l += si->l;
  71. ms_ctx->c += si->c;
  72. ms_ctx->s += si->s;
  73. return 0;
  74. }
  75. /* Called to calculate the final result */
  76. float _ms_ssim_reduce(int w, int h, void *ctx)
  77. {
  78. double size = (double)(w*h);
  79. struct _context *ms_ctx = (struct _context*)ctx;
  80. ms_ctx->l = pow(ms_ctx->l / size, (double)ms_ctx->alpha);
  81. ms_ctx->c = pow(ms_ctx->c / size, (double)ms_ctx->beta);
  82. ms_ctx->s = pow(fabs(ms_ctx->s / size), (double)ms_ctx->gamma);
  83. return (float)(ms_ctx->l * ms_ctx->c * ms_ctx->s);
  84. }
  85. /* Releases the scaled buffers */
  86. void _free_buffers(float **buf, int scales)
  87. {
  88. int idx;
  89. for (idx=0; idx<scales; ++idx)
  90. free(buf[idx]);
  91. }
  92. /* Allocates the scaled buffers. If error, all buffers are free'd */
  93. int _alloc_buffers(float **buf, int w, int h, int scales)
  94. {
  95. int idx;
  96. int cur_w = w;
  97. int cur_h = h;
  98. for (idx=0; idx<scales; ++idx) {
  99. buf[idx] = (float*)malloc(cur_w*cur_h*sizeof(float));
  100. if (!buf[idx]) {
  101. _free_buffers(buf, idx);
  102. return 1;
  103. }
  104. cur_w = cur_w/2 + (cur_w&1);
  105. cur_h = cur_h/2 + (cur_h&1);
  106. }
  107. return 0;
  108. }
  109. /*
  110. * MS_SSIM(X,Y) = Lm(x,y)^aM * MULT[j=1->M]( Cj(x,y)^bj * Sj(x,y)^gj )
  111. * where,
  112. * L = mean
  113. * C = variance
  114. * S = cross-correlation
  115. *
  116. * b1=g1=0.0448, b2=g2=0.2856, b3=g3=0.3001, b4=g4=0.2363, a5=b5=g5=0.1333
  117. */
  118. float iqa_ms_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h,
  119. int stride, const struct iqa_ms_ssim_args *args)
  120. {
  121. int wang=0;
  122. int scales=SCALES;
  123. int gauss=1;
  124. const float *alphas=g_alphas, *betas=g_betas, *gammas=g_gammas;
  125. int idx,x,y,cur_w,cur_h;
  126. int offset,src_offset;
  127. float **ref_imgs, **cmp_imgs; /* Array of pointers to scaled images */
  128. float msssim;
  129. struct _kernel lpf, window;
  130. struct iqa_ssim_args s_args;
  131. struct _map_reduce mr;
  132. struct _context ms_ctx;
  133. if (args) {
  134. wang = args->wang;
  135. gauss = args->gaussian;
  136. scales = args->scales;
  137. if (args->alphas)
  138. alphas = args->alphas;
  139. if (args->betas)
  140. betas = args->betas;
  141. if (args->gammas)
  142. gammas = args->gammas;
  143. }
  144. /* Make sure we won't scale below 1x1 */
  145. cur_w = w;
  146. cur_h = h;
  147. for (idx=0; idx<scales; ++idx) {
  148. if ( gauss ? cur_w<GAUSSIAN_LEN || cur_h<GAUSSIAN_LEN : cur_w<LPF_LEN || cur_h<LPF_LEN )
  149. return INFINITY;
  150. cur_w /= 2;
  151. cur_h /= 2;
  152. }
  153. window.kernel = (float*)g_square_window;
  154. window.w = window.h = SQUARE_LEN;
  155. window.normalized = 1;
  156. window.bnd_opt = KBND_SYMMETRIC;
  157. if (gauss) {
  158. window.kernel = (float*)g_gaussian_window;
  159. window.w = window.h = GAUSSIAN_LEN;
  160. }
  161. mr.map = _ms_ssim_map;
  162. mr.reduce = _ms_ssim_reduce;
  163. /* Allocate the scaled image buffers */
  164. ref_imgs = (float**)malloc(scales*sizeof(float*));
  165. cmp_imgs = (float**)malloc(scales*sizeof(float*));
  166. if (!ref_imgs || !cmp_imgs) {
  167. if (ref_imgs) free(ref_imgs);
  168. if (cmp_imgs) free(cmp_imgs);
  169. return INFINITY;
  170. }
  171. if (_alloc_buffers(ref_imgs, w, h, scales)) {
  172. free(ref_imgs);
  173. free(cmp_imgs);
  174. return INFINITY;
  175. }
  176. if (_alloc_buffers(cmp_imgs, w, h, scales)) {
  177. _free_buffers(ref_imgs, scales);
  178. free(ref_imgs);
  179. free(cmp_imgs);
  180. return INFINITY;
  181. }
  182. /* Copy original images into first scale buffer, forcing stride = width. */
  183. for (y=0; y<h; ++y) {
  184. src_offset = y*stride;
  185. offset = y*w;
  186. for (x=0; x<w; ++x, ++offset, ++src_offset) {
  187. ref_imgs[0][offset] = (float)ref[src_offset];
  188. cmp_imgs[0][offset] = (float)cmp[src_offset];
  189. }
  190. }
  191. /* Create scaled versions of the images */
  192. cur_w=w;
  193. cur_h=h;
  194. lpf.kernel = (float*)g_lpf;
  195. lpf.w = lpf.h = LPF_LEN;
  196. lpf.normalized = 1;
  197. lpf.bnd_opt = KBND_SYMMETRIC;
  198. for (idx=1; idx<scales; ++idx) {
  199. if (_iqa_decimate(ref_imgs[idx-1], cur_w, cur_h, 2, &lpf, ref_imgs[idx], 0, 0) ||
  200. _iqa_decimate(cmp_imgs[idx-1], cur_w, cur_h, 2, &lpf, cmp_imgs[idx], &cur_w, &cur_h))
  201. {
  202. _free_buffers(ref_imgs, scales);
  203. _free_buffers(cmp_imgs, scales);
  204. free(ref_imgs);
  205. free(cmp_imgs);
  206. return INFINITY;
  207. }
  208. }
  209. cur_w=w;
  210. cur_h=h;
  211. msssim = 1.0;
  212. for (idx=0; idx<scales; ++idx) {
  213. ms_ctx.l = 0;
  214. ms_ctx.c = 0;
  215. ms_ctx.s = 0;
  216. ms_ctx.alpha = alphas[idx];
  217. ms_ctx.beta = betas[idx];
  218. ms_ctx.gamma = gammas[idx];
  219. if (!wang) {
  220. /* MS-SSIM* (Rouse/Hemami) */
  221. s_args.alpha = 1.0f;
  222. s_args.beta = 1.0f;
  223. s_args.gamma = 1.0f;
  224. s_args.K1 = 0.0f; /* Force stabilization constants to 0 */
  225. s_args.K2 = 0.0f;
  226. s_args.L = 255;
  227. s_args.f = 1; /* Don't resize */
  228. mr.context = &ms_ctx;
  229. msssim *= _iqa_ssim(ref_imgs[idx], cmp_imgs[idx], cur_w, cur_h, &window, &mr, &s_args);
  230. }
  231. else {
  232. /* MS-SSIM (Wang) */
  233. s_args.alpha = 1.0f;
  234. s_args.beta = 1.0f;
  235. s_args.gamma = 1.0f;
  236. s_args.K1 = 0.01f;
  237. s_args.K2 = 0.03f;
  238. s_args.L = 255;
  239. s_args.f = 1; /* Don't resize */
  240. mr.context = &ms_ctx;
  241. msssim *= _iqa_ssim(ref_imgs[idx], cmp_imgs[idx], cur_w, cur_h, &window, &mr, &s_args);
  242. }
  243. if (msssim == INFINITY)
  244. break;
  245. cur_w = cur_w/2 + (cur_w&1);
  246. cur_h = cur_h/2 + (cur_h&1);
  247. }
  248. _free_buffers(ref_imgs, scales);
  249. _free_buffers(cmp_imgs, scales);
  250. free(ref_imgs);
  251. free(cmp_imgs);
  252. return msssim;
  253. }