最近在做视频质量评估相关的东西,其中涉及了一个离线的PSNR计算,练个手写了个YUV视频的PSNR计算程序。
对于图像PSNR计算公式为:
对于视频,就是将每一帧的失真累加,然后在计算PSNR:
下面来看代码实现,其中需要注意溢出问题。视频的分辨率可能很大,导致计算得到的SSD很大,在后边的log计算中就会出现问题,因此先对SSD除以MN(视频分辨率),可以放置溢出。
main函数main.cpp:
#include "PSNR.h"
#define _CRT_SECURE_NO_WARNINGS
int main(int argc, char *argv[])
{
if( argc != 5 )
{
printf ("The format of input parameter is error!\n");
exit(-1);
}
FILE * org_yuv;
FILE * out_yuv;
if(NULL == (org_yuv = fopen(argv[1], "rb")))
{
printf("File input is can't open!\n");
return -1;
}
if(NULL == (out_yuv = fopen(argv[2], "rb")))
{
printf("File output is can't open!\n");
return -1;
}
int IMAGEWIDTH = 1024;
int IMAGEHEIGHT = 768;
IMAGEWIDTH = atoi(argv[3]);
IMAGEHEIGHT = atoi(argv[4]);
int readsize;
unsigned char *org_buff;
org_buff = (unsigned char *)malloc(IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
memset (org_buff, 0, IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
unsigned char *out_buff;
out_buff=(unsigned char *)malloc(IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
memset (out_buff, 0, IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
int Frame =24;
int i = Frame;
double videoSSD[3] = {0};
double videoPSNR[3] = {0};
while(i)
{
readsize=fread(org_buff, 1 , IMAGEWIDTH*IMAGEHEIGHT*3/2, org_yuv);
readsize=fread(out_buff, 1 , IMAGEWIDTH*IMAGEHEIGHT*3/2, out_yuv);
if(readsize3/2)
break;
printf("Frame %d\n",Frame-i);
calPSNR(org_buff, out_buff, IMAGEWIDTH,IMAGEHEIGHT,videoSSD);
i--;
}
for(int ch=0;ch<3;ch++)
{
videoPSNR[ch] = ( videoSSD[ch] ? 10.0 * log10((255 * 255) *Frame/ videoSSD[ch] ) : 999.99 );
}
printf("\n\nSUMMARY --------------------------------------------------------\n");
printf("Y-PSNR : %.4f, U-PSNR : %.4f, V-PSNR : %.4f\n",videoPSNR[0],videoPSNR[1],videoPSNR[2]);
free(org_buff);
org_buff=NULL;
free(out_buff);
out_buff=NULL;
fclose(org_yuv);
fclose(out_yuv);
}
PSNR计算函数calPSNR.cpp:
#include "PSNR.h"
#include
#include
void calPSNR(unsigned char *yuv_buff,unsigned char *yuv2_buff, int PIC_W, int PIC_H, double *SSD_SUM)
{
unsigned char *orgY_buff, *outY_buff;
orgY_buff = yuv_buff;
outY_buff = yuv2_buff;
double *mSSD_SUM = SSD_SUM;
double SSD[3] = {0};
double PSNR[3] = {0};
int h,v;
for(int ch = 0 ;ch < 3; ch++)
{
int shift = 0;
if(ch)
shift = 1;
int height = PIC_H>>shift;
int width = PIC_W>>shift;
for(v=0; vfor(h=0; hint iDiff = orgY_buff[h] - outY_buff[h];
if(iDiff>255)
iDiff = 255;
SSD[ch] += iDiff * iDiff;
}
orgY_buff += width;
outY_buff += width;
}
SSD[ch] /=(width*height); //防止溢出
mSSD_SUM[ch] += SSD[ch];
PSNR[ch] = ( SSD[ch] ? 10.0 * log10((255 * 255)/ SSD[ch] ) : 999.99 );
}
printf("Y-PSNR : %f, U-PSNR : %f, V-PSNR : %f\n",PSNR[0],PSNR[1],PSNR[2]);
}
头文件PSNR.h:
#include
#include
#include
#include
void calPSNR(unsigned char *rgb_buff, unsigned char *yuv_buff, int ImageWidth, int ImageHeight, double *SSD_SUM);