[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