42 #define DISPLAY_SCALING 255
47 #define MSSIM_C1 (MSSIM_K1*MSSIM_K1)
48 #define MSSIM_C2 (MSSIM_K2*MSSIM_K2)
78 static int ParseParams(
programparams *Param,
int argc,
char *argv[]);
80 int Width,
int Height,
int NumChannels,
float D);
81 void BasicMetrics(
float *Max,
float *Mse,
const float *A,
const float *B,
82 int Width,
int Height,
int NumChannels,
int Pad);
84 int Width,
int Height,
int NumChannels,
int Pad);
90 puts(
"Image difference calculator, P. Getreuer 2010-2011\n");
91 puts(
"Usage: imdiff [options] <exact file> <distorted file>\n"
94 puts(
" -m <metric> Metric to use for comparison, choices are");
95 puts(
" max Maximum absolute difference, max_n |A_n - B_n|");
96 puts(
" mse Mean squared error, 1/N sum |A_n - B_n|^2");
97 puts(
" rmse Root mean squared error, (MSE)^1/2");
98 puts(
" psnr Peak signal-to-noise ratio, -10 log10(MSE/255^2)");
99 puts(
" mssim Mean structural similarity index\n");
100 puts(
" -s Compute metric separately for each channel");
101 puts(
" -p <pad> Remove a margin of <pad> pixels before comparison");
102 puts(
" -D <number> D parameter for difference image\n");
104 puts(
" -q <number> Quality for saving JPEG images (0 to 100)\n");
106 puts(
"Alternatively, a difference image is generated by the syntax\n"
107 " imdiff [-D <number>] <exact file> <distorted file> <output file>\n");
108 puts(
"The difference image is computed as\n"
109 " D_n = 255/D (A_n - B_n) + 255/2.\n"
110 "Values outside of the range [0,255] are saturated.\n");
113 " imdiff -mpsnr frog-exact.png frog-4x.png");
115 " imdiff -mpsnr frog-exact.bmp frog-4x.bmp");
120 int main(
int argc,
char *argv[])
127 } A = {NULL, 0, 0}, B = {NULL, 0, 0};
130 float Max, MaxC[3], Mse, MseC[3], Mssim;
131 int Channel, Status = 1;
134 if(!ParseParams(&Param, argc, argv))
138 if(!(A.Data = (
float *)
ReadImage(&A.Width, &A.Height, Param.
FileA,
143 if(!(B.Data = (
float *)
ReadImage(&B.Width, &B.Height, Param.
FileB,
147 if(A.Width != B.Width || A.Height != B.Height)
149 ErrorMessage(
"Image sizes don't match, %dx%d vs. %dx%d.\n",
150 A.Width, A.Height, B.Width, B.Height);
153 else if(A.Width <= 2*Param.
Pad || A.Height <= 2*Param.
Pad)
156 "Removal of %d-pixel padding removes entire %dx%d image.\n",
157 Param.
Pad, A.Width, A.Height);
174 for(Channel = 0; Channel < 3; Channel++)
177 A.Data + Channel*A.Width*A.Height,
178 B.Data + Channel*B.Width*B.Height,
179 A.Width, A.Height, 1, Param.
Pad);
181 if(MaxC[Channel] > Max)
184 Mse += MseC[Channel];
195 printf(
"Peak signal-to-noise ratio: %.4f\n", -10*log10(Mse));
199 printf(
"Maximum absolute difference: %g %g %g\n",
202 printf(
"Peak signal-to-noise ratio: %.4f %.4f %.4f\n",
203 -10*log10(MseC[0]), -10*log10(MseC[1]), -10*log10(MseC[2]));
206 if(A.Width <= 2*(5 + Param.
Pad)
207 || A.Height <= 2*(5 + Param.
Pad))
208 printf(
"Image size is too small to compute MSSIM.\n");
213 A.Width, A.Height, 3, Param.
Pad);
216 printf(
"Mean structural similarity: %.4f\n", Mssim);
230 printf(
"%.4f %.4f %.4f\n",
239 printf(
"%.4f %.4f %.4f\n",
246 printf(
"%.4f\n", -10*log10(Mse));
248 printf(
"%.4f %.4f %.4f\n",
249 -10*log10(MseC[0]), -10*log10(MseC[1]), -10*log10(MseC[2]));
252 if(A.Width <= 2*(5 + Param.
Pad)
253 || A.Height <= 2*(5 + Param.
Pad))
254 printf(
"Image size is too small to compute MSSIM.\n");
258 A.Width, A.Height, 3, Param.
Pad);
263 printf(
"%.4f\n", Mssim);
279 int Width,
int Height,
int NumChannels,
float D)
281 const int NumEl = NumChannels*Width*Height;
286 for(n = 0; n < NumEl; n++)
287 A[n] = (A[n] - B[n])/D + (float)0.5;
292 void BasicMetrics(
float *Max,
float *Mse,
const float *A,
const float *B,
293 int Width,
int Height,
int NumChannels,
int Pad)
295 float Diff, CurMax = 0;
297 int x, y, Channel, n;
300 for(Channel = 0; Channel < NumChannels; Channel++)
301 for(y = Pad; y < Height - Pad; y++)
302 for(x = Pad; x < Width - Pad; x++)
304 n = x + Width*(y + Height*Channel);
305 Diff = (float)fabs(A[n] - B[n]);
310 AccumMse += Diff*Diff;
314 *Mse = (float)(AccumMse / (NumChannels*(Width - 2*Pad)*(Height - 2*Pad)));
320 int Width,
int Height,
int NumChannels,
int Pad)
328 const int NumPixels = Width*Height;
329 const int NumEl = NumChannels*NumPixels;
330 float *Buffer = NULL, *MuA = NULL, *MuB = NULL,
331 *MuAA = NULL, *MuBB = NULL, *MuAB = NULL;
332 double MuASqr, MuBSqr, MuAMuB,
333 SigmaASqr, SigmaBSqr, SigmaAB, Mssim = -1;
338 || !(Buffer = (
float *)
Malloc(
sizeof(
float)*NumPixels))
339 || !(MuA = (
float *)
Malloc(
sizeof(
float)*NumEl))
340 || !(MuB = (
float *)
Malloc(
sizeof(
float)*NumEl))
341 || !(MuAA = (
float *)
Malloc(
sizeof(
float)*NumEl))
342 || !(MuBB = (
float *)
Malloc(
sizeof(
float)*NumEl))
343 || !(MuAB = (
float *)
Malloc(
sizeof(
float)*NumEl)))
347 Boundary, Width, Height, NumChannels);
349 Boundary, Width, Height, NumChannels);
351 for(n = 0; n < NumEl; n++)
359 Boundary, Width, Height, NumChannels);
361 Boundary, Width, Height, NumChannels);
363 Boundary, Width, Height, NumChannels);
368 for(c = 0; c < NumChannels; c++)
369 for(y = Pad; y < Height - Pad; y++)
370 for(x = Pad; x < Width - Pad; x++)
372 n = x + Width*(y + Height*c);
373 MuASqr = MuA[n]*MuA[n];
374 MuBSqr = MuB[n]*MuB[n];
375 MuAMuB = MuA[n]*MuB[n];
376 SigmaASqr = MuAA[n] - MuASqr;
377 SigmaBSqr = MuBB[n] - MuBSqr;
378 SigmaAB = MuAB[n] - MuAMuB;
382 *(SigmaASqr + SigmaBSqr +
MSSIM_C2));
385 Mssim /= NumChannels*(Width - 2*Pad)*(Height - 2*Pad);
400 static int ParseParams(
programparams *Param,
int argc,
char *argv[])
424 for(i = 1; i < argc;)
426 if(argv[i] && argv[i][0] ==
'-')
428 if((OptionChar = argv[i][1]) == 0)
435 OptionString = &argv[i][2];
437 OptionString = argv[i];
447 Param->
Pad = atoi(OptionString);
460 Param->
D = (float)atof(OptionString);
469 if(!strcmp(OptionString,
"max"))
471 else if(!strcmp(OptionString,
"mse"))
473 else if(!strcmp(OptionString,
"rmse"))
475 else if(!strcmp(OptionString,
"psnr"))
477 else if(!strcmp(OptionString,
"mssim"))
489 ErrorMessage(
"JPEG quality must be between 0 and 100.\n");
498 if(isprint(OptionChar))
511 Param->
FileA = argv[i];
512 else if(!Param->
FileB)
513 Param->
FileB = argv[i];