实验原理
DCPM编码原理
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。DCPM编码是对模拟信号幅度抽样的差值进行量化编码的调制方式,这种方式是用已经过去的抽样值来预测当前的抽样值,对它们的差值进行编码。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,下面分别是编码器系统框图和解码器系统框图,可以看出,虚线框中框出的即为编码器中内嵌的解码器。
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器虚线框中所示。
在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。
在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧、上方预测均可。量化器采用8bit均匀量化。本实验的目标是验证DPCM编码的编码效率。首先读取一个256级的灰度图像,采用自己设定的预测方法计算预测误差,并对预测误差进行8比特均匀量化(基本要求)。还可以对预测误差进行1比特、2比特和4比特的量化设计。
本实验我采用的是左侧预测,8比特均匀量化。
实验流程框图:
代码分析
main.cpp
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<Windows.h>
#include<math.h>
#include"DPCM.h"
#define u_int8_t unsigned __int8
#define u_int unsigned __int32
#define u_int32_t unsigned __int32
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
int main(int argc, char** argv)
{
bool flip = FALSE; //BMP图像倒序存储数据 需要翻转
char* bmpFileName = NULL; //输入
char* yuvFileName = NULL; //输出
char* deviationFileName = NULL; //误差
char* rebuildFileName = NULL; //重建
FILE* bmpFile = NULL;
FILE* yuvFile = NULL;
FILE* deviationFile = NULL;
FILE* rebuildFile = NULL;
u_int8_t* rgbBuf = NULL;
u_int8_t* yBuf = NULL;
u_int8_t* uBuf = NULL;
u_int8_t* vBuf = NULL;
u_int8_t* deviationBuf = NULL;
u_int8_t* rebuildBuf = NULL;
u_int frameWidth, frameHeight; //宽高
int i = 0, j = 0;
bmpFileName = argv[1]; //输入
yuvFileName = argv[2]; //输出
deviationFileName = argv[3]; //误差
rebuildFileName = argv[4]; //重建
/read files/
bmpFile = fopen(bmpFileName, "rb");
if (bmpFile == NULL)
{
printf("cannot find bmp file\n");
exit(1);
}
else
{
printf("The input bmp file is %s\n", bmpFileName);
}
yuvFile = fopen(yuvFileName, "wb");
if (yuvFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
else
{
printf("The output yuv file is %s\n", yuvFileName);
}
deviationFile = fopen(deviationFileName, "wb");
if (deviationFile == NULL)
{
printf("cannot find deviation file\n");
exit(1);
}
else
{
printf("The output deviation file is %s\n", deviationFileName);
}
rebuildFile = fopen(rebuildFileName, "wb");
if (rebuildFile == NULL)
{
printf("cannot find rebuild file\n");
exit(1);
}
else
{
printf("The output rebuild file is %s\n", rebuildFileName);
}
printf("\n");
/read BMP fileheader & infoheader/
if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
{
printf("read file header error!");
exit(0);
}
if (File_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
else
{
printf("this is a bmp file.\n");
}
if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
{
printf("read info header error!");
exit(0);
}
printf("bitcount==%d\n", (unsigned char)Info_header.biBitCount);
/end read header/
frameWidth = Info_header.biWidth; //宽高赋值
frameHeight = Info_header.biHeight;
/开缓存区/
rgbBuf = (u_int8_t *)malloc(sizeof(u_int8_t)*frameHeight*frameWidth * 3);
memset(rgbBuf, 0, frameHeight*frameWidth * 3);
yBuf = (u_int8_t*)malloc(sizeof(u_int8_t)*frameHeight*frameWidth);
uBuf = (u_int8_t*)malloc(sizeof(u_int8_t)*frameHeight*frameWidth / 4);
vBuf = (u_int8_t*)malloc(sizeof(u_int8_t)*frameHeight*frameWidth / 4);
deviationBuf = (u_int8_t*)malloc(sizeof(u_int8_t)*frameHeight*frameWidth);
rebuildBuf = (u_int8_t*)malloc(sizeof(u_int8_t)*frameHeight*frameWidth);
ReadRGB(rgbBuf, bmpFile, File_header, Info_header);
if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
{
printf("Color transferring failed");
return 0;
}
printf("framewidth==%d,frameheight==%d.\n", frameWidth, frameHeight); //输出图像宽高
DO_DPCM(yBuf, deviationBuf, rebuildBuf,frameWidth,frameHeight); //进行DPCM编码
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile); //写输出文件
fwrite(deviationBuf, 1, frameHeight*frameWidth, deviationFile);
fwrite(rebuildBuf, 1, frameHeight*frameWidth, rebuildFile);
/关缓存/
if (rgbBuf != NULL)
free(rgbBuf);
if (yBuf != NULL)
free(yBuf);
if (uBuf != NULL)
free(uBuf);
if (vBuf != NULL)
free(vBuf);
if (deviationBuf != NULL)
free(deviationBuf);
if (rebuildBuf != NULL)
free(rebuildBuf);
/关文件/
if (bmpFile != NULL)
fclose(bmpFile);
if (yuvFile != NULL)
fclose(yuvFile);
if(deviationFile != NULL)
fclose(deviationFile);
if(rebuildFile != NULL)
fclose(rebuildFile);
system("pause");
return 0;
}
DPCM.cpp
#include<Windows.h>
#include<stdio.h>
#include<math.h>
#include"DPCM.h"
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
bool MakePalette(FILE * pFile, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h, RGBQUAD *pRGB_out)
{
/*若图像开始位置与INFOHEADER结束处位置,还有pow(2, info_h.biBitCount)个
结构体RGBQUAQ的空间(颜色数为2的biBitCount次方,调色板为数组),则说明
该bmp图像有调色板。*/
if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD)*pow(2.0, info_h.biBitCount))
{
fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0);
fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2.0, info_h.biBitCount), pFile);
return true;
}
else
return false;
}
void ReadRGB(unsigned char * rgbbuf, FILE *bmpfile, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h)
{
u_int Width = 0, Height = 0;
long Loop = 0;
unsigned char *bmpbuf = NULL;
unsigned char *rgb = NULL;
unsigned char mask = 0;
int deltaw = 0;
rgb = rgbbuf;
if (((info_h.biWidth * info_h.biBitCount / 8) % 4) == 0)
Width = info_h.biWidth*info_h.biBitCount / 8;
else
Width = (info_h.biWidth * info_h.biBitCount + 31) / 32 * 4;
if ((info_h.biHeight % 2) == 0)
Height = info_h.biHeight;
else
Height = info_h.biHeight + 1;
deltaw = Width - info_h.biWidth * info_h.biBitCount / 8;
bmpbuf = (unsigned char *)malloc(sizeof(unsigned char)*Height*Width);
printf("word_width :%d , word_height: %d \n", Width, Height);
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD) * (unsigned int)pow(2.0, (double)info_h.biBitCount));
if (!MakePalette(bmpfile, file_h, info_h, pRGB))
{
printf("No palette!\n"); //没有调色板
}
fread(bmpbuf, 1, Height*Width, bmpfile);
if (info_h.biBitCount == 24) //24位bmp
{
for (Loop = 0; Loop<Height*Width; Loop++)
{
if (deltaw != 0)
{
if (deltaw == 1)
{
if ((Loop + 1) % Width == 0)
continue;
}
else if (deltaw == 2)
{
if ((Loop + 1) % Width == 0 || (Loop + 2) % Width == 0)
continue;
}
else
{
if ((Loop + 1) % Width == 0 || (Loop + 2) % Width == 0 || (Loop + 3) % Width == 0)
continue;
}
}
*rgb = *(bmpbuf + Loop);
rgb++;
}
}
else if (info_h.biBitCount == 16) //16位bmp
{
for (Loop = 0; Loop < Height * Width; Loop += 2)
{
*rgb = (bmpbuf[Loop] & 0x1F) << 3;
*(rgb + 1) = ((bmpbuf[Loop] & 0xE0) >> 2) + ((bmpbuf[Loop + 1] & 0x03) << 6);
*(rgb + 2) = (bmpbuf[Loop + 1] & 0x7C) << 1;
rgb += 3;
}
}
else if (info_h.biBitCount == 32) //32位bmp
{
for (Loop = 0; Loop < info_h.biWidth*info_h.biHeight; Loop++)
{
*(rgb + 3 * Loop) = *(bmpbuf + 4 * Loop);
*(rgb + 3 * Loop + 1) = *(bmpbuf + 4 * Loop + 1);
*(rgb + 3 * Loop + 2) = *(bmpbuf + 4 * Loop + 2);
}
}
else //其他位bmp
{
for (Loop = 0; Loop < Height*Width; Loop++)
{
if (deltaw != 0)
{
if (deltaw == 1)
{
if ((Loop + 1) % Width == 0)
continue;
}
else if (deltaw == 2)
{
if ((Loop + 1) % Width == 0 || (Loop + 2) % Width == 0)
continue;
}
else
{
if ((Loop + 1) % Width == 0 || (Loop + 2) % Width == 0 || (Loop + 3) % Width == 0)
continue;
}
}
switch (info_h.biBitCount) //蒙版
{
case 1:
mask = 0x80; //10000000
break;
case 2:
mask = 0xC0; //11000000
break;
case 4:
mask = 0xF0; //11110000
break;
case 8:
mask = 0xFF; //11111111
break;
}
int shiftCnt = 1;
while (mask)
{
/*根据从数据中提取出的索引号index,以index为调色板数组下标去查询
数据中每info_h.biBitCount位所代表的颜色。
while 循环的次数:
1bit 图像 每字节循环8次
2bit 图像 每字节循环4次
4bit 图像 每字节循环2次
8bit 图像 每字节循环1次。
*/
unsigned char index =
mask == 0xFF ? bmpbuf[Loop] : ((bmpbuf[Loop] & mask) >> (8 - shiftCnt *info_h.biBitCount));
*rgb = pRGB[index].rgbBlue;
*(rgb + 1) = pRGB[index].rgbGreen;
*(rgb + 2) = pRGB[index].rgbRed;
if (info_h.biBitCount == 8) mask = 0;
else mask >>= info_h.biBitCount;
rgb += 3;
shiftCnt++;
}
}
}
if (pRGB != NULL)
free(pRGB);
if (bmpbuf != NULL)
free(bmpbuf);
}
int RGB2YUV(int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
{
static int init_done = 0;
long i, j, size;
float yf, uf, vf;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}
b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
/RGB to YUV/
if (!flip)
{
for (j = 0; j < y_dim; j++)
{
y = y_buffer + (y_dim - j - 1) * x_dim;
u = u_buffer + (y_dim - j - 1) * x_dim;
v = v_buffer + (y_dim - j - 1) * x_dim;
for (i = 0; i < x_dim; i++) {
g = b + 1;
r = b + 2;
yf = (RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
uf = (-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
vf = ((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
*y = (unsigned char)(yf>16 ? (yf>235 ? 235 : (unsigned char)yf) : 16);
*u = (unsigned char)(uf>16 ? (uf>240 ? 240 : (unsigned char)uf) : 16);
*v = (unsigned char)(vf>16 ? (vf>240 ? 240 : (unsigned char)vf) : 16);
b += 3;
y++;
u++;
v++;
}
}
}
else
{
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
*v = (unsigned char)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y++;
u++;
v++;
}
}
for (j = 0; j < y_dim / 2; j++)
{
psu = sub_u_buf + j * x_dim / 2;
psv = sub_v_buf + j * x_dim / 2;
pu1 = u_buffer + 2 * j * x_dim;
pu2 = u_buffer + (2 * j + 1) * x_dim;
pv1 = v_buffer + 2 * j * x_dim;
pv2 = v_buffer + (2 * j + 1) * x_dim;
for (i = 0; i < x_dim / 2; i++)
{
*psu = (*pu1 + *(pu1 + 1) + *pu2 + *(pu2 + 1)) / 4;
*psv = (*pv1 + *(pv1 + 1) + *pv2 + *(pv2 + 1)) / 4;
psu++;
psv++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}
free(u_buffer);
free(v_buffer);
return 0;
}
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}
void DO_DPCM(void * yBuf, void * deviationBuf, void *rebuildBuf, unsigned int width, unsigned int height)
{
unsigned char *ybuffer = NULL;
unsigned char *deviationbuffer = NULL;
unsigned char *rebuildbuffer = NULL;
int deviation = 0;
int i = 0, j = 0;
ybuffer = (unsigned char *)yBuf; //原始图像缓冲区
deviationbuffer = (unsigned char *)deviationBuf; //误差图像缓冲区
rebuildbuffer = (unsigned char *)rebuildBuf; //重建图像缓冲区
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
/*256级的灰度图像,预测值范围为[-255,255],
8bit量化则除以2,将预测值范围变为[-127,127],
再加128后可转到正常灰度显示
*/
if (j == 0) //对第一个像素进行误差预测
{
deviation = (*ybuffer - 128)/2+128; //量化后的预测误差
*deviationbuffer = (unsigned char)deviation;
deviation = (*deviationbuffer - 128)*2+128; //反量化后的预测误差
*rebuildbuffer = (unsigned char)deviation;
ybuffer++;
deviationbuffer++;
rebuildbuffer++;
}
else //对其余像素进行误差预测
{
deviation = (*ybuffer - *(rebuildbuffer-1))/2+128; //量化后的预测误差
*deviationbuffer = (unsigned char)deviation;
deviation = (*deviationbuffer - 128)*2 + *(rebuildbuffer - 1); //反量化后的预测误差
*rebuildbuffer = (unsigned char)deviation;
ybuffer++;
deviationbuffer++;
rebuildbuffer++;
}
}
}
}
实验结果与分析
原始图像 | 预测量化图像 | 重建图像 | 原图像概率分布 | 预测误差图像概率分布 |
---|---|---|---|---|
压缩性能比较:
文件名称 | 源文件大小(KB) | huffman变换(KB) | 压缩比 | DPCM+huffman变换(KB) | 压缩比 |
---|---|---|---|---|---|
birds.bmp | 384 | 304 | 1.26 | 148 | 2.59 |
lena.bmp | 64 | 53 | 1.20 | 37 | 1.73 |
odie.bmp | 64 | 10 | 6.40 | 9 | 7.11 |
由于量化误差是由[-127,127]上移128得到的,所以所得的误差数据会集中分布在128处。