[MEncoder-users] Codec-independant objective quality measurement

Nicolas George nicolas.george at ens.fr
Sat Aug 5 15:09:19 CEST 2006


Le septidi 17 thermidor, an CCXIV, Loren Merritt a écrit :
> mkfifo a.yuv b.yuv
> ffmpeg -i $FILE1 -f rawvideo -y a.yuv &
> ffmpeg -i $FILE2 -f rawvideo -y b.yuv &
> tiny_psnr a.yuv b.yuv

That is approximatively what I do, except that the PSNR-calculating program
also does the plumbing itself.

I am currently adding a driver script, to do cycles of encode-evaluate with
various parameters and plot the result.

> Attached is an implementation of SSIM. As far as I can tell, this is the 
> second most popular metric, behind PSNR.

Thanks. I had heard of SSIM, but I did not manage to find a simple
description, nor a self-contained implementation. I will gladly integrate
it.

SSIM is a frame-by-frame metric, isn't it? If so, I intend to keep track of
the standard deviation too, to draw error bars.

I see that when averaging luminance and chrominance, you explicitly keep the
4-for-1 ratio of the YUV12 stream: for each pixel of the target image, the Y
value has weight 1 while the U and V values have weight 1/4, since they are
counted only once for the four pixels they affect. On the other hand,
pnmpsnr (the tool used by psnr-video.sh) uses the same weight to each of Y,
U and V.

I am not sure what the best is. On one hand, the downsampling of chrominance
is an artifact of the exchange format, and should be compensated. On the
other hand, if the codecs allows themselves to downsample chrominance, that
must mean that it is visually less important, and thus should be counted
with a lower weight. Did you give any thought to that question?

Regards,

-- 
  Nicolas George

> 
> --Loren Merritt

> /*
>  * Copyright (c) 2006 Loren Merritt
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 USA
>  */
> /*
>  * tiny_ssim.c
>  * Computes the Structural Similarity Metric between two rawYV12 video files.
>  * original algorithm:
>  * Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli,
>  *   "Image quality assessment: From error visibility to structural similarity,"
>  *   IEEE Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004.
>  *
>  * To improve speed, this implementation uses the standard approximation of
>  * overlapped 8x8 block sums, rather than the original gaussian weights.
>  */
> 
> #include <inttypes.h>
> #include <math.h>
> #include <stdio.h>
> 
> double ssim_8x8( const uint8_t *pix1, int stride1,
>                  const uint8_t *pix2, int stride2 )
> {
>     const int c1 = (int)(.01*.01*255*255*64 + .5);
>     const int c2 = (int)(.03*.03*255*255*64*63 + .5);
>     uint32_t s1=0, s2=0, ss=0, s12=0;
>     uint32_t vars;
>     int32_t covar;
>     int x, y;
> 
>     for(y=0; y<8; y++)
>         for(x=0; x<8; x++)
>         {
>             int a = pix1[x+y*stride1];
>             int b = pix2[x+y*stride2];
>             s1  += a;
>             s2  += b;
>             ss  += a*a;
>             ss  += b*b;
>             s12 += a*b;
>         }
> 
>     vars = ss*64 - s1*s1 - s2*s2;\
>     covar = s12*64 - s1*s2;\
>     return (double)(2*s1*s2 + c1) * (double)(2*covar + c2)\
>            / ((double)(s1*s1 + s2*s2 + c1) * (double)(vars + c2));
> }
> 
> double ssim_plane( const uint8_t *pix1, const uint8_t *pix2, int width, int height )
> {
>     int x, y;
>     double sum = 0.0;
>     for( y = 0; y <= height-8; y += 4 )
>         for( x = 0; x <= width-8; x += 4 )
>             sum += ssim_8x8( pix1+x+y*width, width,
>                              pix2+x+y*width, width );
>     return sum / ((width/4-1) * (height/4-1));
> }
> 
> uint64_t ssd_plane( const uint8_t *pix1, const uint8_t *pix2, int size )
> {
>     uint64_t ssd = 0;
>     int i;
>     for( i=0; i<size; i++ )
>     {
>         int d = pix1[i] - pix2[i];
>         ssd += d*d;
>     }
>     return ssd;
> }
> 
> double ssd_to_psnr( uint64_t ssd, uint64_t denom )
> {
>     return -10*log((double)ssd/(denom*255*255))/log(10);
> }
> 
> int main(int argc, char* argv[])
> {
>     FILE *f[2];
>     uint8_t *buf[2], *plane[2][3];
>     uint64_t ssd[3] = {0,0,0};
>     double ssim[3] = {0,0,0};
>     int frame_size, w, h;
>     int frames, seek;
>     int i;
> 
>     if( argc<4 || 2 != sscanf(argv[3], "%dx%d", &w, &h) )
>     {
>         printf("tiny_ssim <file1.yuv> <file2.yuv> <width>x<height> [<seek>]\n");
>         return -1;
>     }
> 
>     f[0] = fopen(argv[1], "rb");
>     f[1] = fopen(argv[2], "rb");
>     sscanf(argv[3], "%dx%d", &w, &h);
>     frame_size = w*h*3/2;
>     for( i=0; i<2; i++ )
>     {
>         buf[i] = malloc(frame_size);
>         plane[i][0] = buf[i];
>         plane[i][1] = plane[i][0] + w*h;
>         plane[i][2] = plane[i][1] + w*h/4;
>     }
>     seek = argc<5 ? 0 : atoi(argv[4]);
>     fseek(f[seek<0], seek < 0 ? -seek : seek, SEEK_SET);
> 
>     for( frames=0;; frames++ )
>     {
>         if( fread(buf[0], frame_size, 1, f[0]) != 1) break;
>         if( fread(buf[1], frame_size, 1, f[1]) != 1) break;
>         for( i=0; i<3; i++ )
>         {
>             ssd[i]  += ssd_plane ( plane[0][i], plane[1][i], w*h>>2*!!i );
>             ssim[i] += ssim_plane( plane[0][i], plane[1][i], w>>!!i, h>>!!i );
>         }
>     }
> 
>     if( !frames ) return 0;
> 
>     printf( "PSNR Y:%.3f  U:%.3f  V:%.3f  All:%.3f\n",
>             ssd_to_psnr( ssd[0], (uint64_t)frames*w*h ),
>             ssd_to_psnr( ssd[1], (uint64_t)frames*w*h/4 ),
>             ssd_to_psnr( ssd[2], (uint64_t)frames*w*h/4 ),
>             ssd_to_psnr( ssd[0] + ssd[1] + ssd[2], (uint64_t)frames*w*h*3/2 ) );
>     printf( "SSIM Y:%.5f U:%.5f V:%.5f All:%.5f\n",
>             ssim[0] / frames,
>             ssim[1] / frames,
>             ssim[2] / frames,
>             (ssim[0]*4 + ssim[1] + ssim[2]) / (frames*6) );
> 
>     return 0;
> }
> 

> _______________________________________________
> MEncoder-users mailing list
> MEncoder-users at mplayerhq.hu
> http://lists.mplayerhq.hu/mailman/listinfo/mencoder-users

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 185 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/mencoder-users/attachments/20060805/c69b0cfb/attachment.pgp>


More information about the MEncoder-users mailing list