Parent Directory | Revision Log
Revision 1.4 - (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 : | Isibaar | 1.3 | #include "../portab.h" |
30 : | Skal | 1.1 | #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 : | Isibaar | 1.3 | framestat_t* tmp = ssim->head; |
111 : | Skal | 1.1 | FILE* out = fopen(path,"w"); |
112 : | if(out==NULL) printf("Cannot open %s in plugin_ssim\n",path); | ||
113 : | |||
114 : | fprintf(out,"SSIM Error Metric\n"); | ||
115 : | Skal | 1.4 | fprintf(out,"quant avg min max\n"); |
116 : | Skal | 1.1 | 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 : | Isibaar | 1.3 | framestat_t* tmp; |
126 : | Skal | 1.1 | FILE* out = fopen(path,"w"); |
127 : | if(out==NULL) printf("Cannot open %s in plugin_ssim\n",path); | ||
128 : | |||
129 : | fprintf(out,"quant = ["); | ||
130 : | tmp = ssim->head; | ||
131 : | while(tmp->next->next != NULL){ | ||
132 : | fprintf(out,"%d, ",tmp->quant); | ||
133 : | tmp = tmp->next; | ||
134 : | } | ||
135 : | fprintf(out,"%d];\n\n",tmp->quant); | ||
136 : | |||
137 : | fprintf(out,"ssim_min = ["); | ||
138 : | tmp = ssim->head; | ||
139 : | while(tmp->next->next != NULL){ | ||
140 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
141 : | tmp = tmp->next; | ||
142 : | } | ||
143 : | fprintf(out,"%f];\n\n",tmp->ssim_min); | ||
144 : | |||
145 : | fprintf(out,"ssim_max = ["); | ||
146 : | tmp = ssim->head; | ||
147 : | while(tmp->next->next != NULL){ | ||
148 : | fprintf(out,"%f, ",tmp->ssim_max); | ||
149 : | tmp = tmp->next; | ||
150 : | } | ||
151 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
152 : | |||
153 : | fprintf(out,"ssim_avg = ["); | ||
154 : | tmp = ssim->head; | ||
155 : | while(tmp->next->next != NULL){ | ||
156 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
157 : | tmp = tmp->next; | ||
158 : | } | ||
159 : | fprintf(out,"%f];\n\n",tmp->ssim_avg); | ||
160 : | |||
161 : | fprintf(out,"ivop = ["); | ||
162 : | tmp = ssim->head; | ||
163 : | while(tmp->next->next != NULL){ | ||
164 : | if(tmp->type == XVID_TYPE_IVOP){ | ||
165 : | fprintf(out,"%d, ",tmp->quant); | ||
166 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
167 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
168 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
169 : | } | ||
170 : | tmp = tmp->next; | ||
171 : | } | ||
172 : | fprintf(out,"%d, ",tmp->quant); | ||
173 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
174 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
175 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
176 : | |||
177 : | fprintf(out,"pvop = ["); | ||
178 : | tmp = ssim->head; | ||
179 : | while(tmp->next->next != NULL){ | ||
180 : | if(tmp->type == XVID_TYPE_PVOP){ | ||
181 : | fprintf(out,"%d, ",tmp->quant); | ||
182 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
183 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
184 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
185 : | } | ||
186 : | tmp = tmp->next; | ||
187 : | } | ||
188 : | fprintf(out,"%d, ",tmp->quant); | ||
189 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
190 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
191 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
192 : | |||
193 : | fprintf(out,"bvop = ["); | ||
194 : | tmp = ssim->head; | ||
195 : | while(tmp->next->next != NULL){ | ||
196 : | if(tmp->type == XVID_TYPE_BVOP){ | ||
197 : | fprintf(out,"%d, ",tmp->quant); | ||
198 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
199 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
200 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
201 : | } | ||
202 : | tmp = tmp->next; | ||
203 : | } | ||
204 : | fprintf(out,"%d, ",tmp->quant); | ||
205 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
206 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
207 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
208 : | |||
209 : | fclose(out); | ||
210 : | } | ||
211 : | |||
212 : | /*calculate the luminance of a 8x8 block*/ | ||
213 : | int lum_8x8_c(uint8_t* ptr, int stride){ | ||
214 : | int mean=0,i,j; | ||
215 : | for(i=0;i< 8;i++) | ||
216 : | for(j=0;j< 8;j++){ | ||
217 : | mean += ptr[i*stride + j]; | ||
218 : | } | ||
219 : | return mean; | ||
220 : | } | ||
221 : | |||
222 : | /*calculate the difference between two blocks next to each other on a row*/ | ||
223 : | int lum_2x8_c(uint8_t* ptr, int stride){ | ||
224 : | int mean=0,i; | ||
225 : | /*Luminance*/ | ||
226 : | for(i=0;i< 8;i++){ | ||
227 : | mean -= *(ptr-1); | ||
228 : | mean += *(ptr+ 8 - 1); | ||
229 : | ptr+=stride; | ||
230 : | } | ||
231 : | return mean; | ||
232 : | } | ||
233 : | |||
234 : | /*calculate contrast and correlation of the two blocks*/ | ||
235 : | void iconsim_c(uint8_t* ptro, uint8_t* ptrc, int stride, int lumo, int lumc, int* pdevo, int* pdevc, int* pcorr){ | ||
236 : | int valo, valc, devo =0, devc=0, corr=0; | ||
237 : | int i,j; | ||
238 : | for(i=0;i< 8;i++){ | ||
239 : | for(j=0;j< 8;j++){ | ||
240 : | valo = *ptro - lumo; | ||
241 : | valc = *ptrc - lumc; | ||
242 : | devo += valo*valo; | ||
243 : | ptro++; | ||
244 : | devc += valc*valc; | ||
245 : | ptrc++; | ||
246 : | corr += valo*valc; | ||
247 : | } | ||
248 : | ptro += stride -8; | ||
249 : | ptrc += stride -8; | ||
250 : | } | ||
251 : | *pdevo = devo; | ||
252 : | *pdevc = devc; | ||
253 : | *pcorr = corr; | ||
254 : | }; | ||
255 : | |||
256 : | /*calculate the final ssim value*/ | ||
257 : | static float calc_ssim(int meano, int meanc, int devo, int devc, int corr){ | ||
258 : | static const float c1 = (0.01*255)*(0.01*255); | ||
259 : | static const float c2 = (0.03*255)*(0.03*255); | ||
260 : | float fmeano,fmeanc,fdevo,fdevc,fcorr; | ||
261 : | fmeanc = (float) meanc; | ||
262 : | fmeano = (float) meano; | ||
263 : | fdevo = (float) devo; | ||
264 : | fdevc = (float) devc; | ||
265 : | fcorr = (float) corr; | ||
266 : | Skal | 1.4 | /* printf("meano: %f meanc: %f devo: %f devc: %f corr: %f\n",fmeano,fmeanc,fdevo,fdevc,fcorr); */ |
267 : | Skal | 1.1 | return ((2.0*fmeano*fmeanc + c1)*(fcorr/32.0 + c2))/((fmeano*fmeano + fmeanc*fmeanc + c1)*(fdevc/64.0 + fdevo/64.0 + c2)); |
268 : | } | ||
269 : | |||
270 : | static void ssim_after(xvid_plg_data_t* data, ssim_data_t* ssim){ | ||
271 : | int i,j,c=0; | ||
272 : | int width,height,str,ovr; | ||
273 : | unsigned char * ptr1,*ptr2; | ||
274 : | float isum=0, min=1.00,max=0.00, val; | ||
275 : | int meanc, meano; | ||
276 : | int devc, devo, corr; | ||
277 : | |||
278 : | #define GRID 1 | ||
279 : | |||
280 : | width = data->width - 8; | ||
281 : | height = data->height - 8; | ||
282 : | str = data->original.stride[0]; | ||
283 : | if(str != data->current.stride[0]) printf("WARNING: Different strides in plugin_ssim original: %d current: %d\n",str,data->current.stride[0]); | ||
284 : | ovr = str - width + (width % GRID); | ||
285 : | |||
286 : | ptr1 = (unsigned char*) data->original.plane[0]; | ||
287 : | ptr2 = (unsigned char*) data->current.plane[0]; | ||
288 : | |||
289 : | |||
290 : | |||
291 : | /*TODO: Thread*/ | ||
292 : | for(i=0;i<height;i+=GRID){ | ||
293 : | /*begin of each row*/ | ||
294 : | meano = meanc = devc = devo = corr = 0; | ||
295 : | meano = ssim->func8x8(ptr1,str); | ||
296 : | meanc = ssim->func8x8(ptr2,str); | ||
297 : | ssim->consim(ptr1,ptr2,str,meano>>6,meanc>>6,&devo,&devc,&corr); | ||
298 : | Skal | 1.2 | emms(); |
299 : | |||
300 : | Skal | 1.1 | 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 : | Skal | 1.4 | for(j=GRID;j<width;j+=GRID){ |
314 : | Skal | 1.1 | /* 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 : | Skal | 1.2 | emms(); |
322 : | |||
323 : | Skal | 1.1 | val = calc_ssim(meano,meanc,devo,devc,corr); |
324 : | isum += val; | ||
325 : | c++; | ||
326 : | /* for visualisation | ||
327 : | if(ssim->param->b_visualize) | ||
328 : | ssim->errmap[i*width +j] = (uint8_t) 255*val; | ||
329 : | */ | ||
330 : | if(val < min) min = val; | ||
331 : | if(val > max) max = val; | ||
332 : | ptr1+=GRID; | ||
333 : | ptr2+=GRID; | ||
334 : | } | ||
335 : | ptr1 +=ovr; | ||
336 : | ptr2 +=ovr; | ||
337 : | } | ||
338 : | isum/=c; | ||
339 : | ssim->ssim_sum += isum; | ||
340 : | ssim->frame_cnt++; | ||
341 : | |||
342 : | if(ssim->param->stat_path != NULL) | ||
343 : | framestat_append(ssim,data->type,data->quant,min,max,isum); | ||
344 : | |||
345 : | /* for visualization | ||
346 : | if(ssim->param->b_visualize){ | ||
347 : | disp_gray(0,ssim->errmap,width,height,width, "Error-Map"); | ||
348 : | disp_gray(1,data->original.plane[0],data->width,data->height,data->original.stride[0],"Original"); | ||
349 : | disp_gray(2,data->current.plane[0],data->width,data->height,data->original.stride[0],"Compressed"); | ||
350 : | disp_sync(); | ||
351 : | } | ||
352 : | */ | ||
353 : | if(ssim->param->b_printstat){ | ||
354 : | Skal | 1.4 | printf(" SSIM: avg: %f min: %f max: %f\n",isum,min,max); |
355 : | Skal | 1.1 | } |
356 : | |||
357 : | } | ||
358 : | |||
359 : | static int ssim_create(xvid_plg_create_t* create, void** handle){ | ||
360 : | ssim_data_t* ssim; | ||
361 : | plg_ssim_param_t* param; | ||
362 : | int cpu_flags; | ||
363 : | param = (plg_ssim_param_t*) malloc(sizeof(plg_ssim_param_t)); | ||
364 : | *param = *((plg_ssim_param_t*) create->param); | ||
365 : | ssim = (ssim_data_t*) malloc(sizeof(ssim_data_t)); | ||
366 : | |||
367 : | cpu_flags = check_cpu_features(); | ||
368 : | |||
369 : | ssim->func8x8 = lum_8x8_c; | ||
370 : | ssim->func2x8 = lum_2x8_c; | ||
371 : | ssim->consim = iconsim_c; | ||
372 : | |||
373 : | ssim->param = param; | ||
374 : | |||
375 : | Skal | 1.2 | #if defined(ARCH_IS_IA32) |
376 : | Skal | 1.1 | if(cpu_flags & XVID_CPU_MMX){ |
377 : | ssim->func8x8 = lum_8x8_mmx; | ||
378 : | ssim->consim = consim_mmx; | ||
379 : | } | ||
380 : | if(cpu_flags & XVID_CPU_SSE2){ | ||
381 : | ssim->consim = consim_sse2; | ||
382 : | } | ||
383 : | Skal | 1.2 | #endif |
384 : | Skal | 1.1 | |
385 : | ssim->ssim_sum = 0.0; | ||
386 : | ssim->frame_cnt = 0; | ||
387 : | |||
388 : | /* for visualization | ||
389 : | if(param->b_visualize){ | ||
390 : | //error map | ||
391 : | ssim->errmap = (uint8_t*) malloc(sizeof(uint8_t)*(create->width-8)*(create->height-8)); | ||
392 : | } else { | ||
393 : | ssim->errmap = NULL; | ||
394 : | }; | ||
395 : | */ | ||
396 : | |||
397 : | /*stats*/ | ||
398 : | ssim->head=NULL; | ||
399 : | ssim->tail=NULL; | ||
400 : | |||
401 : | *(handle) = (void*) ssim; | ||
402 : | |||
403 : | return 0; | ||
404 : | } | ||
405 : | |||
406 : | int xvid_plugin_ssim(void * handle, int opt, void * param1, void * param2){ | ||
407 : | ssim_data_t* ssim; | ||
408 : | switch(opt){ | ||
409 : | case(XVID_PLG_INFO): | ||
410 : | ((xvid_plg_info_t*) param1)->flags = XVID_REQORIGINAL; | ||
411 : | break; | ||
412 : | case(XVID_PLG_CREATE): | ||
413 : | ssim_create((xvid_plg_create_t*) param1,(void**) param2); | ||
414 : | break; | ||
415 : | case(XVID_PLG_BEFORE): | ||
416 : | case(XVID_PLG_FRAME): | ||
417 : | break; | ||
418 : | case(XVID_PLG_AFTER): | ||
419 : | ssim_after((xvid_plg_data_t*) param1, (ssim_data_t*) handle); | ||
420 : | break; | ||
421 : | case(XVID_PLG_DESTROY): | ||
422 : | ssim = (ssim_data_t*) handle; | ||
423 : | printf("Average SSIM: %f\n",ssim->ssim_sum/ssim->frame_cnt); | ||
424 : | if(ssim->param->stat_path != NULL) | ||
425 : | framestat_write(ssim,ssim->param->stat_path); | ||
426 : | framestat_free(ssim->head); | ||
427 : | Skal | 1.4 | /*free(ssim->errmap);*/ |
428 : | Skal | 1.1 | free(ssim->param); |
429 : | free(ssim); | ||
430 : | break; | ||
431 : | default: | ||
432 : | break; | ||
433 : | } | ||
434 : | return 0; | ||
435 : | }; |
No admin address has been configured | ViewVC Help |
Powered by ViewVC 1.0.4 |