Parent Directory | Revision Log
Revision 1.1 - (view) (download)
1 : | Skal | 1.1 | /***************************************************************************** |
2 : | * | ||
3 : | * XVID MPEG-4 VIDEO CODEC | ||
4 : | * - SSIM plugin: computes the SSIM metric - | ||
5 : | * | ||
6 : | * Copyright(C) 2005 Johannes Reinhardt <Johannes.Reinhardt@gmx.de> | ||
7 : | * | ||
8 : | * This program is free software ; you can redistribute it and/or modify | ||
9 : | * it under the terms of the GNU General Public License as published by | ||
10 : | * the Free Software Foundation ; either version 2 of the License, or | ||
11 : | * (at your option) any later version. | ||
12 : | * | ||
13 : | * This program is distributed in the hope that it will be useful, | ||
14 : | * but WITHOUT ANY WARRANTY ; without even the implied warranty of | ||
15 : | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 : | * GNU General Public License for more details. | ||
17 : | * | ||
18 : | * You should have received a copy of the GNU General Public License | ||
19 : | * along with this program ; if not, write to the Free Software | ||
20 : | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 : | * | ||
22 : | * | ||
23 : | * | ||
24 : | ****************************************************************************/ | ||
25 : | |||
26 : | #include <malloc.h> | ||
27 : | #include <stdio.h> | ||
28 : | #include <math.h> | ||
29 : | #include <stdint.h> | ||
30 : | #include "../xvid.h" | ||
31 : | #include "plugin_ssim.h" | ||
32 : | #include "../utils/emms.h" | ||
33 : | |||
34 : | /* needed for visualisation of the error map with X | ||
35 : | display.h borrowed from x264 | ||
36 : | #include "display.h"*/ | ||
37 : | |||
38 : | typedef struct framestat_t framestat_t; | ||
39 : | |||
40 : | struct framestat_t{ | ||
41 : | int type; | ||
42 : | int quant; | ||
43 : | float ssim_min; | ||
44 : | float ssim_max; | ||
45 : | float ssim_avg; | ||
46 : | framestat_t* next; | ||
47 : | }; | ||
48 : | |||
49 : | |||
50 : | typedef int (*lumfunc)(uint8_t* ptr, int stride); | ||
51 : | typedef void (*csfunc)(uint8_t* ptro, uint8_t* ptrc, int stride, int lumo, int lumc, int* pdevo, int* pdevc, int* pcorr); | ||
52 : | |||
53 : | int lum_8x8_mmx(uint8_t* ptr, int stride); | ||
54 : | void consim_mmx(uint8_t* ptro, uint8_t* ptrc, int stride, int lumo, int lumc, int* pdevo, int* pdevc, int* pcorr); | ||
55 : | void consim_sse2(uint8_t* ptro, uint8_t* ptrc, int stride, int lumo, int lumc, int* pdevo, int* pdevc, int* pcorr); | ||
56 : | |||
57 : | typedef struct{ | ||
58 : | |||
59 : | plg_ssim_param_t* param; | ||
60 : | |||
61 : | /* for error map visualisation | ||
62 : | uint8_t* errmap; | ||
63 : | */ | ||
64 : | |||
65 : | /*for average SSIM*/ | ||
66 : | float ssim_sum; | ||
67 : | int frame_cnt; | ||
68 : | |||
69 : | /*function pointers*/ | ||
70 : | lumfunc func8x8; | ||
71 : | lumfunc func2x8; | ||
72 : | csfunc consim; | ||
73 : | |||
74 : | /*stats - for debugging*/ | ||
75 : | framestat_t* head; | ||
76 : | framestat_t* tail; | ||
77 : | } ssim_data_t; | ||
78 : | |||
79 : | /* append the stats for another frame to the linked list*/ | ||
80 : | void framestat_append(ssim_data_t* ssim,int type, int quant, float min, float max, float avg){ | ||
81 : | framestat_t* act; | ||
82 : | act = (framestat_t*) malloc(sizeof(framestat_t)); | ||
83 : | act->type = type; | ||
84 : | act->quant = quant; | ||
85 : | act->ssim_min = min; | ||
86 : | act->ssim_max = max; | ||
87 : | act->ssim_avg = avg; | ||
88 : | act->next = NULL; | ||
89 : | |||
90 : | if(ssim->head == NULL){ | ||
91 : | ssim->head = act; | ||
92 : | ssim->tail = act; | ||
93 : | } else { | ||
94 : | ssim->tail->next = act; | ||
95 : | ssim->tail = act; | ||
96 : | } | ||
97 : | } | ||
98 : | |||
99 : | /* destroy the whole list*/ | ||
100 : | void framestat_free(framestat_t* stat){ | ||
101 : | if(stat != NULL){ | ||
102 : | if(stat->next != NULL) framestat_free(stat->next); | ||
103 : | free(stat); | ||
104 : | } | ||
105 : | return; | ||
106 : | } | ||
107 : | |||
108 : | /*writeout the collected stats*/ | ||
109 : | void framestat_write(ssim_data_t* ssim, char* path){ | ||
110 : | FILE* out = fopen(path,"w"); | ||
111 : | if(out==NULL) printf("Cannot open %s in plugin_ssim\n",path); | ||
112 : | framestat_t* tmp = ssim->head; | ||
113 : | |||
114 : | fprintf(out,"SSIM Error Metric\n"); | ||
115 : | fprintf(out,"quant avg min max"); | ||
116 : | while(tmp->next->next != NULL){ | ||
117 : | fprintf(out,"%3d %1.4f %1.4f %1.4f\n",tmp->quant,tmp->ssim_avg,tmp->ssim_min,tmp->ssim_max); | ||
118 : | tmp = tmp->next; | ||
119 : | } | ||
120 : | fclose(out); | ||
121 : | } | ||
122 : | |||
123 : | /*writeout the collected stats in octave readable format*/ | ||
124 : | void framestat_write_oct(ssim_data_t* ssim, char* path){ | ||
125 : | |||
126 : | FILE* out = fopen(path,"w"); | ||
127 : | if(out==NULL) printf("Cannot open %s in plugin_ssim\n",path); | ||
128 : | framestat_t* tmp; | ||
129 : | |||
130 : | fprintf(out,"quant = ["); | ||
131 : | tmp = ssim->head; | ||
132 : | while(tmp->next->next != NULL){ | ||
133 : | fprintf(out,"%d, ",tmp->quant); | ||
134 : | tmp = tmp->next; | ||
135 : | } | ||
136 : | fprintf(out,"%d];\n\n",tmp->quant); | ||
137 : | |||
138 : | fprintf(out,"ssim_min = ["); | ||
139 : | tmp = ssim->head; | ||
140 : | while(tmp->next->next != NULL){ | ||
141 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
142 : | tmp = tmp->next; | ||
143 : | } | ||
144 : | fprintf(out,"%f];\n\n",tmp->ssim_min); | ||
145 : | |||
146 : | fprintf(out,"ssim_max = ["); | ||
147 : | tmp = ssim->head; | ||
148 : | while(tmp->next->next != NULL){ | ||
149 : | fprintf(out,"%f, ",tmp->ssim_max); | ||
150 : | tmp = tmp->next; | ||
151 : | } | ||
152 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
153 : | |||
154 : | fprintf(out,"ssim_avg = ["); | ||
155 : | tmp = ssim->head; | ||
156 : | while(tmp->next->next != NULL){ | ||
157 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
158 : | tmp = tmp->next; | ||
159 : | } | ||
160 : | fprintf(out,"%f];\n\n",tmp->ssim_avg); | ||
161 : | |||
162 : | fprintf(out,"ivop = ["); | ||
163 : | tmp = ssim->head; | ||
164 : | while(tmp->next->next != NULL){ | ||
165 : | if(tmp->type == XVID_TYPE_IVOP){ | ||
166 : | fprintf(out,"%d, ",tmp->quant); | ||
167 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
168 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
169 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
170 : | } | ||
171 : | tmp = tmp->next; | ||
172 : | } | ||
173 : | fprintf(out,"%d, ",tmp->quant); | ||
174 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
175 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
176 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
177 : | |||
178 : | fprintf(out,"pvop = ["); | ||
179 : | tmp = ssim->head; | ||
180 : | while(tmp->next->next != NULL){ | ||
181 : | if(tmp->type == XVID_TYPE_PVOP){ | ||
182 : | fprintf(out,"%d, ",tmp->quant); | ||
183 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
184 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
185 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
186 : | } | ||
187 : | tmp = tmp->next; | ||
188 : | } | ||
189 : | fprintf(out,"%d, ",tmp->quant); | ||
190 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
191 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
192 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
193 : | |||
194 : | fprintf(out,"bvop = ["); | ||
195 : | tmp = ssim->head; | ||
196 : | while(tmp->next->next != NULL){ | ||
197 : | if(tmp->type == XVID_TYPE_BVOP){ | ||
198 : | fprintf(out,"%d, ",tmp->quant); | ||
199 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
200 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
201 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
202 : | } | ||
203 : | tmp = tmp->next; | ||
204 : | } | ||
205 : | fprintf(out,"%d, ",tmp->quant); | ||
206 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
207 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
208 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
209 : | |||
210 : | fclose(out); | ||
211 : | } | ||
212 : | |||
213 : | /*calculate the luminance of a 8x8 block*/ | ||
214 : | int lum_8x8_c(uint8_t* ptr, int stride){ | ||
215 : | int mean=0,i,j; | ||
216 : | for(i=0;i< 8;i++) | ||
217 : | for(j=0;j< 8;j++){ | ||
218 : | mean += ptr[i*stride + j]; | ||
219 : | } | ||
220 : | return mean; | ||
221 : | } | ||
222 : | |||
223 : | /*calculate the difference between two blocks next to each other on a row*/ | ||
224 : | int lum_2x8_c(uint8_t* ptr, int stride){ | ||
225 : | int mean=0,i; | ||
226 : | /*Luminance*/ | ||
227 : | for(i=0;i< 8;i++){ | ||
228 : | mean -= *(ptr-1); | ||
229 : | mean += *(ptr+ 8 - 1); | ||
230 : | ptr+=stride; | ||
231 : | } | ||
232 : | return mean; | ||
233 : | } | ||
234 : | |||
235 : | /*calculate contrast and correlation of the two blocks*/ | ||
236 : | void iconsim_c(uint8_t* ptro, uint8_t* ptrc, int stride, int lumo, int lumc, int* pdevo, int* pdevc, int* pcorr){ | ||
237 : | int valo, valc, devo =0, devc=0, corr=0; | ||
238 : | int i,j; | ||
239 : | for(i=0;i< 8;i++){ | ||
240 : | for(j=0;j< 8;j++){ | ||
241 : | valo = *ptro - lumo; | ||
242 : | valc = *ptrc - lumc; | ||
243 : | devo += valo*valo; | ||
244 : | ptro++; | ||
245 : | devc += valc*valc; | ||
246 : | ptrc++; | ||
247 : | corr += valo*valc; | ||
248 : | } | ||
249 : | ptro += stride -8; | ||
250 : | ptrc += stride -8; | ||
251 : | } | ||
252 : | *pdevo = devo; | ||
253 : | *pdevc = devc; | ||
254 : | *pcorr = corr; | ||
255 : | }; | ||
256 : | |||
257 : | /*calculate the final ssim value*/ | ||
258 : | static float calc_ssim(int meano, int meanc, int devo, int devc, int corr){ | ||
259 : | static const float c1 = (0.01*255)*(0.01*255); | ||
260 : | static const float c2 = (0.03*255)*(0.03*255); | ||
261 : | float fmeano,fmeanc,fdevo,fdevc,fcorr; | ||
262 : | fmeanc = (float) meanc; | ||
263 : | fmeano = (float) meano; | ||
264 : | fdevo = (float) devo; | ||
265 : | fdevc = (float) devc; | ||
266 : | fcorr = (float) corr; | ||
267 : | // printf("meano: %f meanc: %f devo: %f devc: %f corr: %f\n",fmeano,fmeanc,fdevo,fdevc,fcorr); | ||
268 : | return ((2.0*fmeano*fmeanc + c1)*(fcorr/32.0 + c2))/((fmeano*fmeano + fmeanc*fmeanc + c1)*(fdevc/64.0 + fdevo/64.0 + c2)); | ||
269 : | } | ||
270 : | |||
271 : | static void ssim_after(xvid_plg_data_t* data, ssim_data_t* ssim){ | ||
272 : | int i,j,c=0; | ||
273 : | int width,height,str,ovr; | ||
274 : | unsigned char * ptr1,*ptr2; | ||
275 : | float isum=0, min=1.00,max=0.00, val; | ||
276 : | int meanc, meano; | ||
277 : | int devc, devo, corr; | ||
278 : | |||
279 : | #define GRID 1 | ||
280 : | |||
281 : | width = data->width - 8; | ||
282 : | height = data->height - 8; | ||
283 : | str = data->original.stride[0]; | ||
284 : | if(str != data->current.stride[0]) printf("WARNING: Different strides in plugin_ssim original: %d current: %d\n",str,data->current.stride[0]); | ||
285 : | ovr = str - width + (width % GRID); | ||
286 : | |||
287 : | ptr1 = (unsigned char*) data->original.plane[0]; | ||
288 : | ptr2 = (unsigned char*) data->current.plane[0]; | ||
289 : | |||
290 : | |||
291 : | |||
292 : | /*TODO: Thread*/ | ||
293 : | for(i=0;i<height;i+=GRID){ | ||
294 : | /*begin of each row*/ | ||
295 : | meano = meanc = devc = devo = corr = 0; | ||
296 : | meano = ssim->func8x8(ptr1,str); | ||
297 : | meanc = ssim->func8x8(ptr2,str); | ||
298 : | ssim->consim(ptr1,ptr2,str,meano>>6,meanc>>6,&devo,&devc,&corr); | ||
299 : | |||
300 : | val = calc_ssim(meano,meanc,devo,devc,corr); | ||
301 : | isum += val; | ||
302 : | c++; | ||
303 : | /* for visualisation | ||
304 : | if(ssim->param->b_visualize) | ||
305 : | ssim->errmap[i*width] = (uint8_t) 127*val; | ||
306 : | */ | ||
307 : | |||
308 : | if(val < min) min = val; | ||
309 : | if(val > max) max = val; | ||
310 : | ptr1+=GRID; | ||
311 : | ptr2+=GRID; | ||
312 : | /*rest of each row*/ | ||
313 : | for(j=1;j<width;j+=GRID){ | ||
314 : | /* for grid = 1 use | ||
315 : | meano += ssim->func2x8(ptr1,str); | ||
316 : | meanc += ssim->func2x8(ptr2,str); | ||
317 : | */ | ||
318 : | meano = ssim->func8x8(ptr1,str); | ||
319 : | meanc = ssim->func8x8(ptr2,str); | ||
320 : | ssim->consim(ptr1,ptr2,str,meano>>6,meanc>>6,&devo,&devc,&corr); | ||
321 : | val = calc_ssim(meano,meanc,devo,devc,corr); | ||
322 : | isum += val; | ||
323 : | c++; | ||
324 : | /* for visualisation | ||
325 : | if(ssim->param->b_visualize) | ||
326 : | ssim->errmap[i*width +j] = (uint8_t) 255*val; | ||
327 : | */ | ||
328 : | if(val < min) min = val; | ||
329 : | if(val > max) max = val; | ||
330 : | ptr1+=GRID; | ||
331 : | ptr2+=GRID; | ||
332 : | } | ||
333 : | ptr1 +=ovr; | ||
334 : | ptr2 +=ovr; | ||
335 : | } | ||
336 : | isum/=c; | ||
337 : | ssim->ssim_sum += isum; | ||
338 : | ssim->frame_cnt++; | ||
339 : | |||
340 : | if(ssim->param->stat_path != NULL) | ||
341 : | framestat_append(ssim,data->type,data->quant,min,max,isum); | ||
342 : | |||
343 : | /* for visualization | ||
344 : | if(ssim->param->b_visualize){ | ||
345 : | disp_gray(0,ssim->errmap,width,height,width, "Error-Map"); | ||
346 : | disp_gray(1,data->original.plane[0],data->width,data->height,data->original.stride[0],"Original"); | ||
347 : | disp_gray(2,data->current.plane[0],data->width,data->height,data->original.stride[0],"Compressed"); | ||
348 : | disp_sync(); | ||
349 : | } | ||
350 : | */ | ||
351 : | if(ssim->param->b_printstat){ | ||
352 : | printf("SSIM: avg: %f min: %f max: %f\n",isum,min,max); | ||
353 : | } | ||
354 : | |||
355 : | } | ||
356 : | |||
357 : | static int ssim_create(xvid_plg_create_t* create, void** handle){ | ||
358 : | ssim_data_t* ssim; | ||
359 : | plg_ssim_param_t* param; | ||
360 : | int cpu_flags; | ||
361 : | param = (plg_ssim_param_t*) malloc(sizeof(plg_ssim_param_t)); | ||
362 : | *param = *((plg_ssim_param_t*) create->param); | ||
363 : | ssim = (ssim_data_t*) malloc(sizeof(ssim_data_t)); | ||
364 : | |||
365 : | cpu_flags = check_cpu_features(); | ||
366 : | |||
367 : | ssim->func8x8 = lum_8x8_c; | ||
368 : | ssim->func2x8 = lum_2x8_c; | ||
369 : | ssim->consim = iconsim_c; | ||
370 : | |||
371 : | ssim->param = param; | ||
372 : | |||
373 : | if(cpu_flags & XVID_CPU_MMX){ | ||
374 : | ssim->func8x8 = lum_8x8_mmx; | ||
375 : | ssim->consim = consim_mmx; | ||
376 : | } | ||
377 : | if(cpu_flags & XVID_CPU_SSE2){ | ||
378 : | ssim->consim = consim_sse2; | ||
379 : | } | ||
380 : | |||
381 : | ssim->ssim_sum = 0.0; | ||
382 : | ssim->frame_cnt = 0; | ||
383 : | |||
384 : | /* for visualization | ||
385 : | if(param->b_visualize){ | ||
386 : | //error map | ||
387 : | ssim->errmap = (uint8_t*) malloc(sizeof(uint8_t)*(create->width-8)*(create->height-8)); | ||
388 : | } else { | ||
389 : | ssim->errmap = NULL; | ||
390 : | }; | ||
391 : | */ | ||
392 : | |||
393 : | /*stats*/ | ||
394 : | ssim->head=NULL; | ||
395 : | ssim->tail=NULL; | ||
396 : | |||
397 : | *(handle) = (void*) ssim; | ||
398 : | |||
399 : | return 0; | ||
400 : | } | ||
401 : | |||
402 : | int xvid_plugin_ssim(void * handle, int opt, void * param1, void * param2){ | ||
403 : | ssim_data_t* ssim; | ||
404 : | switch(opt){ | ||
405 : | case(XVID_PLG_INFO): | ||
406 : | ((xvid_plg_info_t*) param1)->flags = XVID_REQORIGINAL; | ||
407 : | break; | ||
408 : | case(XVID_PLG_CREATE): | ||
409 : | ssim_create((xvid_plg_create_t*) param1,(void**) param2); | ||
410 : | break; | ||
411 : | case(XVID_PLG_BEFORE): | ||
412 : | case(XVID_PLG_FRAME): | ||
413 : | break; | ||
414 : | case(XVID_PLG_AFTER): | ||
415 : | ssim_after((xvid_plg_data_t*) param1, (ssim_data_t*) handle); | ||
416 : | break; | ||
417 : | case(XVID_PLG_DESTROY): | ||
418 : | ssim = (ssim_data_t*) handle; | ||
419 : | printf("Average SSIM: %f\n",ssim->ssim_sum/ssim->frame_cnt); | ||
420 : | if(ssim->param->stat_path != NULL) | ||
421 : | framestat_write(ssim,ssim->param->stat_path); | ||
422 : | framestat_free(ssim->head); | ||
423 : | //free(ssim->errmap); | ||
424 : | free(ssim->param); | ||
425 : | free(ssim); | ||
426 : | break; | ||
427 : | default: | ||
428 : | break; | ||
429 : | } | ||
430 : | return 0; | ||
431 : | }; |
No admin address has been configured | ViewVC Help |
Powered by ViewVC 1.0.4 |