Parent Directory | Revision Log
Revision 1.5 - (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 : | Skal | 1.5 | int grid; |
66 : | |||
67 : | Skal | 1.1 | /*for average SSIM*/ |
68 : | float ssim_sum; | ||
69 : | int frame_cnt; | ||
70 : | |||
71 : | /*function pointers*/ | ||
72 : | lumfunc func8x8; | ||
73 : | lumfunc func2x8; | ||
74 : | csfunc consim; | ||
75 : | |||
76 : | /*stats - for debugging*/ | ||
77 : | framestat_t* head; | ||
78 : | framestat_t* tail; | ||
79 : | } ssim_data_t; | ||
80 : | |||
81 : | /* append the stats for another frame to the linked list*/ | ||
82 : | void framestat_append(ssim_data_t* ssim,int type, int quant, float min, float max, float avg){ | ||
83 : | framestat_t* act; | ||
84 : | act = (framestat_t*) malloc(sizeof(framestat_t)); | ||
85 : | act->type = type; | ||
86 : | act->quant = quant; | ||
87 : | act->ssim_min = min; | ||
88 : | act->ssim_max = max; | ||
89 : | act->ssim_avg = avg; | ||
90 : | act->next = NULL; | ||
91 : | |||
92 : | if(ssim->head == NULL){ | ||
93 : | ssim->head = act; | ||
94 : | ssim->tail = act; | ||
95 : | } else { | ||
96 : | ssim->tail->next = act; | ||
97 : | ssim->tail = act; | ||
98 : | } | ||
99 : | } | ||
100 : | |||
101 : | /* destroy the whole list*/ | ||
102 : | void framestat_free(framestat_t* stat){ | ||
103 : | if(stat != NULL){ | ||
104 : | if(stat->next != NULL) framestat_free(stat->next); | ||
105 : | free(stat); | ||
106 : | } | ||
107 : | return; | ||
108 : | } | ||
109 : | |||
110 : | /*writeout the collected stats*/ | ||
111 : | void framestat_write(ssim_data_t* ssim, char* path){ | ||
112 : | Isibaar | 1.3 | framestat_t* tmp = ssim->head; |
113 : | Skal | 1.1 | FILE* out = fopen(path,"w"); |
114 : | if(out==NULL) printf("Cannot open %s in plugin_ssim\n",path); | ||
115 : | |||
116 : | fprintf(out,"SSIM Error Metric\n"); | ||
117 : | Skal | 1.4 | fprintf(out,"quant avg min max\n"); |
118 : | Skal | 1.1 | while(tmp->next->next != NULL){ |
119 : | fprintf(out,"%3d %1.4f %1.4f %1.4f\n",tmp->quant,tmp->ssim_avg,tmp->ssim_min,tmp->ssim_max); | ||
120 : | tmp = tmp->next; | ||
121 : | } | ||
122 : | fclose(out); | ||
123 : | } | ||
124 : | |||
125 : | /*writeout the collected stats in octave readable format*/ | ||
126 : | void framestat_write_oct(ssim_data_t* ssim, char* path){ | ||
127 : | Isibaar | 1.3 | framestat_t* tmp; |
128 : | Skal | 1.1 | FILE* out = fopen(path,"w"); |
129 : | if(out==NULL) printf("Cannot open %s in plugin_ssim\n",path); | ||
130 : | |||
131 : | fprintf(out,"quant = ["); | ||
132 : | tmp = ssim->head; | ||
133 : | while(tmp->next->next != NULL){ | ||
134 : | fprintf(out,"%d, ",tmp->quant); | ||
135 : | tmp = tmp->next; | ||
136 : | } | ||
137 : | fprintf(out,"%d];\n\n",tmp->quant); | ||
138 : | |||
139 : | fprintf(out,"ssim_min = ["); | ||
140 : | tmp = ssim->head; | ||
141 : | while(tmp->next->next != NULL){ | ||
142 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
143 : | tmp = tmp->next; | ||
144 : | } | ||
145 : | fprintf(out,"%f];\n\n",tmp->ssim_min); | ||
146 : | |||
147 : | fprintf(out,"ssim_max = ["); | ||
148 : | tmp = ssim->head; | ||
149 : | while(tmp->next->next != NULL){ | ||
150 : | fprintf(out,"%f, ",tmp->ssim_max); | ||
151 : | tmp = tmp->next; | ||
152 : | } | ||
153 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
154 : | |||
155 : | fprintf(out,"ssim_avg = ["); | ||
156 : | tmp = ssim->head; | ||
157 : | while(tmp->next->next != NULL){ | ||
158 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
159 : | tmp = tmp->next; | ||
160 : | } | ||
161 : | fprintf(out,"%f];\n\n",tmp->ssim_avg); | ||
162 : | |||
163 : | fprintf(out,"ivop = ["); | ||
164 : | tmp = ssim->head; | ||
165 : | while(tmp->next->next != NULL){ | ||
166 : | if(tmp->type == XVID_TYPE_IVOP){ | ||
167 : | fprintf(out,"%d, ",tmp->quant); | ||
168 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
169 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
170 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
171 : | } | ||
172 : | tmp = tmp->next; | ||
173 : | } | ||
174 : | fprintf(out,"%d, ",tmp->quant); | ||
175 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
176 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
177 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
178 : | |||
179 : | fprintf(out,"pvop = ["); | ||
180 : | tmp = ssim->head; | ||
181 : | while(tmp->next->next != NULL){ | ||
182 : | if(tmp->type == XVID_TYPE_PVOP){ | ||
183 : | fprintf(out,"%d, ",tmp->quant); | ||
184 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
185 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
186 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
187 : | } | ||
188 : | tmp = tmp->next; | ||
189 : | } | ||
190 : | fprintf(out,"%d, ",tmp->quant); | ||
191 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
192 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
193 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
194 : | |||
195 : | fprintf(out,"bvop = ["); | ||
196 : | tmp = ssim->head; | ||
197 : | while(tmp->next->next != NULL){ | ||
198 : | if(tmp->type == XVID_TYPE_BVOP){ | ||
199 : | fprintf(out,"%d, ",tmp->quant); | ||
200 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
201 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
202 : | fprintf(out,"%f; ",tmp->ssim_max); | ||
203 : | } | ||
204 : | tmp = tmp->next; | ||
205 : | } | ||
206 : | fprintf(out,"%d, ",tmp->quant); | ||
207 : | fprintf(out,"%f, ",tmp->ssim_avg); | ||
208 : | fprintf(out,"%f, ",tmp->ssim_min); | ||
209 : | fprintf(out,"%f];\n\n",tmp->ssim_max); | ||
210 : | |||
211 : | fclose(out); | ||
212 : | } | ||
213 : | |||
214 : | /*calculate the luminance of a 8x8 block*/ | ||
215 : | int lum_8x8_c(uint8_t* ptr, int stride){ | ||
216 : | int mean=0,i,j; | ||
217 : | for(i=0;i< 8;i++) | ||
218 : | for(j=0;j< 8;j++){ | ||
219 : | mean += ptr[i*stride + j]; | ||
220 : | } | ||
221 : | return mean; | ||
222 : | } | ||
223 : | |||
224 : | /*calculate the difference between two blocks next to each other on a row*/ | ||
225 : | int lum_2x8_c(uint8_t* ptr, int stride){ | ||
226 : | int mean=0,i; | ||
227 : | /*Luminance*/ | ||
228 : | for(i=0;i< 8;i++){ | ||
229 : | mean -= *(ptr-1); | ||
230 : | mean += *(ptr+ 8 - 1); | ||
231 : | ptr+=stride; | ||
232 : | } | ||
233 : | return mean; | ||
234 : | } | ||
235 : | |||
236 : | /*calculate contrast and correlation of the two blocks*/ | ||
237 : | Skal | 1.5 | void consim_c(uint8_t* ptro, uint8_t* ptrc, int stride, int lumo, int lumc, int* pdevo, int* pdevc, int* pcorr){ |
238 : | unsigned int valo, valc, devo=0, devc=0, corr=0,i,j,str; | ||
239 : | str = stride - 8; | ||
240 : | Skal | 1.1 | for(i=0;i< 8;i++){ |
241 : | for(j=0;j< 8;j++){ | ||
242 : | Skal | 1.5 | valo = *ptro; |
243 : | valc = *ptrc; | ||
244 : | Skal | 1.1 | devo += valo*valo; |
245 : | Skal | 1.5 | devc += valc*valc; |
246 : | corr += valo*valc; | ||
247 : | Skal | 1.1 | ptro++; |
248 : | ptrc++; | ||
249 : | } | ||
250 : | Skal | 1.5 | ptro += str; |
251 : | ptrc += str; | ||
252 : | Skal | 1.1 | } |
253 : | Skal | 1.5 | |
254 : | *pdevo = devo - ((lumo*lumo + 32) >> 6); | ||
255 : | *pdevc = devc - ((lumc*lumc + 32) >> 6); | ||
256 : | *pcorr = corr - ((lumo*lumc + 32) >> 6); | ||
257 : | Skal | 1.1 | }; |
258 : | |||
259 : | /*calculate the final ssim value*/ | ||
260 : | static float calc_ssim(int meano, int meanc, int devo, int devc, int corr){ | ||
261 : | static const float c1 = (0.01*255)*(0.01*255); | ||
262 : | static const float c2 = (0.03*255)*(0.03*255); | ||
263 : | float fmeano,fmeanc,fdevo,fdevc,fcorr; | ||
264 : | fmeanc = (float) meanc; | ||
265 : | fmeano = (float) meano; | ||
266 : | fdevo = (float) devo; | ||
267 : | fdevc = (float) devc; | ||
268 : | fcorr = (float) corr; | ||
269 : | Skal | 1.4 | /* printf("meano: %f meanc: %f devo: %f devc: %f corr: %f\n",fmeano,fmeanc,fdevo,fdevc,fcorr); */ |
270 : | 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)); |
271 : | } | ||
272 : | |||
273 : | static void ssim_after(xvid_plg_data_t* data, ssim_data_t* ssim){ | ||
274 : | int i,j,c=0; | ||
275 : | int width,height,str,ovr; | ||
276 : | unsigned char * ptr1,*ptr2; | ||
277 : | float isum=0, min=1.00,max=0.00, val; | ||
278 : | int meanc, meano; | ||
279 : | int devc, devo, corr; | ||
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 : | Skal | 1.5 | ovr = str - width + (width % ssim->grid); |
286 : | Skal | 1.1 | |
287 : | ptr1 = (unsigned char*) data->original.plane[0]; | ||
288 : | ptr2 = (unsigned char*) data->current.plane[0]; | ||
289 : | |||
290 : | |||
291 : | |||
292 : | /*TODO: Thread*/ | ||
293 : | Skal | 1.5 | for(i=0;i<height;i+=ssim->grid){ |
294 : | Skal | 1.1 | /*begin of each row*/ |
295 : | meano = meanc = devc = devo = corr = 0; | ||
296 : | meano = ssim->func8x8(ptr1,str); | ||
297 : | meanc = ssim->func8x8(ptr2,str); | ||
298 : | Skal | 1.5 | ssim->consim(ptr1,ptr2,str,meano,meanc,&devo,&devc,&corr); |
299 : | Skal | 1.2 | emms(); |
300 : | |||
301 : | Skal | 1.1 | val = calc_ssim(meano,meanc,devo,devc,corr); |
302 : | isum += val; | ||
303 : | c++; | ||
304 : | /* for visualisation | ||
305 : | if(ssim->param->b_visualize) | ||
306 : | ssim->errmap[i*width] = (uint8_t) 127*val; | ||
307 : | */ | ||
308 : | |||
309 : | Skal | 1.5 | |
310 : | Skal | 1.1 | if(val < min) min = val; |
311 : | if(val > max) max = val; | ||
312 : | Skal | 1.5 | ptr1+=ssim->grid; |
313 : | ptr2+=ssim->grid; | ||
314 : | Skal | 1.1 | /*rest of each row*/ |
315 : | Skal | 1.5 | for(j=ssim->grid;j<width;j+=ssim->grid){ |
316 : | if(ssim->grid == 1){ | ||
317 : | Skal | 1.1 | meano += ssim->func2x8(ptr1,str); |
318 : | meanc += ssim->func2x8(ptr2,str); | ||
319 : | Skal | 1.5 | } else { |
320 : | Skal | 1.1 | meano = ssim->func8x8(ptr1,str); |
321 : | meanc = ssim->func8x8(ptr2,str); | ||
322 : | Skal | 1.5 | } |
323 : | ssim->consim(ptr1,ptr2,str,meano,meanc,&devo,&devc,&corr); | ||
324 : | Skal | 1.2 | emms(); |
325 : | |||
326 : | Skal | 1.1 | val = calc_ssim(meano,meanc,devo,devc,corr); |
327 : | isum += val; | ||
328 : | c++; | ||
329 : | /* for visualisation | ||
330 : | if(ssim->param->b_visualize) | ||
331 : | ssim->errmap[i*width +j] = (uint8_t) 255*val; | ||
332 : | */ | ||
333 : | if(val < min) min = val; | ||
334 : | if(val > max) max = val; | ||
335 : | Skal | 1.5 | ptr1+=ssim->grid; |
336 : | ptr2+=ssim->grid; | ||
337 : | Skal | 1.1 | } |
338 : | ptr1 +=ovr; | ||
339 : | ptr2 +=ovr; | ||
340 : | } | ||
341 : | isum/=c; | ||
342 : | ssim->ssim_sum += isum; | ||
343 : | ssim->frame_cnt++; | ||
344 : | |||
345 : | if(ssim->param->stat_path != NULL) | ||
346 : | framestat_append(ssim,data->type,data->quant,min,max,isum); | ||
347 : | |||
348 : | /* for visualization | ||
349 : | if(ssim->param->b_visualize){ | ||
350 : | disp_gray(0,ssim->errmap,width,height,width, "Error-Map"); | ||
351 : | disp_gray(1,data->original.plane[0],data->width,data->height,data->original.stride[0],"Original"); | ||
352 : | disp_gray(2,data->current.plane[0],data->width,data->height,data->original.stride[0],"Compressed"); | ||
353 : | disp_sync(); | ||
354 : | } | ||
355 : | */ | ||
356 : | if(ssim->param->b_printstat){ | ||
357 : | Skal | 1.4 | printf(" SSIM: avg: %f min: %f max: %f\n",isum,min,max); |
358 : | Skal | 1.1 | } |
359 : | |||
360 : | } | ||
361 : | |||
362 : | static int ssim_create(xvid_plg_create_t* create, void** handle){ | ||
363 : | ssim_data_t* ssim; | ||
364 : | plg_ssim_param_t* param; | ||
365 : | int cpu_flags; | ||
366 : | param = (plg_ssim_param_t*) malloc(sizeof(plg_ssim_param_t)); | ||
367 : | *param = *((plg_ssim_param_t*) create->param); | ||
368 : | ssim = (ssim_data_t*) malloc(sizeof(ssim_data_t)); | ||
369 : | |||
370 : | cpu_flags = check_cpu_features(); | ||
371 : | |||
372 : | ssim->func8x8 = lum_8x8_c; | ||
373 : | ssim->func2x8 = lum_2x8_c; | ||
374 : | Skal | 1.5 | ssim->consim = consim_c; |
375 : | Skal | 1.1 | |
376 : | ssim->param = param; | ||
377 : | |||
378 : | Skal | 1.5 | ssim->grid = param->acc; |
379 : | |||
380 : | /*gaussian weigthing not implemented*/ | ||
381 : | if(ssim->grid == 0) ssim->grid = 1; | ||
382 : | if(ssim->grid > 4) ssim->grid = 4; | ||
383 : | |||
384 : | printf("Grid: %d\n",ssim->grid); | ||
385 : | |||
386 : | Skal | 1.2 | #if defined(ARCH_IS_IA32) |
387 : | Skal | 1.5 | if((cpu_flags & XVID_CPU_MMX) && (param->acc > 0)){ |
388 : | Skal | 1.1 | ssim->func8x8 = lum_8x8_mmx; |
389 : | ssim->consim = consim_mmx; | ||
390 : | } | ||
391 : | Skal | 1.5 | if((cpu_flags & XVID_CPU_SSE2) && (param->acc > 0)){ |
392 : | Skal | 1.1 | ssim->consim = consim_sse2; |
393 : | } | ||
394 : | Skal | 1.2 | #endif |
395 : | Skal | 1.1 | |
396 : | ssim->ssim_sum = 0.0; | ||
397 : | ssim->frame_cnt = 0; | ||
398 : | |||
399 : | /* for visualization | ||
400 : | if(param->b_visualize){ | ||
401 : | //error map | ||
402 : | ssim->errmap = (uint8_t*) malloc(sizeof(uint8_t)*(create->width-8)*(create->height-8)); | ||
403 : | } else { | ||
404 : | ssim->errmap = NULL; | ||
405 : | }; | ||
406 : | */ | ||
407 : | |||
408 : | /*stats*/ | ||
409 : | ssim->head=NULL; | ||
410 : | ssim->tail=NULL; | ||
411 : | |||
412 : | *(handle) = (void*) ssim; | ||
413 : | |||
414 : | return 0; | ||
415 : | } | ||
416 : | |||
417 : | int xvid_plugin_ssim(void * handle, int opt, void * param1, void * param2){ | ||
418 : | ssim_data_t* ssim; | ||
419 : | switch(opt){ | ||
420 : | case(XVID_PLG_INFO): | ||
421 : | ((xvid_plg_info_t*) param1)->flags = XVID_REQORIGINAL; | ||
422 : | break; | ||
423 : | case(XVID_PLG_CREATE): | ||
424 : | ssim_create((xvid_plg_create_t*) param1,(void**) param2); | ||
425 : | break; | ||
426 : | case(XVID_PLG_BEFORE): | ||
427 : | case(XVID_PLG_FRAME): | ||
428 : | break; | ||
429 : | case(XVID_PLG_AFTER): | ||
430 : | ssim_after((xvid_plg_data_t*) param1, (ssim_data_t*) handle); | ||
431 : | break; | ||
432 : | case(XVID_PLG_DESTROY): | ||
433 : | ssim = (ssim_data_t*) handle; | ||
434 : | printf("Average SSIM: %f\n",ssim->ssim_sum/ssim->frame_cnt); | ||
435 : | if(ssim->param->stat_path != NULL) | ||
436 : | framestat_write(ssim,ssim->param->stat_path); | ||
437 : | framestat_free(ssim->head); | ||
438 : | Skal | 1.4 | /*free(ssim->errmap);*/ |
439 : | Skal | 1.1 | free(ssim->param); |
440 : | free(ssim); | ||
441 : | break; | ||
442 : | default: | ||
443 : | break; | ||
444 : | } | ||
445 : | return 0; | ||
446 : | }; |
No admin address has been configured | ViewVC Help |
Powered by ViewVC 1.0.4 |