数据压缩原理 实验四 DPCM压缩系统的实现和分析

实验原理

实验原理

DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器虚线框中所示。

在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。

在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧、上方预测均可。量化器采用8bit均匀量化。本实验的目标是验证DPCM编码的编码效率。首先读取一个256级的灰度图像,采用自己设定的预测方法计算预测误差,并对预测误差进行8比特均匀量化(基本要求)。还可以对预测误差进行1比特、2比特和4比特的量化设计。

本实验我采用的是左侧预测,8比特均匀量化。

实验流程

先将BMP文件转换为YUV文件,提取Y数据,再对Y数据进行预测计算误差,然后对预测误差进行8bit均匀量化,再对量化后的预测误差进行反量化,求重建值。将误差图像和原图像传入上次实验的Huffman编码工程得到编码数据。

代码分析

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++;
            }
        }
    }
}

实验结果与总结

实验图片进行预测误差和重建结果如下:

birds
birds误差
birds重建
birds图表
birds误差图表

相机男
相机男误差
相机男重建
相机男图表
相机男误差图表

小丑
小丑误差
小丑重建
小丑图表
小丑误差图表

水果
水果误差
水果重建
水果图表
水果误差图表

lena
lena误差
lena重建
lena图表
lena误差图表

噪声
噪声误差
噪声重建
噪声图表
噪声误差图表

欧弟
欧弟误差
欧弟重建
欧弟图表
欧弟误差图表

圆圈
圆圈误差
圆圈重建
圆圈图表
圆圈误差图表

由于量化误差是由[-127,127]上移128得到的,所以所得的误差数据会集中分布在128处。

将实验图片直接进行Huffman编码和进行DPCM后做Huffman编码的文件进行比较,结果如下:
数据分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DPCM(差分脉冲编码调制)是一种数据压缩系统,其目的是在尽量不失真的情况下减少数据存储或传输所需的带宽。DPCM 压缩系统实现主要是在传输前对原始信号进行差分编码,将每一个样本值与它前面的样本值的差值编码传输。在解码端,将编码的差值与前一个样本值相加,得到解码后的样本值。 DPCM 压缩系统的性能取决于差分编码器的性能和采样率。通常,差分编码器需要对原始信号进行线性预测,并将其与实际样本值的差异编码。预测器的设计和参数设置是影响性能的关键因素。 DPCM 压缩系统主要有以下优点:首先,DPCM 采用差分编码,可以将一系列相邻的样本压缩成差分值,从而减少需要传输的数据量。其次,由于 DPCM 是一种有损压缩策略,可以通过控制压缩后的失真来有效地减少数据量。再者,DPCM 压缩系统的计算和编码速度较快,可以在硬件中实现,适用于实时视频或音频传输。 然而,DPCM 压缩系统也有一些缺点。首先,差分编码容易受到干扰和噪声的影响,从而导致误差传播和失真的增加。此外,DPCM 不适用于大多数不规则信号,如图片和文本等。 总之,DPCM 压缩系统是一种有损压缩策略,可以通过对原始信号进行差分编码来减少数据量。其性能主要取决于差分编码器的设计和采样率,适用于实时音视频传输和存储等场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值