00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "contributors.h"
00018 #include "global.h"
00019 #include "img_distortion.h"
00020 #include "enc_statistics.h"
00021 #include "memalloc.h"
00022 #include "math.h"
00023
00024 #ifndef UNBIASED_VARIANCE
00025 #define UNBIASED_VARIANCE // unbiased estimation of the variance
00026 #endif
00027
00028 #define MS_SSIM_PAD 6
00029 #define MS_SSIM_PAD2 3
00030
00031 #define MS_SSIM_BETA0 0.0448
00032 #define MS_SSIM_BETA1 0.2856
00033 #define MS_SSIM_BETA2 0.3001
00034 #define MS_SSIM_BETA3 0.2363
00035 #define MS_SSIM_BETA4 0.1333
00036
00037 #define MAX_SSIM_LEVELS 5
00038
00039
00040 float compute_structural_components (ImageParameters *p_Img, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp)
00041 {
00042 static const float K2 = 0.03f;
00043 float max_pix_value_sqd;
00044 float C2;
00045 float win_pixels = (float) (win_width * win_height);
00046 #ifdef UNBIASED_VARIANCE
00047 float win_pixels_bias = win_pixels - 1;
00048 #else
00049 float win_pixels_bias = win_pixels;
00050 #endif
00051 float mb_ssim, meanOrg, meanEnc;
00052 float varOrg, varEnc, covOrgEnc;
00053 int imeanOrg, imeanEnc, ivarOrg, ivarEnc, icovOrgEnc;
00054 float cur_distortion = 0.0;
00055 int i, j, n, m, win_cnt = 0;
00056 int overlapSize = p_Inp->SSIMOverlapSize;
00057
00058 max_pix_value_sqd = (float) (p_Img->max_pel_value_comp[comp] * p_Img->max_pel_value_comp[comp]);
00059 C2 = K2 * K2 * max_pix_value_sqd;
00060
00061 for (j = 0; j <= height - win_height; j += overlapSize)
00062 {
00063 for (i = 0; i <= width - win_width; i += overlapSize)
00064 {
00065 imeanOrg = 0;
00066 imeanEnc = 0;
00067 ivarOrg = 0;
00068 ivarEnc = 0;
00069 icovOrgEnc = 0;
00070
00071 for ( n = j;n < j + win_height;n ++)
00072 {
00073 for (m = i;m < i + win_width;m ++)
00074 {
00075 imeanOrg += refImg[n][m];
00076 imeanEnc += encImg[n][m];
00077 ivarOrg += refImg[n][m] * refImg[n][m];
00078 ivarEnc += encImg[n][m] * encImg[n][m];
00079 icovOrgEnc += refImg[n][m] * encImg[n][m];
00080 }
00081 }
00082
00083 meanOrg = (float) imeanOrg / win_pixels;
00084 meanEnc = (float) imeanEnc / win_pixels;
00085
00086 varOrg = ((float) ivarOrg - ((float) imeanOrg) * meanOrg) / win_pixels_bias;
00087 varEnc = ((float) ivarEnc - ((float) imeanEnc) * meanEnc) / win_pixels_bias;
00088 covOrgEnc = ((float) icovOrgEnc - ((float) imeanOrg) * meanEnc) / win_pixels_bias;
00089
00090 mb_ssim = (float) (2.0 * covOrgEnc + C2);
00091 mb_ssim /= (float) (varOrg + varEnc + C2);
00092
00093 cur_distortion += mb_ssim;
00094 win_cnt++;
00095 }
00096 }
00097
00098 cur_distortion /= (float) win_cnt;
00099
00100 if (cur_distortion >= 1.0 && cur_distortion < 1.01)
00101 cur_distortion = 1.0;
00102
00103 return cur_distortion;
00104 }
00105
00106 float compute_luminance_component (ImageParameters *p_Img, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp)
00107 {
00108 static const float K1 = 0.01f;
00109 float max_pix_value_sqd;
00110 float C1;
00111 float win_pixels = (float) (win_width * win_height);
00112 float mb_ssim, meanOrg, meanEnc;
00113 int imeanOrg, imeanEnc;
00114 float cur_distortion = 0.0;
00115 int i, j, n, m, win_cnt = 0;
00116 int overlapSize = p_Inp->SSIMOverlapSize;
00117
00118 max_pix_value_sqd = (float) (p_Img->max_pel_value_comp[comp] * p_Img->max_pel_value_comp[comp]);
00119 C1 = K1 * K1 * max_pix_value_sqd;
00120
00121 for (j = 0; j <= height - win_height; j += overlapSize)
00122 {
00123 for (i = 0; i <= width - win_width; i += overlapSize)
00124 {
00125 imeanOrg = 0;
00126 imeanEnc = 0;
00127
00128 for ( n = j;n < j + win_height;n ++)
00129 {
00130 for (m = i;m < i + win_width;m ++)
00131 {
00132 imeanOrg += refImg[n][m];
00133 imeanEnc += encImg[n][m];
00134 }
00135 }
00136
00137 meanOrg = (float) imeanOrg / win_pixels;
00138 meanEnc = (float) imeanEnc / win_pixels;
00139
00140 mb_ssim = (float) (2.0 * meanOrg * meanEnc + C1);
00141 mb_ssim /= (float) (meanOrg * meanOrg + meanEnc * meanEnc + C1);
00142
00143 cur_distortion += mb_ssim;
00144 win_cnt++;
00145 }
00146 }
00147
00148 cur_distortion /= (float) win_cnt;
00149
00150 if (cur_distortion >= 1.0 && cur_distortion < 1.01)
00151 cur_distortion = 1.0;
00152
00153 return cur_distortion;
00154 }
00155
00156 void horizontal_symmetric_extension(int **buffer, int width, int height )
00157 {
00158 int j;
00159 int* buf;
00160
00161 int height_plus_pad2 = height + MS_SSIM_PAD2;
00162 int width_plus_pad2_minus_one = width + MS_SSIM_PAD2 - 1;
00163
00164
00165 for (j = MS_SSIM_PAD2; j < height_plus_pad2; j++ ) {
00166
00167 buf = &buffer[j][MS_SSIM_PAD2];
00168 buf[-1] = buf[1];
00169 buf[-2] = buf[2];
00170
00171
00172 buf = &buffer[j][width_plus_pad2_minus_one];
00173 buf[1] = buf[-1];
00174 buf[2] = buf[-2];
00175 buf[3] = buf[-3];
00176 }
00177 }
00178
00179 void vertical_symmetric_extension(int **buffer, int width, int height)
00180 {
00181 int i;
00182
00183 int *bufminus1 = &buffer[MS_SSIM_PAD2-1][MS_SSIM_PAD2];
00184 int *bufminus2 = &buffer[MS_SSIM_PAD2-2][MS_SSIM_PAD2];
00185 int *bufplus1 = &buffer[MS_SSIM_PAD2+1][MS_SSIM_PAD2];
00186 int *bufplus2 = &buffer[MS_SSIM_PAD2+2][MS_SSIM_PAD2];
00187
00188 int *bufhminus1 = &buffer[height + MS_SSIM_PAD2-2][MS_SSIM_PAD2];
00189 int *bufhminus2 = &buffer[height + MS_SSIM_PAD2-3][MS_SSIM_PAD2];
00190 int *bufhminus3 = &buffer[height + MS_SSIM_PAD2-4][MS_SSIM_PAD2];
00191 int *bufhplus1 = &buffer[height + MS_SSIM_PAD2][MS_SSIM_PAD2];
00192 int *bufhplus2 = &buffer[height + MS_SSIM_PAD2+1][MS_SSIM_PAD2];
00193 int *bufhplus3 = &buffer[height + MS_SSIM_PAD2+2][MS_SSIM_PAD2];
00194
00195 for (i = 0; i < width; i++)
00196 {
00197
00198 bufminus1[i] = bufplus1[i];
00199 bufminus2[i] = bufplus2[i];
00200
00201 bufhplus1[i] = bufhminus1[i];
00202 bufhplus2[i] = bufhminus2[i];
00203 bufhplus3[i] = bufhminus3[i];
00204 }
00205 }
00206
00207 static void imgpel_to_padded_int(imgpel** src, int **buffer, int width, int height)
00208 {
00209 int i, j;
00210 int* tmpDst;
00211 imgpel* tmpSrc;
00212
00213 tmpDst = buffer[MS_SSIM_PAD2];
00214 for (j = 0; j < height; j++)
00215 {
00216 tmpSrc = src[j];
00217 tmpDst = &buffer[j + MS_SSIM_PAD2][MS_SSIM_PAD2];
00218 for (i = 0; i < width; i++)
00219 {
00220 tmpDst[i] = (int)tmpSrc[i];
00221 }
00222 }
00223 }
00224
00225 void downsample(imgpel** src, imgpel** out, int height, int width)
00226 {
00227 int height2 = height >> 1;
00228 int width2 = width >> 1;
00229 int i, j;
00230 int ii, jj;
00231 int iDst;
00232 int tmp, tmp1, tmp2;
00233 int* tmpDst;
00234 int* tmpSrc;
00235
00236 int** itemp;
00237 int** dest;
00238
00239 get_mem2Dint(&itemp, height + MS_SSIM_PAD, width + MS_SSIM_PAD);
00240 get_mem2Dint(&dest, height + MS_SSIM_PAD, width2 + MS_SSIM_PAD);
00241
00242 imgpel_to_padded_int(src, itemp, width, height);
00243 horizontal_symmetric_extension(itemp, width, height);
00244
00245 for (j = 0; j < height; j++)
00246 {
00247 tmpDst = dest[j+MS_SSIM_PAD2];
00248 tmpSrc = itemp[j+MS_SSIM_PAD2];
00249 iDst = MS_SSIM_PAD2;
00250 for (i = 0; i < width2; i++, iDst++)
00251 {
00252 ii = (i << 1) + MS_SSIM_PAD2;
00253 tmp1 = tmpSrc[ii-1] + tmpSrc[ii+2];
00254 tmp2 = tmpSrc[ii] + tmpSrc[ii+1];
00255 tmpDst[iDst] = tmpSrc[ii-2] + tmpSrc[ii+3] + (tmp1 << 1) + tmp1 + (tmp2 << 5) - (tmp2 << 2);
00256 tmpDst[iDst] >>= 6;
00257 }
00258 }
00259
00260
00261 vertical_symmetric_extension(dest, width2, height);
00262
00263 for (i = 0; i < width2; i++)
00264 {
00265 ii = i + MS_SSIM_PAD2;
00266 for (j = 0; j < height2; j++)
00267 {
00268 jj = (j << 1) + MS_SSIM_PAD2;
00269 tmp1 = dest[jj-1][ii] + dest[jj+2][ii];
00270 tmp2 = dest[jj][ii] + dest[jj+1][ii];
00271 tmp = dest[jj-2][ii] + dest[jj+3][ii] + (tmp1 << 1) + tmp1 + (tmp2 << 5) - (tmp2 << 2);
00272 out[j][i] = (unsigned char) (tmp >> 6);
00273 }
00274 }
00275 free_mem2Dint(itemp);
00276 free_mem2Dint(dest);
00277 }
00278
00279 float compute_ms_ssim(ImageParameters *p_Img, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp)
00280 {
00281 float structural[MAX_SSIM_LEVELS];
00282 float cur_distortion;
00283 float luminance;
00284 imgpel** dsRef;
00285 imgpel** dsEnc;
00286 int m;
00287 static const int max_ssim_levels_minus_one = MAX_SSIM_LEVELS - 1;
00288 static const float exponent[5] = {(float)MS_SSIM_BETA0, (float)MS_SSIM_BETA1, (float)MS_SSIM_BETA2, (float)MS_SSIM_BETA3, (float)MS_SSIM_BETA4};
00289
00290 dsRef = NULL;
00291 dsEnc = NULL;
00292 get_mem2Dpel(&dsRef, height>>1, width>>1);
00293 get_mem2Dpel(&dsEnc, height>>1, width>>1);
00294
00295 structural[0] = compute_structural_components(p_Img, p_Inp, refImg, encImg, height, width, win_height, win_width, comp);
00296 cur_distortion = (float)pow(structural[0], exponent[0]);
00297
00298 downsample(refImg, dsRef, height, width);
00299 downsample(encImg, dsEnc, height, width);
00300
00301 for (m = 1; m < MAX_SSIM_LEVELS; m++)
00302 {
00303 height >>= 1;
00304 width >>= 1;
00305 structural[m] = compute_structural_components(p_Img, p_Inp, dsRef, dsEnc, height, width, imin(win_height,height), imin(win_width,width), comp);
00306 cur_distortion *= (float)pow(structural[m], exponent[m]);
00307 if (m < max_ssim_levels_minus_one)
00308 {
00309 downsample(dsRef, dsRef, height, width);
00310 downsample(dsEnc, dsEnc, height, width);
00311 }
00312 else
00313 {
00314 luminance = compute_luminance_component(p_Img, p_Inp, dsRef, dsEnc, height, width, imin(win_height,height), imin(win_width,width), comp);
00315 cur_distortion *= (float)pow(luminance, exponent[m]);
00316 }
00317 }
00318 free_mem2Dpel(dsRef);
00319 free_mem2Dpel(dsEnc);
00320 dsRef = NULL;
00321 dsEnc = NULL;
00322
00323 return cur_distortion;
00324 }
00325
00326
00327
00328
00329
00330
00331
00332 void find_ms_ssim (ImageParameters *p_Img, InputParameters *p_Inp, ImageStructure *ref, ImageStructure *src, DistMetric *metricSSIM)
00333 {
00334 DistortionParams *p_Dist = p_Img->p_Dist;
00335 FrameFormat *format = &ref->format;
00336
00337 metricSSIM->value[0] = compute_ms_ssim (p_Img, p_Inp, ref->data[0], src->data[0], format->height, format->width, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, 0);
00338
00339 if (format->yuv_format != YUV400)
00340 {
00341 metricSSIM->value[1] = compute_ms_ssim (p_Img, p_Inp, ref->data[1], src->data[1], format->height_cr, format->width_cr, p_Img->mb_cr_size_y, p_Img->mb_cr_size_x, 1);
00342 metricSSIM->value[2] = compute_ms_ssim (p_Img, p_Inp, ref->data[2], src->data[2], format->height_cr, format->width_cr, p_Img->mb_cr_size_y, p_Img->mb_cr_size_x, 2);
00343 }
00344
00345 {
00346 accumulate_average(metricSSIM, p_Dist->frame_ctr);
00347 accumulate_avslice(metricSSIM, p_Img->type, p_Img->p_Stats->frame_ctr[p_Img->type]);
00348 }
00349 }