]> git.sesse.net Git - mlt/blob - src/modules/qimage/transition_vqm.cpp
add vqm transition
[mlt] / src / modules / qimage / transition_vqm.cpp
1 /*
2  * transition_vqm.c -- video quality measurement
3  * Copyright (c) 2012 Dan Dennedy <dan@dennedy.org>
4  * Core psnr and ssim routines based on code from
5  *   qsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>
19  */
20
21 #include <framework/mlt.h>
22 #include <string.h>
23 #include <math.h>
24 #include <stdio.h>
25
26 static double calc_psnr( const uint8_t *a, const uint8_t *b, int size, int bpp )
27 {
28         double mse = 0.0;
29         int n = size + 1;
30
31         while ( --n )
32         {
33                 int diff = *a - *b;
34                 mse += diff * diff;
35                 a += bpp;
36                 b += bpp;
37         }
38
39         return 10.0 * log10( 255.0 * 255.0 / ( mse == 0 ? 1e-10 : mse/size ) );
40 }
41
42 static double calc_ssim( const uint8_t *a, const uint8_t *b, int width, int height, int window_size, int bpp )
43 {
44         int     windows_x = width / window_size;
45         int windows_y = height / window_size;
46         double  avg = 0.0;
47
48         if ( !windows_x || !windows_y )
49                 return 0.0;
50
51         // for each window
52         for ( int y = 0; y < windows_y; ++y )
53                 for ( int x = 0; x < windows_x; ++x )
54                 {
55                         int     base_offset = x * window_size + y * window_size * width;
56                         double  ref_acc = 0.0,
57                                         ref_acc_2 = 0.0,
58                                         cmp_acc = 0.0,
59                                         cmp_acc_2 = 0.0,
60                                         ref_cmp_acc = 0.0;
61
62                         // accumulate the pixel values for this window
63                         for ( int j = 0; j < window_size; ++j )
64                                 for ( int i = 0; i < window_size; ++i )
65                                 {
66                                         uint8_t c_a = a[bpp * (base_offset + j * width + i)];
67                                         uint8_t c_b = b[bpp * (base_offset + j * width + i)];
68                                         ref_acc += c_a;
69                                         ref_acc_2 += c_a * c_a;
70                                         cmp_acc += c_b;
71                                         cmp_acc_2 += c_b * c_b;
72                                         ref_cmp_acc += c_a * c_b;
73                                 }
74
75                         // compute the SSIM for this window
76                         // http://en.wikipedia.org/wiki/SSIM
77                         // http://en.wikipedia.org/wiki/Variance
78                         // http://en.wikipedia.org/wiki/Covariance
79                         double n_samples = window_size * window_size,
80                                         ref_avg = ref_acc / n_samples,
81                                         ref_var = ref_acc_2 / n_samples - ref_avg * ref_avg,
82                                         cmp_avg = cmp_acc / n_samples,
83                                         cmp_var = cmp_acc_2 / n_samples - cmp_avg * cmp_avg,
84                                         ref_cmp_cov = ref_cmp_acc / n_samples - ref_avg * cmp_avg,
85                                         c1 = 6.5025, // (0.01*255.0)^2
86                                         c2 = 58.5225, // (0.03*255)^2
87                                         ssim_num = (2.0 * ref_avg * cmp_avg + c1) * (2.0 * ref_cmp_cov + c2),
88                                         ssim_den = (ref_avg * ref_avg + cmp_avg * cmp_avg + c1) * (ref_var + cmp_var + c2);
89
90                         // accumulate the SSIM
91                         avg += ssim_num / ssim_den;
92                 }
93
94         // return the average SSIM
95         return avg / windows_x / windows_y;
96 }
97
98 static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
99 {
100         mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
101         mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) );
102         uint8_t *b_image;
103         int window_size = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "window_size" );
104         double psnr[3], ssim[3];
105
106         *format = mlt_image_yuv422;
107         mlt_frame_get_image( b_frame, &b_image, format, width, height, writable );
108         mlt_frame_get_image( a_frame, image, format, width, height, writable );
109
110         psnr[0] = calc_psnr( *image, b_image, *width * *height, 2 );
111         psnr[1] = calc_psnr( *image + 1, b_image + 1, *width * *height / 2, 4 );
112         psnr[2] = calc_psnr( *image + 3, b_image + 3, *width * *height / 2, 4 );
113         ssim[0] = calc_ssim( *image, b_image, *width, *height, window_size, 2 );
114         ssim[1] = calc_ssim( *image + 1, b_image + 1, *width / 2, *height, window_size, 4 );
115         ssim[2] = calc_ssim( *image + 3, b_image + 3, *width / 2, *height, window_size, 4 );
116         printf( "%05d %05.2f %05.2f %05.2f %5.3f %5.3f %5.3f\n",
117                         mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2],
118                         ssim[0], ssim[1], ssim[2] );
119
120         window_size = mlt_image_format_size( *format, *width, *height, NULL ) / 2;
121         memcpy( *image + window_size, b_image + window_size, window_size );
122
123         return 0;
124 }
125
126 static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
127 {
128         mlt_frame_push_service( a_frame, transition );
129         mlt_frame_push_frame( a_frame, b_frame );
130         mlt_frame_push_get_image( a_frame, get_image );
131
132         return a_frame;
133 }
134
135 extern "C" {
136
137 mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
138 {
139         mlt_transition transition = mlt_transition_new();
140
141         if ( transition )
142         {
143                 mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
144
145                 transition->process = process;
146                 mlt_properties_set_int( properties, "_transition_type", 1 ); // video only
147                 mlt_properties_set_int( properties, "window_size", 8 );
148                 printf( "frame psnr[Y] psnr[Cb] psnr[Cr] ssim[Y] ssim[Cb] ssim[Cr]\n" );
149         }
150
151         return transition;
152 }
153
154 } // extern "C"