1 實驗目的
掌握DPCM編解碼系統的基本原理。初步掌握實驗用C/C++/Python等語言程式設計實作DPCM編碼器,并分析其壓縮效率。
2 實驗原理
2.1 DPCM
DPCM是差分預測編碼調制的縮寫,是比較典型的預測編碼系統。在DPCM系統中,需要注意的是預測器的輸入是已經解碼以後的樣本。之是以不用原始樣本來做預測,是因為在解碼端無法得到原始樣本,隻能得到存在誤差的樣本。是以,在DPCM編碼器中實際内嵌了一個解碼器,如編碼器中虛線框中所示。
首先輸入一個圖像,與上一個圖像的預測值做差,将內插補點進行編碼。
編碼後的內插補點有兩個去向,一是直接輸出,二是通過解碼器解出內插補點,再與上一幀的預測值相加,就得到了目前圖像的預測值,為下一幀圖像到來時做好準備。
2.2 PSNR(Peak Signal Noise Ratio)
PSNR(峰值信噪比)是一種度量圖像失真的方式,本實驗使用PSNR作為圖像品質評價的名額。峰值信噪比與圖像品質近似成正比關系。
計算公式如下:
3 實驗内容
在本次實驗中,我們采用固定預測器和均勻量化器。預測器采用左側預測。量化器采用8比特均勻量化。
在DPCM編碼器實作的過程中同時輸出預測誤差圖像和重建圖像。将預測誤差圖像寫入檔案并将該檔案輸入Huffman編碼器,得到輸出碼流、給出機率分布圖并計算壓縮比。将原始圖像檔案輸入Huffman編碼器,得到輸出碼流、給出機率分布圖并計算壓縮比。比較兩種系統(DPCM+熵編碼和僅進行熵編碼)之間的編碼效率(壓縮比和圖像品質)。壓縮品質以PSNR進行計算。
3.1 實驗代碼
得到重建圖像以及預測誤差圖像,計算PSNR,得到頻率分布。
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<math.h>
#include<iostream>
using namespace std;
//計算PSNR
double PSNR( unsigned char* standard, unsigned char* image,int height,int width,int bitNum )
{
double psnr = 0, MSE = 0;
for( int i = 0; i < height * width; i++ )
{
MSE += 1ll* (image[i] - standard[i]) * (image[i] - standard[i]);
}
MSE /= height * width;
long long t = 1 << bitNum; t -= 1; t *= t;
psnr = 10 * log10( t / MSE);
return psnr;
}
//得到頻率分布
void GetFrequency( unsigned char* buffer, double* frequency,int height,int width )
{
int length = height * width;
for( int i = 0; i < length; i++ ) frequency[buffer[i]] ++;
for( int i = 0; i < 256; i++ ) frequency[i] /= length;
}
int main(int argc,char** argv)
{
char *yuvfilename=NULL;
char *re_y_filename=NULL;
char *err_filename=NULL;
FILE* yuvfile=NULL;
FILE* re_y_file=NULL;
FILE* err_file=NULL;
int width,height;
yuvfilename=argv[1];
re_y_filename=argv[2];
err_filename=argv[3];
width=atoi(argv[4]);
height=atoi(argv[5]);
unsigned char* u_buffer=NULL;
unsigned char* v_buffer=NULL;
unsigned char* y_buffer=NULL;//原始圖像
unsigned char* re_y_buffer=NULL;//重建值
unsigned char* err_buffer=NULL;//預測誤差
yuvfile=fopen(yuvfilename,"rb");
if(yuvfile==0)
{
printf("cannot find yuv file.\n");
}
else
{
printf("open yuv file successfully!\n");
}
y_buffer=(unsigned char*)malloc(width*height);
u_buffer=(unsigned char*)malloc(width*height/4);
v_buffer=(unsigned char*)malloc(width*height/4);
err_buffer=(unsigned char*)malloc(width*height*1.5);
re_y_buffer=(unsigned char*)malloc(width*height*1.5);
fread(y_buffer,1,width*height,yuvfile);
fread(u_buffer,1,width*height/4,yuvfile);
fread(v_buffer,1,width*height/4,yuvfile);
//得到原始圖像的機率分布
FILE *orig;
orig=fopen("C:/Users/15643/Desktop/DPCM/DPCM/DPCM/orig.txt","wb");
double frequency[256]={0};
GetFrequency(y_buffer,frequency,height,width);
for(int i=0;i<256;i++)
{
fprintf(orig,"%d\t%f\n",i,frequency[i]);
}
//DPCM編碼
for(int i=0;i<width*height;i++)
{
float err;//誤差
if(i%width==0)
{
err=float(y_buffer[i])-128;//假設預測值為128
err_buffer[i]=(unsigned char)(err/2+128);//對預測誤差量化
re_y_buffer[i]=(unsigned char)(128+(err_buffer[i]-128)*2);//重建值=反量化後的誤差+預測值
}
else
{
err=float(y_buffer[i]-re_y_buffer[i-1]);//選取前一像素的重建值作為預測值
err_buffer[i]=(unsigned char)(err/2+128);
re_y_buffer[i]=(unsigned char)(re_y_buffer[i-1]+(err_buffer[i]-128)*2);
}
}
//得到殘差的機率分布
FILE *err;
err=fopen("C:/Users/15643/Desktop/DPCM/DPCM/DPCM/err.txt","wb");
double frequency_[256]={0};
GetFrequency(err_buffer,frequency_,height,width);
for(int i=0;i<256;i++)
{
fprintf(err,"%d\t%f\n",i,frequency_[i]);
}
//計算PSNR
cout<<"psnr="<<PSNR(y_buffer,re_y_buffer,height,width,8)<<endl;
re_y_file=fopen(re_y_filename,"wb");
err_file=fopen(err_filename,"wb");
fwrite(re_y_buffer,width*height,1,re_y_file);
fwrite(u_buffer,width*height/4,1,re_y_file);
fwrite(v_buffer,width*height/4,1,re_y_file);
fwrite(err_buffer,width*height,1,err_file);
fwrite(u_buffer,width*height/4,1,err_file);
fwrite(v_buffer,width*height/4,1,err_file);
fclose(re_y_file);
fclose(err_file);
}
設定指令參數:
執行結果:
使用YUVviewer檢視生成的結果:
利用origin軟體繪制頻率分布圖:
1.原始圖像:
2.殘差圖像
3.2 利用Huffman編碼器進行熵編碼
1. DPCM+熵編碼
在指令行中輸入:
結果:
2. 僅進行熵編碼
在指令行輸入:
結果:
3. 對比
DPCM+熵編碼 | 僅進行熵編碼 | |
---|---|---|
編碼對象 | 殘差圖像 Lena_err.yuv | 原始圖像 Lena256B.yuv |
原始大小(KB) | 96 | 96 |
壓縮後大小(KB) | 46 | 69 |
壓縮比 | 47.9% | 71.9% |