[FFmpeg-devel] [PATCH] Add a tiny_ssim that is avified.

Ronald S. Bultje rsbultje at gmail.com
Fri Jun 19 14:44:51 CEST 2015


---
 tests/Makefile             |   5 +-
 tests/tiny_ssim.c          | 153 +------------------------------------
 tests/tiny_ssim_avf.c      | 159 +++++++++++++++++++++++++++++++++++++++
 tests/tiny_ssim_template.c | 183 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 350 insertions(+), 150 deletions(-)
 create mode 100644 tests/tiny_ssim_avf.c
 create mode 100644 tests/tiny_ssim_template.c

diff --git a/tests/Makefile b/tests/Makefile
index cffa541..642e287 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -188,7 +188,10 @@ $(FATE_EXTERN):
 	@echo "$@ requires external samples and SAMPLES not specified"; false
 endif
 
-FATE_UTILS = base64 tiny_psnr tiny_ssim
+FATE_UTILS = base64 tiny_psnr tiny_ssim tiny_ssim_avf
+
+tests/tiny_ssim_avf: tests/tiny_ssim_avf.o
+	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(FFLIBS) $(FF_EXTRALIBS) $(FF_DEP_LIBS)
 
 TOOL = ffmpeg
 
diff --git a/tests/tiny_ssim.c b/tests/tiny_ssim.c
index 9f355a3..678c4dd 100644
--- a/tests/tiny_ssim.c
+++ b/tests/tiny_ssim.c
@@ -27,154 +27,7 @@
  * overlapped 8x8 block sums, rather than the original gaussian weights.
  */
 
-#include "config.h"
-#include <inttypes.h>
-#include <limits.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
-#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
-
-#define BIT_DEPTH 8
-#define PIXEL_MAX ((1 << BIT_DEPTH)-1)
-typedef uint8_t  pixel;
-
-/****************************************************************************
- * structural similarity metric
- ****************************************************************************/
-static void ssim_4x4x2_core( const pixel *pix1, intptr_t stride1,
-                             const pixel *pix2, intptr_t stride2,
-                             int sums[2][4] )
-{
-    int x,y,z;
-
-    for( z = 0; z < 2; z++ )
-    {
-        uint32_t s1 = 0, s2 = 0, ss = 0, s12 = 0;
-        for( y = 0; y < 4; y++ )
-            for( x = 0; x < 4; 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;
-            }
-        sums[z][0] = s1;
-        sums[z][1] = s2;
-        sums[z][2] = ss;
-        sums[z][3] = s12;
-        pix1 += 4;
-        pix2 += 4;
-    }
-}
-
-static float ssim_end1( int s1, int s2, int ss, int s12 )
-{
-/* Maximum value for 10-bit is: ss*64 = (2^10-1)^2*16*4*64 = 4286582784, which will overflow in some cases.
- * s1*s1, s2*s2, and s1*s2 also obtain this value for edge cases: ((2^10-1)*16*4)^2 = 4286582784.
- * Maximum value for 9-bit is: ss*64 = (2^9-1)^2*16*4*64 = 1069551616, which will not overflow. */
-#if BIT_DEPTH > 9
-#define type float
-    static const float ssim_c1 = .01*.01*PIXEL_MAX*PIXEL_MAX*64;
-    static const float ssim_c2 = .03*.03*PIXEL_MAX*PIXEL_MAX*64*63;
-#else
-#define type int
-    static const int ssim_c1 = (int)(.01*.01*PIXEL_MAX*PIXEL_MAX*64 + .5);
-    static const int ssim_c2 = (int)(.03*.03*PIXEL_MAX*PIXEL_MAX*64*63 + .5);
-#endif
-    type fs1 = s1;
-    type fs2 = s2;
-    type fss = ss;
-    type fs12 = s12;
-    type vars = fss*64 - fs1*fs1 - fs2*fs2;
-    type covar = fs12*64 - fs1*fs2;
-    return (float)(2*fs1*fs2 + ssim_c1) * (float)(2*covar + ssim_c2)
-         / ((float)(fs1*fs1 + fs2*fs2 + ssim_c1) * (float)(vars + ssim_c2));
-#undef type
-}
-
-static float ssim_end4( int sum0[5][4], int sum1[5][4], int width )
-{
-    float ssim = 0.0;
-    int i;
-
-    for( i = 0; i < width; i++ )
-        ssim += ssim_end1( sum0[i][0] + sum0[i+1][0] + sum1[i][0] + sum1[i+1][0],
-                           sum0[i][1] + sum0[i+1][1] + sum1[i][1] + sum1[i+1][1],
-                           sum0[i][2] + sum0[i+1][2] + sum1[i][2] + sum1[i+1][2],
-                           sum0[i][3] + sum0[i+1][3] + sum1[i][3] + sum1[i+1][3] );
-    return ssim;
-}
-
-float ssim_plane(
-                           pixel *pix1, intptr_t stride1,
-                           pixel *pix2, intptr_t stride2,
-                           int width, int height, void *buf, int *cnt )
-{
-    int z = 0;
-    int x, y;
-    float ssim = 0.0;
-    int (*sum0)[4] = buf;
-    int (*sum1)[4] = sum0 + (width >> 2) + 3;
-    width >>= 2;
-    height >>= 2;
-    for( y = 1; y < height; y++ )
-    {
-        for( ; z <= y; z++ )
-        {
-            FFSWAP( void*, sum0, sum1 );
-            for( x = 0; x < width; x+=2 )
-                ssim_4x4x2_core( &pix1[4*(x+z*stride1)], stride1, &pix2[4*(x+z*stride2)], stride2, &sum0[x] );
-        }
-        for( x = 0; x < width-1; x += 4 )
-            ssim += ssim_end4( sum0+x, sum1+x, FFMIN(4,width-x-1) );
-    }
-//     *cnt = (height-1) * (width-1);
-    return ssim / ((height-1) * (width-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;
-}
-
-static double ssd_to_psnr( uint64_t ssd, uint64_t denom )
-{
-    return -10*log((double)ssd/(denom*255*255))/log(10);
-}
-
-static double ssim_db( double ssim, double weight )
-{
-    return 10*(log(weight)/log(10)-log(weight-ssim)/log(10));
-}
-
-static void print_results(uint64_t ssd[3], double ssim[3], int frames, int w, int h)
-{
-    printf( "PSNR Y:%.3f  U:%.3f  V:%.3f  All:%.3f | ",
-            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 (%.5f)",
-            ssim[0] / frames,
-            ssim[1] / frames,
-            ssim[2] / frames,
-            (ssim[0]*4 + ssim[1] + ssim[2]) / (frames*6),
-            ssim_db(ssim[0] * 4 + ssim[1] + ssim[2], frames*6));
-}
+#include "tests/tiny_ssim_template.c"
 
 int main(int argc, char* argv[])
 {
@@ -222,7 +75,9 @@ int main(int argc, char* argv[])
         if( fread(buf[1], frame_size, 1, f[1]) != 1) break;
         for( i=0; i<3; i++ )
         {
-            ssd_one[i]  = ssd_plane ( plane[0][i], plane[1][i], w*h>>2*!!i );
+            ssd_one[i]  = ssd_plane ( plane[0][i], w>>!!i,
+                                      plane[1][i], w>>!!i,
+                                      w>>!!i, h>>!!i );
             ssim_one[i] = ssim_plane( plane[0][i], w>>!!i,
                                      plane[1][i], w>>!!i,
                                      w>>!!i, h>>!!i, temp, NULL );
diff --git a/tests/tiny_ssim_avf.c b/tests/tiny_ssim_avf.c
new file mode 100644
index 0000000..f650178
--- /dev/null
+++ b/tests/tiny_ssim_avf.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2003-2013 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 "tests/tiny_ssim_template.c"
+
+#include "libavformat/avformat.h"
+#include "libavcodec/avcodec.h"
+
+struct inputStuff {
+    AVFormatContext *ctx;
+    AVCodecContext *avctx;
+    int videoStreamIndex;
+    AVFrame *pic;
+};
+
+static int open_file(struct inputStuff *i, const char *file) {
+    int res;
+
+    i->ctx = NULL;
+    if ((res = avformat_open_input(&i->ctx, file, NULL, NULL)))
+        return res;
+
+    if ((res = avformat_find_stream_info(i->ctx, NULL)) < 0)
+        return res;
+
+    AVCodec *codec;
+    if ((res = av_find_best_stream(i->ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0)) < 0)
+        return res;
+
+    i->videoStreamIndex = res;
+    i->avctx = i->ctx->streams[i->videoStreamIndex]->codec;
+
+    if ((res = avcodec_open2(i->avctx, codec, NULL)) < 0)
+        return res;
+
+    i->pic = av_frame_alloc();
+
+    return 0;
+}
+
+static int read_frame(struct inputStuff *i) {
+    AVPacket pkt;
+    while (!av_read_frame(i->ctx, &pkt)) {
+        int got;
+        if (pkt.stream_index != i->videoStreamIndex)
+            continue;
+        av_dup_packet(&pkt);
+        int res = avcodec_decode_video2(i->avctx, i->pic, &got, &pkt);
+        av_free_packet(&pkt);
+        if (res < 0) {
+            return res;
+        } else if (got > 0) {
+            return 0;
+        }
+    }
+
+    if (i->avctx->codec->capabilities & CODEC_CAP_DELAY ||
+        i->avctx->active_thread_type & FF_THREAD_FRAME)
+    {
+        do {
+            pkt.data = NULL;
+            pkt.size = 0;
+            int got, res = avcodec_decode_video2(i->avctx, i->pic, &got, &pkt);
+            if (res < 0) {
+                return res;
+            } else if (got > 0) {
+                return 0;
+            } else
+                break;
+        } while (1);
+    }
+
+    return -1;
+}
+
+int main(int argc, char* argv[])
+{
+    struct inputStuff f[2];
+    int *temp;
+    uint64_t ssd[3] = {0,0,0};
+    double ssim[3] = {0,0,0};
+    int frames;
+    int i;
+
+    if( argc<3)
+    {
+        printf("tiny_ssim <file1> <file2>\n");
+        return -1;
+    }
+
+    av_register_all();
+    if (open_file(&f[0], argv[1]) < 0 ||
+        open_file(&f[1], argv[2]) < 0) {
+        fprintf(stderr, "Failed to open one of the files\n");
+        return -2;
+    }
+
+    temp = malloc((2*f[0].avctx->width+12)*sizeof(*temp));
+
+    for( frames=0;; frames++ )
+    {
+        uint64_t ssd_one[3];
+        double ssim_one[3];
+        if (read_frame(&f[0]) < 0 ||
+            read_frame(&f[1]) < 0)
+            break;
+        for( i=0; i<3; i++ )
+        {
+            ssd_one[i]  = ssd_plane ( f[0].pic->data[i], f[0].pic->linesize[i],
+                                      f[1].pic->data[i], f[1].pic->linesize[i],
+                                      f[0].pic->width>>!!i, f[0].pic->height>>!!i);
+            ssim_one[i] = ssim_plane( f[0].pic->data[i], f[0].pic->linesize[i],
+                                      f[1].pic->data[i], f[1].pic->linesize[i],
+                                      f[0].pic->width>>!!i, f[0].pic->height>>!!i,
+                                      temp, NULL);
+            ssd[i] += ssd_one[i];
+            ssim[i] += ssim_one[i];
+        }
+
+        printf("Frame %d | ", frames);
+        print_results(ssd_one, ssim_one, 1, f[0].pic->width, f[0].pic->height);
+        printf("                \r");
+        fflush(stdout);
+    }
+
+    if( !frames ) return 0;
+
+    printf("Total %d frames | ", frames);
+    print_results(ssd, ssim, frames, f[0].avctx->width, f[0].avctx->height);
+    printf("\n");
+
+    return 0;
+}
diff --git a/tests/tiny_ssim_template.c b/tests/tiny_ssim_template.c
new file mode 100644
index 0000000..2588a76
--- /dev/null
+++ b/tests/tiny_ssim_template.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2003-2013 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 "config.h"
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+
+#define BIT_DEPTH 8
+#define PIXEL_MAX ((1 << BIT_DEPTH)-1)
+typedef uint8_t  pixel;
+
+/****************************************************************************
+ * structural similarity metric
+ ****************************************************************************/
+static void ssim_4x4x2_core( const pixel *pix1, intptr_t stride1,
+                             const pixel *pix2, intptr_t stride2,
+                             int sums[2][4] )
+{
+    int x,y,z;
+
+    for( z = 0; z < 2; z++ )
+    {
+        uint32_t s1 = 0, s2 = 0, ss = 0, s12 = 0;
+        for( y = 0; y < 4; y++ )
+            for( x = 0; x < 4; 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;
+            }
+        sums[z][0] = s1;
+        sums[z][1] = s2;
+        sums[z][2] = ss;
+        sums[z][3] = s12;
+        pix1 += 4;
+        pix2 += 4;
+    }
+}
+
+static float ssim_end1( int s1, int s2, int ss, int s12 )
+{
+/* Maximum value for 10-bit is: ss*64 = (2^10-1)^2*16*4*64 = 4286582784, which will overflow in some cases.
+ * s1*s1, s2*s2, and s1*s2 also obtain this value for edge cases: ((2^10-1)*16*4)^2 = 4286582784.
+ * Maximum value for 9-bit is: ss*64 = (2^9-1)^2*16*4*64 = 1069551616, which will not overflow. */
+#if BIT_DEPTH > 9
+#define type float
+    static const float ssim_c1 = .01*.01*PIXEL_MAX*PIXEL_MAX*64;
+    static const float ssim_c2 = .03*.03*PIXEL_MAX*PIXEL_MAX*64*63;
+#else
+#define type int
+    static const int ssim_c1 = (int)(.01*.01*PIXEL_MAX*PIXEL_MAX*64 + .5);
+    static const int ssim_c2 = (int)(.03*.03*PIXEL_MAX*PIXEL_MAX*64*63 + .5);
+#endif
+    type fs1 = s1;
+    type fs2 = s2;
+    type fss = ss;
+    type fs12 = s12;
+    type vars = fss*64 - fs1*fs1 - fs2*fs2;
+    type covar = fs12*64 - fs1*fs2;
+    return (float)(2*fs1*fs2 + ssim_c1) * (float)(2*covar + ssim_c2)
+         / ((float)(fs1*fs1 + fs2*fs2 + ssim_c1) * (float)(vars + ssim_c2));
+#undef type
+}
+
+static float ssim_end4( int sum0[5][4], int sum1[5][4], int width )
+{
+    float ssim = 0.0;
+    int i;
+
+    for( i = 0; i < width; i++ )
+        ssim += ssim_end1( sum0[i][0] + sum0[i+1][0] + sum1[i][0] + sum1[i+1][0],
+                           sum0[i][1] + sum0[i+1][1] + sum1[i][1] + sum1[i+1][1],
+                           sum0[i][2] + sum0[i+1][2] + sum1[i][2] + sum1[i+1][2],
+                           sum0[i][3] + sum0[i+1][3] + sum1[i][3] + sum1[i+1][3] );
+    return ssim;
+}
+
+static float ssim_plane(
+                           pixel *pix1, intptr_t stride1,
+                           pixel *pix2, intptr_t stride2,
+                           int width, int height, void *buf, int *cnt )
+{
+    int z = 0;
+    int x, y;
+    float ssim = 0.0;
+    int (*sum0)[4] = buf;
+    int (*sum1)[4] = sum0 + (width >> 2) + 3;
+    width >>= 2;
+    height >>= 2;
+    for( y = 1; y < height; y++ )
+    {
+        for( ; z <= y; z++ )
+        {
+            FFSWAP( void*, sum0, sum1 );
+            for( x = 0; x < width; x+=2 )
+                ssim_4x4x2_core( &pix1[4*(x+z*stride1)], stride1, &pix2[4*(x+z*stride2)], stride2, &sum0[x] );
+        }
+        for( x = 0; x < width-1; x += 4 )
+            ssim += ssim_end4( sum0+x, sum1+x, FFMIN(4,width-x-1) );
+    }
+//     *cnt = (height-1) * (width-1);
+    return ssim / ((height-1) * (width-1));
+}
+
+
+static uint64_t ssd_plane( const uint8_t *pix1, int stridea, const uint8_t *pix2, int strideb, int w, int h )
+{
+    uint64_t ssd = 0;
+    int x, y;
+
+    for (y=0;y<h;y++)
+    {
+        for (x=0;x<w;x++)
+        {
+             int d = pix1[x] - pix2[x];
+             ssd += d * d;
+        }
+        pix1 += stridea;
+        pix2 += strideb;
+    }
+    return ssd;
+}
+
+static double ssd_to_psnr( uint64_t ssd, uint64_t denom )
+{
+    return -10*log((double)ssd/(denom*255*255))/log(10);
+}
+
+static double ssim_db( double ssim, double weight )
+{
+    return 10*(log(weight)/log(10)-log(weight-ssim)/log(10));
+}
+
+static void print_results(uint64_t ssd[3], double ssim[3], int frames, int w, int h)
+{
+    printf( "PSNR Y:%.3f  U:%.3f  V:%.3f  All:%.3f | ",
+            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 (%.5f)",
+            ssim[0] / frames,
+            ssim[1] / frames,
+            ssim[2] / frames,
+            (ssim[0]*4 + ssim[1] + ssim[2]) / (frames*6),
+            ssim_db(ssim[0] * 4 + ssim[1] + ssim[2], frames*6));
+}
-- 
2.1.2



More information about the ffmpeg-devel mailing list