dump_fastssim.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. #include "vidinput.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <math.h>
  5. #if !defined(M_PI)
  6. # define M_PI (3.141592653589793238462643)
  7. #endif
  8. #include <string.h>
  9. /*Yes, yes, we're going to hell.*/
  10. #if defined(_WIN32)
  11. #include <io.h>
  12. #include <fcntl.h>
  13. #endif
  14. #include "getopt.h"
  15. const char *optstring = "cfrs";
  16. const struct option options[]={
  17. {"show-chroma",no_argument,NULL,'c'},
  18. {"frame-type",no_argument,NULL,'f'},
  19. {"raw",no_argument,NULL,'r'},
  20. {"summary",no_argument,NULL,'s'},
  21. {NULL,0,NULL,0}
  22. };
  23. static int show_frame_type;
  24. static int summary_only;
  25. static int show_chroma;
  26. typedef struct fs_level fs_level;
  27. typedef struct fs_ctx fs_ctx;
  28. #define SSIM_C1 (255*255*0.01*0.01)
  29. #define SSIM_C2 (255*255*0.03*0.03)
  30. #define FS_MINI(_a,_b) ((_a)<(_b)?(_a):(_b))
  31. #define FS_MAXI(_a,_b) ((_a)>(_b)?(_a):(_b))
  32. struct fs_level{
  33. uint16_t *im1;
  34. uint16_t *im2;
  35. double *ssim;
  36. int w;
  37. int h;
  38. };
  39. struct fs_ctx{
  40. fs_level *level;
  41. int nlevels;
  42. unsigned *col_buf;
  43. };
  44. static void fs_ctx_init(fs_ctx *_ctx,int _w,int _h,int _nlevels){
  45. unsigned char *data;
  46. size_t data_size;
  47. int lw;
  48. int lh;
  49. int l;
  50. lw = (_w + 1) >> 1;
  51. lh = (_h + 1) >> 1;
  52. data_size=_nlevels*sizeof(fs_level)+2*(lw+8)*8*sizeof(*_ctx->col_buf);
  53. for(l=0;l<_nlevels;l++){
  54. size_t im_size;
  55. size_t level_size;
  56. im_size=lw*(size_t)lh;
  57. level_size=2*im_size*sizeof(*_ctx->level[l].im1);
  58. level_size+=sizeof(*_ctx->level[l].ssim)-1;
  59. level_size/=sizeof(*_ctx->level[l].ssim);
  60. level_size+=im_size;
  61. level_size*=sizeof(*_ctx->level[l].ssim);
  62. data_size+=level_size;
  63. lw = (lw + 1) >> 1;
  64. lh = (lh + 1) >> 1;
  65. }
  66. data=(unsigned char *)malloc(data_size);
  67. _ctx->level=(fs_level *)data;
  68. _ctx->nlevels=_nlevels;
  69. data+=_nlevels*sizeof(*_ctx->level);
  70. lw = (_w + 1) >> 1;
  71. lh = (_h + 1) >> 1;
  72. for(l=0;l<_nlevels;l++){
  73. size_t im_size;
  74. size_t level_size;
  75. _ctx->level[l].w=lw;
  76. _ctx->level[l].h=lh;
  77. im_size=lw*(size_t)lh;
  78. level_size=2*im_size*sizeof(*_ctx->level[l].im1);
  79. level_size+=sizeof(*_ctx->level[l].ssim)-1;
  80. level_size/=sizeof(*_ctx->level[l].ssim);
  81. level_size*=sizeof(*_ctx->level[l].ssim);
  82. _ctx->level[l].im1=(uint16_t *)data;
  83. _ctx->level[l].im2=_ctx->level[l].im1+im_size;
  84. data+=level_size;
  85. _ctx->level[l].ssim=(double *)data;
  86. data+=im_size*sizeof(*_ctx->level[l].ssim);
  87. lw = (lw + 1) >> 1;
  88. lh = (lh + 1) >> 1;
  89. }
  90. _ctx->col_buf=(unsigned *)data;
  91. }
  92. static void fs_ctx_clear(fs_ctx *_ctx){
  93. free(_ctx->level);
  94. }
  95. static void fs_downsample_level(fs_ctx *_ctx,int _l){
  96. const uint16_t *src1;
  97. const uint16_t *src2;
  98. uint16_t *dst1;
  99. uint16_t *dst2;
  100. int w2;
  101. int h2;
  102. int w;
  103. int h;
  104. int i;
  105. int j;
  106. w=_ctx->level[_l].w;
  107. h=_ctx->level[_l].h;
  108. dst1=_ctx->level[_l].im1;
  109. dst2=_ctx->level[_l].im2;
  110. w2=_ctx->level[_l-1].w;
  111. h2=_ctx->level[_l-1].h;
  112. src1=_ctx->level[_l-1].im1;
  113. src2=_ctx->level[_l-1].im2;
  114. for(j=0;j<h;j++){
  115. int j0offs;
  116. int j1offs;
  117. j0offs=2*j*w2;
  118. j1offs=FS_MINI(2*j+1,h2)*w2;
  119. for(i=0;i<w;i++){
  120. int i0;
  121. int i1;
  122. i0=2*i;
  123. i1=FS_MINI(i0+1,w2);
  124. dst1[j*w+i]=src1[j0offs+i0]+src1[j0offs+i1]
  125. +src1[j1offs+i0]+src1[j1offs+i1];
  126. dst2[j*w+i]=src2[j0offs+i0]+src2[j0offs+i1]
  127. +src2[j1offs+i0]+src2[j1offs+i1];
  128. }
  129. }
  130. }
  131. static void fs_downsample_level0(fs_ctx *_ctx,const unsigned char *_src1,
  132. int _s1ystride,const unsigned char *_src2,int _s2ystride,int _w,int _h){
  133. uint16_t *dst1;
  134. uint16_t *dst2;
  135. int w;
  136. int h;
  137. int i;
  138. int j;
  139. w=_ctx->level[0].w;
  140. h=_ctx->level[0].h;
  141. dst1=_ctx->level[0].im1;
  142. dst2=_ctx->level[0].im2;
  143. for(j=0;j<h;j++){
  144. int j0;
  145. int j1;
  146. j0=2*j;
  147. j1=FS_MINI(j0+1,_h);
  148. for(i=0;i<w;i++){
  149. int i0;
  150. int i1;
  151. i0=2*i;
  152. i1=FS_MINI(i0+1,_w);
  153. dst1[j*w+i]=_src1[j0*_s1ystride+i0]+_src1[j0*_s1ystride+i1]
  154. +_src1[j1*_s1ystride+i0]+_src1[j1*_s1ystride+i1];
  155. dst2[j*w+i]=_src2[j0*_s2ystride+i0]+_src2[j0*_s2ystride+i1]
  156. +_src2[j1*_s2ystride+i0]+_src2[j1*_s2ystride+i1];
  157. }
  158. }
  159. }
  160. static void fs_apply_luminance(fs_ctx *_ctx,int _l){
  161. unsigned *col_sums_x;
  162. unsigned *col_sums_y;
  163. uint16_t *im1;
  164. uint16_t *im2;
  165. double *ssim;
  166. double c1;
  167. int w;
  168. int h;
  169. int j0offs;
  170. int j1offs;
  171. int i;
  172. int j;
  173. w=_ctx->level[_l].w;
  174. h=_ctx->level[_l].h;
  175. col_sums_x=_ctx->col_buf;
  176. col_sums_y=col_sums_x+w;
  177. im1=_ctx->level[_l].im1;
  178. im2=_ctx->level[_l].im2;
  179. for(i=0;i<w;i++)col_sums_x[i]=5*im1[i];
  180. for(i=0;i<w;i++)col_sums_y[i]=5*im2[i];
  181. for(j=1;j<4;j++){
  182. j1offs=FS_MINI(j,h-1)*w;
  183. for(i=0;i<w;i++)col_sums_x[i]+=im1[j1offs+i];
  184. for(i=0;i<w;i++)col_sums_y[i]+=im2[j1offs+i];
  185. }
  186. ssim=_ctx->level[_l].ssim;
  187. c1=(double)(SSIM_C1*4096*(1<<4*_l));
  188. for(j=0;j<h;j++){
  189. unsigned mux;
  190. unsigned muy;
  191. int i0;
  192. int i1;
  193. mux=5*col_sums_x[0];
  194. muy=5*col_sums_y[0];
  195. for(i=1;i<4;i++){
  196. i1=FS_MINI(i,w-1);
  197. mux+=col_sums_x[i1];
  198. muy+=col_sums_y[i1];
  199. }
  200. for(i=0;i<w;i++){
  201. ssim[j*w+i]*=(2*mux*(double)muy+c1)/(mux*(double)mux+muy*(double)muy+c1);
  202. if(i+1<w){
  203. i0=FS_MAXI(0,i-4);
  204. i1=FS_MINI(i+4,w-1);
  205. mux+=col_sums_x[i1]-col_sums_x[i0];
  206. muy+=col_sums_x[i1]-col_sums_x[i0];
  207. }
  208. }
  209. if(j+1<h){
  210. j0offs=FS_MAXI(0,j-4)*w;
  211. for(i=0;i<w;i++)col_sums_x[i]-=im1[j0offs+i];
  212. for(i=0;i<w;i++)col_sums_y[i]-=im2[j0offs+i];
  213. j1offs=FS_MINI(j+4,h-1)*w;
  214. for(i=0;i<w;i++)col_sums_x[i]+=im1[j1offs+i];
  215. for(i=0;i<w;i++)col_sums_y[i]+=im2[j1offs+i];
  216. }
  217. }
  218. }
  219. #define FS_COL_SET(_col,_joffs,_ioffs) \
  220. do{ \
  221. unsigned gx; \
  222. unsigned gy; \
  223. gx = gx_buf[((j + (_joffs)) & 7)*stride + i + (_ioffs)]; \
  224. gy = gy_buf[((j + (_joffs)) & 7)*stride + i + (_ioffs)]; \
  225. col_sums_gx2[(_col)]=gx*(double)gx; \
  226. col_sums_gy2[(_col)]=gy*(double)gy; \
  227. col_sums_gxgy[(_col)]=gx*(double)gy; \
  228. } \
  229. while(0)
  230. #define FS_COL_ADD(_col,_joffs,_ioffs) \
  231. do{ \
  232. unsigned gx; \
  233. unsigned gy; \
  234. gx = gx_buf[((j + (_joffs)) & 7)*stride + i + (_ioffs)]; \
  235. gy = gy_buf[((j + (_joffs)) & 7)*stride + i + (_ioffs)]; \
  236. col_sums_gx2[(_col)]+=gx*(double)gx; \
  237. col_sums_gy2[(_col)]+=gy*(double)gy; \
  238. col_sums_gxgy[(_col)]+=gx*(double)gy; \
  239. } \
  240. while(0)
  241. #define FS_COL_SUB(_col,_joffs,_ioffs) \
  242. do{ \
  243. unsigned gx; \
  244. unsigned gy; \
  245. gx = gx_buf[((j + (_joffs)) & 7)*stride + i + (_ioffs)]; \
  246. gy = gy_buf[((j + (_joffs)) & 7)*stride + i + (_ioffs)]; \
  247. col_sums_gx2[(_col)]-=gx*(double)gx; \
  248. col_sums_gy2[(_col)]-=gy*(double)gy; \
  249. col_sums_gxgy[(_col)]-=gx*(double)gy; \
  250. } \
  251. while(0)
  252. #define FS_COL_COPY(_col1,_col2) \
  253. do{ \
  254. col_sums_gx2[(_col1)]=col_sums_gx2[(_col2)]; \
  255. col_sums_gy2[(_col1)]=col_sums_gy2[(_col2)]; \
  256. col_sums_gxgy[(_col1)]=col_sums_gxgy[(_col2)]; \
  257. } \
  258. while(0)
  259. #define FS_COL_HALVE(_col1,_col2) \
  260. do{ \
  261. col_sums_gx2[(_col1)]=col_sums_gx2[(_col2)]*0.5; \
  262. col_sums_gy2[(_col1)]=col_sums_gy2[(_col2)]*0.5; \
  263. col_sums_gxgy[(_col1)]=col_sums_gxgy[(_col2)]*0.5; \
  264. } \
  265. while(0)
  266. #define FS_COL_DOUBLE(_col1,_col2) \
  267. do{ \
  268. col_sums_gx2[(_col1)]=col_sums_gx2[(_col2)]*2; \
  269. col_sums_gy2[(_col1)]=col_sums_gy2[(_col2)]*2; \
  270. col_sums_gxgy[(_col1)]=col_sums_gxgy[(_col2)]*2; \
  271. } \
  272. while(0)
  273. static void fs_calc_structure(fs_ctx *_ctx,int _l){
  274. uint16_t *im1;
  275. uint16_t *im2;
  276. unsigned *gx_buf;
  277. unsigned *gy_buf;
  278. double *ssim;
  279. double col_sums_gx2[8];
  280. double col_sums_gy2[8];
  281. double col_sums_gxgy[8];
  282. double c2;
  283. int stride;
  284. int w;
  285. int h;
  286. int i;
  287. int j;
  288. w=_ctx->level[_l].w;
  289. h=_ctx->level[_l].h;
  290. im1=_ctx->level[_l].im1;
  291. im2=_ctx->level[_l].im2;
  292. ssim=_ctx->level[_l].ssim;
  293. gx_buf=_ctx->col_buf;
  294. stride=w+8;
  295. gy_buf=gx_buf+8*stride;
  296. memset(gx_buf,0,2*8*stride*sizeof(*gx_buf));
  297. c2=SSIM_C2*(1<<4*_l)*16*104;
  298. for(j=0;j<h+4;j++){
  299. if(j<h-1){
  300. for(i=0;i<w-1;i++){
  301. unsigned g1;
  302. unsigned g2;
  303. unsigned gx;
  304. unsigned gy;
  305. g1=abs(im1[(j+1)*w+i+1]-im1[j*w+i]);
  306. g2=abs(im1[(j+1)*w+i]-im1[j*w+i+1]);
  307. gx=4*FS_MAXI(g1,g2)+FS_MINI(g1,g2);
  308. g1=abs(im2[(j+1)*w+i+1]-im2[j*w+i]);
  309. g2=abs(im2[(j+1)*w+i]-im2[j*w+i+1]);
  310. gy=4*FS_MAXI(g1,g2)+FS_MINI(g1,g2);
  311. gx_buf[(j&7)*stride+i+4]=gx;
  312. gy_buf[(j&7)*stride+i+4]=gy;
  313. }
  314. }
  315. else{
  316. memset(gx_buf+(j&7)*stride,0,stride*sizeof(*gx_buf));
  317. memset(gy_buf+(j&7)*stride,0,stride*sizeof(*gy_buf));
  318. }
  319. if(j>=4){
  320. int k;
  321. col_sums_gx2[3]=col_sums_gx2[2]=col_sums_gx2[1]=col_sums_gx2[0]=0;
  322. col_sums_gy2[3]=col_sums_gy2[2]=col_sums_gy2[1]=col_sums_gy2[0]=0;
  323. col_sums_gxgy[3]=col_sums_gxgy[2]=col_sums_gxgy[1]=col_sums_gxgy[0]=0;
  324. for(i=4;i<8;i++){
  325. FS_COL_SET(i,-1,0);
  326. FS_COL_ADD(i,0,0);
  327. for(k=1;k<8-i;k++){
  328. FS_COL_DOUBLE(i,i);
  329. FS_COL_ADD(i,-k-1,0);
  330. FS_COL_ADD(i,k,0);
  331. }
  332. }
  333. for(i=0;i<w;i++){
  334. double mugx2;
  335. double mugy2;
  336. double mugxgy;
  337. mugx2=col_sums_gx2[0];
  338. for(k=1;k<8;k++)mugx2+=col_sums_gx2[k];
  339. mugy2=col_sums_gy2[0];
  340. for(k=1;k<8;k++)mugy2+=col_sums_gy2[k];
  341. mugxgy=col_sums_gxgy[0];
  342. for(k=1;k<8;k++)mugxgy+=col_sums_gxgy[k];
  343. ssim[(j-4)*w+i]=(2*mugxgy+c2)/(mugx2+mugy2+c2);
  344. if(i+1<w){
  345. FS_COL_SET(0,-1,1);
  346. FS_COL_ADD(0,0,1);
  347. FS_COL_SUB(2,-3,2);
  348. FS_COL_SUB(2,2,2);
  349. FS_COL_HALVE(1,2);
  350. FS_COL_SUB(3,-4,3);
  351. FS_COL_SUB(3,3,3);
  352. FS_COL_HALVE(2,3);
  353. FS_COL_COPY(3,4);
  354. FS_COL_DOUBLE(4,5);
  355. FS_COL_ADD(4,-4,5);
  356. FS_COL_ADD(4,3,5);
  357. FS_COL_DOUBLE(5,6);
  358. FS_COL_ADD(5,-3,6);
  359. FS_COL_ADD(5,2,6);
  360. FS_COL_DOUBLE(6,7);
  361. FS_COL_ADD(6,-2,7);
  362. FS_COL_ADD(6,1,7);
  363. FS_COL_SET(7,-1,8);
  364. FS_COL_ADD(7,0,8);
  365. }
  366. }
  367. }
  368. }
  369. }
  370. #define FS_NLEVELS (4)
  371. /*These weights were derived from the default weights found in Wang's original
  372. Matlab implementation: {0.0448, 0.2856, 0.2363, 0.1333}.
  373. We drop the finest scale and renormalize the rest to sum to 1.*/
  374. static const double FS_WEIGHTS[FS_NLEVELS]={
  375. 0.2989654541015625,0.3141326904296875,0.2473602294921875,0.1395416259765625
  376. };
  377. static double fs_average(fs_ctx *_ctx,int _l){
  378. double *ssim;
  379. double ret;
  380. int w;
  381. int h;
  382. int i;
  383. int j;
  384. w=_ctx->level[_l].w;
  385. h=_ctx->level[_l].h;
  386. ssim=_ctx->level[_l].ssim;
  387. ret=0;
  388. for(j=0;j<h;j++)for(i=0;i<w;i++)ret+=ssim[j*w+i];
  389. return pow(ret/(w*h),FS_WEIGHTS[_l]);
  390. }
  391. double calc_ssim(const unsigned char *_src,int _systride,
  392. const unsigned char *_dst,int _dystride,int _w,int _h){
  393. fs_ctx ctx;
  394. double ret;
  395. int l;
  396. ret=1;
  397. fs_ctx_init(&ctx,_w,_h,FS_NLEVELS);
  398. fs_downsample_level0(&ctx,_src,_systride,_dst,_dystride,_w,_h);
  399. for(l=0;l<FS_NLEVELS-1;l++){
  400. fs_calc_structure(&ctx,l);
  401. ret*=fs_average(&ctx,l);
  402. fs_downsample_level(&ctx,l+1);
  403. }
  404. fs_calc_structure(&ctx,l);
  405. fs_apply_luminance(&ctx,l);
  406. ret*=fs_average(&ctx,l);
  407. fs_ctx_clear(&ctx);
  408. return ret;
  409. }
  410. static void usage(char *_argv[]){
  411. fprintf(stderr,"Usage: %s [options] <video1> <video2>\n"
  412. " <video1> and <video2> must be YUV4MPEG files.\n\n"
  413. " Options:\n\n"
  414. " -c --show-chroma Also show values for the chroma channels.\n"
  415. " -f --frame-type Show frame type and QI value for each Theora frame.\n"
  416. " -r --raw Show raw SSIM scores, instead of"
  417. " 10*log10(1/(1-ssim)).\n"
  418. " -s --summary Only output the summary line.\n",_argv[0]);
  419. }
  420. typedef double (*convert_ssim_func)(double _ssim,double _weight);
  421. static double convert_ssim_raw(double _ssim,double _weight){
  422. return _ssim/_weight;
  423. }
  424. static double convert_ssim_db(double _ssim,double _weight){
  425. return 10*(log10(_weight)-log10(_weight-_ssim));
  426. }
  427. int main(int _argc,char *_argv[]){
  428. video_input vid1;
  429. video_input_info info1;
  430. video_input vid2;
  431. video_input_info info2;
  432. convert_ssim_func convert;
  433. double gssim[3];
  434. double cweight;
  435. int frameno;
  436. FILE *fin;
  437. int long_option_index;
  438. int c;
  439. #ifdef _WIN32
  440. /*We need to set stdin/stdout to binary mode on windows.
  441. Beware the evil ifdef.
  442. We avoid these where we can, but this one we cannot.
  443. Don't add any more, you'll probably go to hell if you do.*/
  444. _setmode(_fileno(stdin),_O_BINARY);
  445. #endif
  446. /*Process option arguments.*/
  447. convert=convert_ssim_db;
  448. while((c=getopt_long(_argc,_argv,optstring,options,&long_option_index))!=EOF){
  449. switch(c){
  450. case 'f':show_frame_type=1;break;
  451. case 'r':convert=convert_ssim_raw;break;
  452. case 's':summary_only=1;break;
  453. case 'c':show_chroma=1;break;
  454. default:{
  455. usage(_argv);
  456. exit(EXIT_FAILURE);
  457. }break;
  458. }
  459. }
  460. if(optind+2!=_argc){
  461. usage(_argv);
  462. exit(EXIT_FAILURE);
  463. }
  464. fin=strcmp(_argv[optind],"-")==0?stdin:fopen(_argv[optind],"rb");
  465. if(fin==NULL){
  466. fprintf(stderr,"Unable to open '%s' for extraction.\n",_argv[optind]);
  467. exit(EXIT_FAILURE);
  468. }
  469. fprintf(stderr,"Opening %s...\n",_argv[optind]);
  470. if(video_input_open(&vid1,fin)<0)exit(EXIT_FAILURE);
  471. video_input_get_info(&vid1,&info1);
  472. fin=strcmp(_argv[optind+1],"-")==0?stdin:fopen(_argv[optind+1],"rb");
  473. if(fin==NULL){
  474. fprintf(stderr,"Unable to open '%s' for extraction.\n",_argv[optind+1]);
  475. exit(EXIT_FAILURE);
  476. }
  477. fprintf(stderr,"Opening %s...\n",_argv[optind+1]);
  478. if(video_input_open(&vid2,fin)<0)exit(EXIT_FAILURE);
  479. video_input_get_info(&vid2,&info2);
  480. /*Check to make sure these videos are compatible.*/
  481. if(info1.pic_w!=info2.pic_w||info1.pic_h!=info2.pic_h){
  482. fprintf(stderr,"Video resolution does not match.\n");
  483. exit(EXIT_FAILURE);
  484. }
  485. if(info1.pixel_fmt!=info2.pixel_fmt){
  486. fprintf(stderr,"Pixel formats do not match.\n");
  487. exit(EXIT_FAILURE);
  488. }
  489. if((info1.pic_x&!(info1.pixel_fmt&1))!=(info2.pic_x&!(info2.pixel_fmt&1))||
  490. (info1.pic_y&!(info1.pixel_fmt&2))!=(info2.pic_y&!(info2.pixel_fmt&2))){
  491. fprintf(stderr,"Chroma subsampling offsets do not match.\n");
  492. exit(EXIT_FAILURE);
  493. }
  494. if(info1.fps_n*(int64_t)info2.fps_d!=
  495. info2.fps_n*(int64_t)info1.fps_d){
  496. fprintf(stderr,"Warning: framerates do not match.\n");
  497. }
  498. if(info1.par_n*(int64_t)info2.par_d!=
  499. info2.par_n*(int64_t)info1.par_d){
  500. fprintf(stderr,"Warning: aspect ratios do not match.\n");
  501. }
  502. gssim[0]=gssim[1]=gssim[2]=0;
  503. /*We just use a simple weighting to get a single full-color score.
  504. In reality the CSF for chroma is not the same as luma.*/
  505. cweight = 0.25*(4 >> (!(info1.pixel_fmt & 1) + !(info1.pixel_fmt & 2)));
  506. for(frameno=0;;frameno++){
  507. video_input_ycbcr f1;
  508. video_input_ycbcr f2;
  509. double ssim[3];
  510. char tag1[5];
  511. char tag2[5];
  512. int ret1;
  513. int ret2;
  514. int pli;
  515. int nplanes;
  516. ret1=video_input_fetch_frame(&vid1,f1,tag1);
  517. ret2=video_input_fetch_frame(&vid2,f2,tag2);
  518. if(ret1==0&&ret2==0)break;
  519. else if(ret1<0||ret2<0)break;
  520. else if(ret1==0){
  521. fprintf(stderr,"%s ended before %s.\n",
  522. _argv[optind],_argv[optind+1]);
  523. break;
  524. }
  525. else if(ret2==0){
  526. fprintf(stderr,"%s ended before %s.\n",
  527. _argv[optind+1],_argv[optind]);
  528. break;
  529. }
  530. /*Okay, we got one frame from each.*/
  531. nplanes = show_chroma ? 3 : 1;
  532. for(pli=0;pli<nplanes;pli++){
  533. int xdec;
  534. int ydec;
  535. xdec=pli&&!(info1.pixel_fmt&1);
  536. ydec=pli&&!(info1.pixel_fmt&2);
  537. ssim[pli]=calc_ssim(
  538. f1[pli].data+(info1.pic_y>>ydec)*f1[pli].stride+(info1.pic_x>>xdec),
  539. f1[pli].stride,
  540. f2[pli].data+(info2.pic_y>>ydec)*f2[pli].stride+(info2.pic_x>>xdec),
  541. f2[pli].stride,
  542. ((info1.pic_x + info1.pic_w + xdec) >> xdec) - (info1.pic_x >> xdec),
  543. ((info1.pic_y + info1.pic_h + ydec) >> ydec) - (info1.pic_y >> ydec));
  544. gssim[pli]+=ssim[pli];
  545. }
  546. if(!summary_only){
  547. if(show_frame_type)printf("%s%s",tag1,tag2);
  548. if(show_chroma){
  549. printf("%08i: %-8G (Y': %-8G Cb: %-8G Cr: %-8G)\n",frameno,
  550. convert(ssim[0]+cweight*(ssim[1]+ssim[2]),1+2*cweight),
  551. convert(ssim[0],1),convert(ssim[1],1),convert(ssim[2],1));
  552. }
  553. else printf("%08i: %-8G\n",frameno,convert(ssim[0],1));
  554. }
  555. }
  556. if(show_chroma){
  557. printf("Total: %-8G (Y': %-8G Cb: %-8G Cr: %-8G)\n",
  558. convert(gssim[0]+cweight*(gssim[1]+gssim[2]),(1+2*cweight)*frameno),
  559. convert(gssim[0],frameno),convert(gssim[1],frameno),
  560. convert(gssim[2],frameno));
  561. }
  562. else printf("Total: %-8G\n",convert(gssim[0],frameno));
  563. video_input_close(&vid1);
  564. video_input_close(&vid2);
  565. return EXIT_SUCCESS;
  566. }