Use optimized SSIM from libvpx.
Review URL: http://webrtc-codereview.appspot.com/117005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@407 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
bca7fa09af
commit
f53055d60e
@ -358,7 +358,9 @@ NormalTest::Print()
|
||||
double avgEncTime = _totalEncodeTime / _frameCnt;
|
||||
double avgDecTime = _totalDecodeTime / _frameCnt;
|
||||
double psnr;
|
||||
double ssim;
|
||||
PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr);
|
||||
SSIMfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &ssim);
|
||||
printf("Actual bitrate: %f kbps\n", actualBitRate);
|
||||
printf("Target bitrate: %f kbps\n", _bitRate);
|
||||
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
|
||||
@ -368,6 +370,8 @@ NormalTest::Print()
|
||||
( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
|
||||
printf("PSNR: %f \n", psnr);
|
||||
( _log) << "PSNR: " << psnr << std::endl;
|
||||
printf("SSIM: %f \n", ssim);
|
||||
( _log) << "SSIM: " << ssim << std::endl;
|
||||
(_log) << std::endl;
|
||||
|
||||
printf("\nVCM Normal Test: \n\n%i tests completed\n", vcmMacrosTests);
|
||||
|
@ -440,6 +440,100 @@ PSNRfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ssim_parms_8x8_c
|
||||
(
|
||||
unsigned char *s,
|
||||
int sp,
|
||||
unsigned char *r,
|
||||
int rp,
|
||||
unsigned long *sum_s,
|
||||
unsigned long *sum_r,
|
||||
unsigned long *sum_sq_s,
|
||||
unsigned long *sum_sq_r,
|
||||
unsigned long *sum_sxr
|
||||
)
|
||||
{
|
||||
int i,j;
|
||||
for(i=0;i<8;i++,s+=sp,r+=rp)
|
||||
{
|
||||
for(j=0;j<8;j++)
|
||||
{
|
||||
*sum_s += s[j];
|
||||
*sum_r += r[j];
|
||||
*sum_sq_s += s[j] * s[j];
|
||||
*sum_sq_r += r[j] * r[j];
|
||||
*sum_sxr += s[j] * r[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
similarity
|
||||
(
|
||||
unsigned long sum_s,
|
||||
unsigned long sum_r,
|
||||
unsigned long sum_sq_s,
|
||||
unsigned long sum_sq_r,
|
||||
unsigned long sum_sxr,
|
||||
int count
|
||||
)
|
||||
{
|
||||
int64_t ssim_n, ssim_d;
|
||||
int64_t c1, c2;
|
||||
const int64_t cc1 = 26634; // (64^2*(.01*255)^2
|
||||
const int64_t cc2 = 239708; // (64^2*(.03*255)^2
|
||||
|
||||
//scale the constants by number of pixels
|
||||
c1 = (cc1*count*count)>>12;
|
||||
c2 = (cc2*count*count)>>12;
|
||||
|
||||
ssim_n = (2*sum_s*sum_r+ c1)*((int64_t) 2*count*sum_sxr-
|
||||
(int64_t) 2*sum_s*sum_r+c2);
|
||||
|
||||
ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)*
|
||||
((int64_t)count*sum_sq_s-(int64_t)sum_s*sum_s +
|
||||
(int64_t)count*sum_sq_r-(int64_t) sum_r*sum_r +c2) ;
|
||||
|
||||
return ssim_n * 1.0 / ssim_d;
|
||||
}
|
||||
|
||||
static double
|
||||
ssim_8x8(unsigned char *s,int sp, unsigned char *r,int rp)
|
||||
{
|
||||
unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
|
||||
ssim_parms_8x8_c(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
|
||||
return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64);
|
||||
}
|
||||
|
||||
double
|
||||
SSIM_frame
|
||||
(
|
||||
unsigned char *img1,
|
||||
unsigned char *img2,
|
||||
int stride_img1,
|
||||
int stride_img2,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
WebRtc_Word32 i,j;
|
||||
WebRtc_UWord32 samples = 0;
|
||||
double ssim_total = 0;
|
||||
|
||||
// sample point start with each 4x4 location
|
||||
for(i=0; i < height-8; i+=4, img1 += stride_img1*4, img2 += stride_img2*4)
|
||||
{
|
||||
for(j=0; j < width-8; j+=4 )
|
||||
{
|
||||
double v = ssim_8x8(img1+j, stride_img1, img2+j, stride_img2);
|
||||
ssim_total += v;
|
||||
samples++;
|
||||
}
|
||||
}
|
||||
ssim_total /= samples;
|
||||
return ssim_total;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *SSIMptr)
|
||||
{
|
||||
@ -459,281 +553,23 @@ SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName,
|
||||
|
||||
int frames = 0;
|
||||
|
||||
int frameBytes = 3*width*height/2; // bytes in one frame I420
|
||||
unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420
|
||||
unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420
|
||||
const int frameBytes = 3*width*height/2; // bytes in one frame I420
|
||||
unsigned char *ref = new unsigned char[frameBytes];
|
||||
unsigned char *test = new unsigned char[frameBytes];
|
||||
|
||||
int refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
||||
int testBytes = (int) fread(test, 1, frameBytes, testFp);
|
||||
|
||||
float *righMostColumnAvgTest = new float[width];
|
||||
float *righMostColumnAvgRef = new float[width];
|
||||
float *righMostColumnContrastTest = new float[width];
|
||||
float *righMostColumnContrastRef = new float[width];
|
||||
float *righMostColumnCrossCorr = new float[width];
|
||||
|
||||
float term1,term2,term3,term4,term5;
|
||||
|
||||
//
|
||||
// SSIM: variable definition, window function, initialization
|
||||
int window = 10;
|
||||
//
|
||||
int flag_window = 0; //0 and 1 for uniform window filter, 2 for gaussian window
|
||||
//
|
||||
float variance_window = 2.0; //variance for window function
|
||||
float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1)
|
||||
//statistics per column of window (#columns = window+1), 0 element for avg over all columns
|
||||
float avgTest[12];
|
||||
float avgRef[12];
|
||||
float contrastTest[12];
|
||||
float contrastRef[12];
|
||||
float crossCorr[12];
|
||||
//
|
||||
//offsets for stability
|
||||
float offset1 = 1.0f; //0.1
|
||||
float offset2 = 1.0f; //0.1
|
||||
//for Guassian window: settings from paper:
|
||||
//float offset1 = 6.0f; // ~ (K1*L)^2 , K1 = 0.01
|
||||
//float offset2 = 58.0f; // ~ (K1*L)^2 , K2 = 0.03
|
||||
|
||||
|
||||
float offset3 = offset2/2;
|
||||
//
|
||||
//define window for SSIM: take uniform filter for now
|
||||
float sumfil = 0.0;
|
||||
int nn=-1;
|
||||
for(int j=-window/2;j<=window/2;j++)
|
||||
for(int i=-window/2;i<=window/2;i++)
|
||||
{
|
||||
nn+=1;
|
||||
if (flag_window != 2)
|
||||
ssimFilter[nn] = 1.0;
|
||||
else
|
||||
{
|
||||
float dist = (float)(i*i) + (float)(j*j);
|
||||
float tmp = 0.5f*dist/variance_window;
|
||||
ssimFilter[nn] = exp(-tmp);
|
||||
}
|
||||
sumfil +=ssimFilter[nn];
|
||||
}
|
||||
//normalize window
|
||||
nn=-1;
|
||||
for(int j=-window/2;j<=window/2;j++)
|
||||
for(int i=-window/2;i<=window/2;i++)
|
||||
{
|
||||
nn+=1;
|
||||
ssimFilter[nn] = ssimFilter[nn]/((float)sumfil);
|
||||
}
|
||||
//
|
||||
float ssimScene = 0.0; //avgerage SSIM for sequence
|
||||
//
|
||||
//SSIM: done with variables and defintion
|
||||
//
|
||||
|
||||
int sh = 8; //boundary offset
|
||||
double ssimScene = 0.0; //avgerage SSIM for sequence
|
||||
|
||||
while( refBytes == frameBytes && testBytes == frameBytes )
|
||||
{
|
||||
float ssimFrame = 0.0;
|
||||
|
||||
int numPixels = 0;
|
||||
|
||||
//skip over pixels vertically and horizontally
|
||||
//for window cases 1 and 2
|
||||
int skipH = 2;
|
||||
int skipV = 2;
|
||||
|
||||
//uniform window case, with window computation updated for each pixel horiz and vert: can't skip pixels for this case
|
||||
if (flag_window == 0)
|
||||
{
|
||||
skipH = 1;
|
||||
skipV = 1;
|
||||
}
|
||||
for(int i=sh;i<height-sh;i+=skipV)
|
||||
for(int j=sh;j<width-sh;j+=skipH)
|
||||
{
|
||||
avgTest[0] = 0.0;
|
||||
avgRef[0] = 0.0;
|
||||
contrastTest[0] = 0.0;
|
||||
contrastRef[0] = 0.0;
|
||||
crossCorr[0] = 0.0;
|
||||
|
||||
numPixels +=1;
|
||||
|
||||
if (flag_window > 0 )
|
||||
{
|
||||
//initialize statistics
|
||||
avgTest[0] = 0.0;
|
||||
avgRef[0] = 0.0;
|
||||
contrastTest[0] = 0.0;
|
||||
contrastRef[0] = 0.0;
|
||||
crossCorr[0] = 0.0;
|
||||
|
||||
int nn=-1;
|
||||
//compute contrast and correlation
|
||||
//windows are symmetrics
|
||||
for(int jj=-window/2;jj<=window/2;jj++)
|
||||
for(int ii=-window/2;ii<=window/2;ii++)
|
||||
{
|
||||
nn+=1;
|
||||
int i2 = i+ii;
|
||||
int j2 = j+jj;
|
||||
float tmp1 = (float)test[i2*width+j2];
|
||||
float tmp2 = (float)ref[i2*width+j2];
|
||||
|
||||
term1 = tmp1;
|
||||
term2 = tmp2;
|
||||
term3 = tmp1*tmp1;
|
||||
term4 = tmp2*tmp2;
|
||||
term5 = tmp1*tmp2;
|
||||
|
||||
//local average of each signal
|
||||
avgTest[0] += ssimFilter[nn]*term1;
|
||||
avgRef[0] += ssimFilter[nn]*term2;
|
||||
//local correlation/contrast of each signal
|
||||
contrastTest[0] += ssimFilter[nn]*term3;
|
||||
contrastRef[0] += ssimFilter[nn]*term4;
|
||||
//local cross correlation
|
||||
crossCorr[0] += ssimFilter[nn]*term5;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//for uniform window case == 0: only need to loop over whole window for first row and column, and then shift/update
|
||||
if (j == sh || i == sh)
|
||||
{
|
||||
//initialize statistics
|
||||
for(int k=0;k<window+2;k++)
|
||||
{
|
||||
avgTest[k] = 0.0;
|
||||
avgRef[k] = 0.0;
|
||||
contrastTest[k] = 0.0;
|
||||
contrastRef[k] = 0.0;
|
||||
crossCorr[k] = 0.0;
|
||||
}
|
||||
|
||||
int nn=-1;
|
||||
//compute contrast and correlation
|
||||
//windows are symmetrics
|
||||
for(int jj=-window/2;jj<=window/2;jj++)
|
||||
for(int ii=-window/2;ii<=window/2;ii++)
|
||||
{
|
||||
nn+=1;
|
||||
int i2 = i+ii;
|
||||
int j2 = j+jj;
|
||||
float tmp1 = (float)test[i2*width+j2];
|
||||
float tmp2 = (float)ref[i2*width+j2];
|
||||
|
||||
term1 = tmp1;
|
||||
term2 = tmp2;
|
||||
term3 = tmp1*tmp1;
|
||||
term4 = tmp2*tmp2;
|
||||
term5 = tmp1*tmp2;
|
||||
|
||||
//local average of each signal
|
||||
avgTest[jj+window/2+1] += term1;
|
||||
avgRef[jj+window/2+1] += term2;
|
||||
//local correlation/contrast of each signal
|
||||
contrastTest[jj+window/2+1] += term3;
|
||||
contrastRef[jj+window/2+1] += term4;
|
||||
//local cross correlation
|
||||
crossCorr[jj+window/2+1] += term5;
|
||||
|
||||
}
|
||||
|
||||
//normalize
|
||||
for(int k=1;k<window+2;k++)
|
||||
{
|
||||
avgTest[k] = ssimFilter[0]*avgTest[k];
|
||||
avgRef[k] = ssimFilter[0]*avgRef[k];
|
||||
contrastTest[k] = ssimFilter[0]*contrastTest[k];
|
||||
contrastRef[k] = ssimFilter[0]*contrastRef[k];
|
||||
crossCorr[k] = ssimFilter[0]*crossCorr[k];
|
||||
}
|
||||
|
||||
}
|
||||
//for all other pixels, update window filter computation
|
||||
else
|
||||
{
|
||||
//shift statistics horiz.
|
||||
for(int k=1;k<window+1;k++)
|
||||
{
|
||||
avgTest[k]=avgTest[k+1];
|
||||
avgRef[k]=avgRef[k+1];
|
||||
contrastTest[k] = contrastTest[k+1];
|
||||
contrastRef[k] = contrastRef[k+1];
|
||||
crossCorr[k] = crossCorr[k+1];
|
||||
}
|
||||
|
||||
//compute statistics for last column
|
||||
//update right-most column, by updating with bottom pixel contribution
|
||||
int j2 = j + window/2; //last column of window
|
||||
int i2 = i + window/2; //last window pixel of column
|
||||
int ix = i - window/2 - 1; //last window pixel of top neighboring pixel
|
||||
float tmp1 = (float)test[i2*width+j2];
|
||||
float tmp2 = (float)ref[i2*width+j2];
|
||||
float tmp1x = (float)test[ix*width+j2];
|
||||
float tmp2x = (float)ref[ix*width+j2];
|
||||
|
||||
avgTest[window+1] = righMostColumnAvgTest[j] + ssimFilter[0]*(tmp1 - tmp1x);
|
||||
avgRef[window+1] = righMostColumnAvgRef[j] + ssimFilter[0]*(tmp2 - tmp2x);
|
||||
contrastTest[window+1] = righMostColumnContrastTest[j] + ssimFilter[0]*(tmp1*tmp1 - tmp1x*tmp1x);
|
||||
contrastRef[window+1] = righMostColumnContrastRef[j] + ssimFilter[0]*(tmp2*tmp2 - tmp2x*tmp2x);
|
||||
crossCorr[window+1] = righMostColumnCrossCorr[j] + ssimFilter[0]*(tmp1*tmp2 - tmp1x*tmp2x);
|
||||
}
|
||||
|
||||
//sum over all columns
|
||||
for(int k=1;k<window+2;k++)
|
||||
{
|
||||
avgTest[0] += avgTest[k];
|
||||
avgRef[0] += avgRef[k];
|
||||
contrastTest[0] += contrastTest[k];
|
||||
contrastRef[0] += contrastRef[k];
|
||||
crossCorr[0] += crossCorr[k];
|
||||
}
|
||||
|
||||
//
|
||||
righMostColumnAvgTest[j] = avgTest[window+1];
|
||||
righMostColumnAvgRef[j] = avgRef[window+1];
|
||||
righMostColumnContrastTest[j] = contrastTest[window+1];
|
||||
righMostColumnContrastRef[j] = contrastRef[window+1];
|
||||
righMostColumnCrossCorr[j] = crossCorr[window+1];
|
||||
//
|
||||
|
||||
} //end of window = 0 case
|
||||
|
||||
float tmp1 = (contrastTest[0] - avgTest[0]*avgTest[0]);
|
||||
if (tmp1 < 0.0) tmp1 = 0.0;
|
||||
contrastTest[0] = sqrt(tmp1);
|
||||
float tmp2 = (contrastRef[0] - avgRef[0]*avgRef[0]);
|
||||
if (tmp2 < 0.0) tmp2 = 0.0;
|
||||
contrastRef[0] = sqrt(tmp2);
|
||||
crossCorr[0] = crossCorr[0] - avgTest[0]*avgRef[0];
|
||||
|
||||
float ssimCorrCoeff = (crossCorr[0]+offset3)/(contrastTest[0]*contrastRef[0] + offset3);
|
||||
float ssimLuminance = (2*avgTest[0]*avgRef[0]+offset1)/(avgTest[0]*avgTest[0] + avgRef[0]*avgRef[0] + offset1);
|
||||
float ssimContrast = (2*contrastTest[0]*contrastRef[0]+offset2)/(contrastTest[0]*contrastTest[0] + contrastRef[0]*contrastRef[0] + offset2);
|
||||
|
||||
float ssimPixel = ssimCorrCoeff * ssimLuminance * ssimContrast;
|
||||
ssimFrame += ssimPixel;
|
||||
|
||||
} //done with ssim computation
|
||||
|
||||
ssimFrame = ssimFrame / (numPixels);
|
||||
//printf("***SSIM for frame ***%f \n",ssimFrame);
|
||||
ssimScene += ssimFrame;
|
||||
//
|
||||
//SSIM: done with SSIM computation
|
||||
//
|
||||
ssimScene += SSIM_frame(ref, test, width, width, width, height);
|
||||
|
||||
frames++;
|
||||
|
||||
refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
||||
testBytes = (int) fread(test, 1, frameBytes, testFp);
|
||||
|
||||
}
|
||||
|
||||
//SSIM: normalize/average for sequence
|
||||
@ -744,12 +580,6 @@ SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName,
|
||||
delete [] ref;
|
||||
delete [] test;
|
||||
|
||||
delete [] righMostColumnAvgTest;
|
||||
delete [] righMostColumnAvgRef;
|
||||
delete [] righMostColumnContrastTest;
|
||||
delete [] righMostColumnContrastRef;
|
||||
delete [] righMostColumnCrossCorr;
|
||||
|
||||
|
||||
fclose(refFp);
|
||||
fclose(testFp);
|
||||
|
Loading…
x
Reference in New Issue
Block a user