]> git.sesse.net Git - mlt/blob - src/modules/qt/transition_vqm.cpp
Rename 'qimage' module to 'qt'
[mlt] / src / modules / qt / 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 #include <QApplication>
26 #include <QImage>
27 #include <QColor>
28 #include <QLocale>
29 #include <QPainter>
30 #include <QPalette>
31 #include <QFont>
32 #include <QString>
33
34 static QApplication *app = 0;
35
36 static double calc_psnr( const uint8_t *a, const uint8_t *b, int size, int bpp )
37 {
38         double mse = 0.0;
39         int n = size + 1;
40
41         while ( --n )
42         {
43                 int diff = *a - *b;
44                 mse += diff * diff;
45                 a += bpp;
46                 b += bpp;
47         }
48
49         return 10.0 * log10( 255.0 * 255.0 / ( mse == 0 ? 1e-10 : mse/size ) );
50 }
51
52 static double calc_ssim( const uint8_t *a, const uint8_t *b, int width, int height, int window_size, int bpp )
53 {
54         int     windows_x = width / window_size;
55         int windows_y = height / window_size;
56         double  avg = 0.0;
57
58         if ( !windows_x || !windows_y )
59                 return 0.0;
60
61         // for each window
62         for ( int y = 0; y < windows_y; ++y )
63                 for ( int x = 0; x < windows_x; ++x )
64                 {
65                         int     base_offset = x * window_size + y * window_size * width;
66                         double  ref_acc = 0.0,
67                                         ref_acc_2 = 0.0,
68                                         cmp_acc = 0.0,
69                                         cmp_acc_2 = 0.0,
70                                         ref_cmp_acc = 0.0;
71
72                         // accumulate the pixel values for this window
73                         for ( int j = 0; j < window_size; ++j )
74                                 for ( int i = 0; i < window_size; ++i )
75                                 {
76                                         uint8_t c_a = a[bpp * (base_offset + j * width + i)];
77                                         uint8_t c_b = b[bpp * (base_offset + j * width + i)];
78                                         ref_acc += c_a;
79                                         ref_acc_2 += c_a * c_a;
80                                         cmp_acc += c_b;
81                                         cmp_acc_2 += c_b * c_b;
82                                         ref_cmp_acc += c_a * c_b;
83                                 }
84
85                         // compute the SSIM for this window
86                         // http://en.wikipedia.org/wiki/SSIM
87                         // http://en.wikipedia.org/wiki/Variance
88                         // http://en.wikipedia.org/wiki/Covariance
89                         double n_samples = window_size * window_size,
90                                         ref_avg = ref_acc / n_samples,
91                                         ref_var = ref_acc_2 / n_samples - ref_avg * ref_avg,
92                                         cmp_avg = cmp_acc / n_samples,
93                                         cmp_var = cmp_acc_2 / n_samples - cmp_avg * cmp_avg,
94                                         ref_cmp_cov = ref_cmp_acc / n_samples - ref_avg * cmp_avg,
95                                         c1 = 6.5025, // (0.01*255.0)^2
96                                         c2 = 58.5225, // (0.03*255)^2
97                                         ssim_num = (2.0 * ref_avg * cmp_avg + c1) * (2.0 * ref_cmp_cov + c2),
98                                         ssim_den = (ref_avg * ref_avg + cmp_avg * cmp_avg + c1) * (ref_var + cmp_var + c2);
99
100                         // accumulate the SSIM
101                         avg += ssim_num / ssim_den;
102                 }
103
104         // return the average SSIM
105         return avg / windows_x / windows_y;
106 }
107
108 static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
109 {
110         mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
111         mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame );
112         mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) );
113         uint8_t *b_image;
114         int window_size = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "window_size" );
115         double psnr[3], ssim[3];
116
117         *format = mlt_image_yuv422;
118         mlt_frame_get_image( b_frame, &b_image, format, width, height, writable );
119         mlt_frame_get_image( a_frame, image, format, width, height, writable );
120
121         psnr[0] = calc_psnr( *image, b_image, *width * *height, 2 );
122         psnr[1] = calc_psnr( *image + 1, b_image + 1, *width * *height / 2, 4 );
123         psnr[2] = calc_psnr( *image + 3, b_image + 3, *width * *height / 2, 4 );
124         ssim[0] = calc_ssim( *image, b_image, *width, *height, window_size, 2 );
125         ssim[1] = calc_ssim( *image + 1, b_image + 1, *width / 2, *height, window_size, 4 );
126         ssim[2] = calc_ssim( *image + 3, b_image + 3, *width / 2, *height, window_size, 4 );
127         mlt_properties_set_double( properties, "meta.vqm.psnr.y", psnr[0] );
128         mlt_properties_set_double( properties, "meta.vqm.psnr.cb", psnr[1] );
129         mlt_properties_set_double( properties, "meta.vqm.psnr.cr", psnr[2] );
130         mlt_properties_set_double( properties, "meta.vqm.ssim.y", ssim[0] );
131         mlt_properties_set_double( properties, "meta.vqm.ssim.cb", ssim[1] );
132         mlt_properties_set_double( properties, "meta.vqm.ssim.cr", ssim[2] );
133         printf( "%05d %05.2f %05.2f %05.2f %5.3f %5.3f %5.3f\n",
134                         mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2],
135                         ssim[0], ssim[1], ssim[2] );
136
137         // copy the B frame to the bottom of the A frame for comparison
138         window_size = mlt_image_format_size( *format, *width, *height, NULL ) / 2;
139         memcpy( *image + window_size, b_image + window_size, window_size );
140
141         if ( !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "render" ) )
142                 return 0;
143
144         // get RGBA image for Qt drawing
145         *format = mlt_image_rgb24a;
146         mlt_frame_get_image( a_frame, image, format, width, height, 1 );
147
148         // convert mlt image to qimage
149         QImage img( *width, *height, QImage::Format_ARGB32 );
150         int y = *height + 1;
151         uint8_t *src = *image;
152         while ( --y )
153         {
154                 QRgb *dst = (QRgb*) img.scanLine( *height - y );
155                 int x = *width + 1;
156                 while ( --x )
157                 {
158                         *dst++ = qRgba( src[0], src[1], src[2], 255 );
159                         src += 4;
160                 }
161         }
162
163         // create QApplication, if needed
164         if ( !app )
165         {
166                 if ( qApp )
167                 {
168                         app = qApp;
169                 }
170                 else
171                 {
172                         int argc = 1;
173                         char* argv[] = { strdup( "unknown" ) };
174
175                         app = new QApplication( argc, argv );
176                         const char *localename = mlt_properties_get_lcnumeric( MLT_TRANSITION_PROPERTIES(transition) );
177                         QLocale::setDefault( QLocale( localename ) );
178                         free( argv[0] );
179                 }
180         }
181
182         // setup Qt drawing
183         QPainter painter;
184         painter.begin( &img );
185         painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing );
186
187         // draw some stuff with Qt
188         QPalette palette;
189         QFont font;
190         QString s;
191         font.setBold( true );
192         font.setPointSize( 30 * *height / 1080 );
193         painter.setPen( QColor("black") );
194         painter.drawLine( 0, *height/2 + 1, *width, *height/2 );
195         painter.setPen( QColor("white") );
196         painter.drawLine( 0, *height/2 - 1, *width, *height/2 );
197         painter.setFont( font );
198         s.sprintf( "Frame: %05d\nPSNR:   %05.2f (Y) %05.2f (Cb) %05.2f (Cr)\nSSIM:    %5.3f (Y) %5.3f (Cb) %5.3f (Cr)",
199                           mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2],
200                           ssim[0], ssim[1], ssim[2] );
201         painter.setPen( QColor("black") );
202         painter.drawText( 52, *height * 8 / 10 + 2, *width, *height, 0, s );
203         painter.setPen( QColor("white") );
204         painter.drawText( 50, *height * 8 / 10, *width, *height, 0, s );
205
206         // finish Qt drawing
207         painter.end();
208         window_size = mlt_image_format_size( *format, *width, *height, NULL );
209         uint8_t *dst = (uint8_t *) mlt_pool_alloc( window_size );
210         mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), "image", dst, window_size, mlt_pool_release, NULL );
211         *image = dst;
212
213         // convert qimage to mlt
214         y = *height + 1;
215         while ( --y )
216         {
217                 QRgb *src = (QRgb*) img.scanLine( *height - y );
218                 int x = *width + 1;
219                 while ( --x )
220                 {
221                         *dst++ = qRed( *src );
222                         *dst++ = qGreen( *src );
223                         *dst++ = qBlue( *src );
224                         *dst++ = qAlpha( *src );
225                         src++;
226                 }
227         }
228
229         return 0;
230 }
231
232 static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
233 {
234         mlt_frame_push_service( a_frame, transition );
235         mlt_frame_push_frame( a_frame, b_frame );
236         mlt_frame_push_get_image( a_frame, get_image );
237
238         return a_frame;
239 }
240
241 extern "C" {
242
243 mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
244 {
245         mlt_transition transition = mlt_transition_new();
246
247         if ( transition )
248         {
249                 mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
250
251                 transition->process = process;
252                 mlt_properties_set_int( properties, "_transition_type", 1 ); // video only
253                 mlt_properties_set_int( properties, "window_size", 8 );
254                 printf( "frame psnr[Y] psnr[Cb] psnr[Cr] ssim[Y] ssim[Cb] ssim[Cr]\n" );
255         }
256
257         return transition;
258 }
259
260 } // extern "C"