DPCM差分预测编码实验报告

DPCM

原理

在这里插入图片描述
如图所示,首先输入一个图像,与上一个图像的预测值做差,将差值进行编码。
编码后的差值有两个去向,一是直接输出,二是通过解码器反解出差值,与上一帧的预测值相加,就得到了当前图像的预测值,为下一帧图像到来时做好准备。

实现功能

本次采用左侧预测,并且默认最左侧像素前的真实值为均为128。并且实现了如下功能:

  • 进行不同量化bit数的差分预测编码
  • 将编码结果进行输出并进行霍夫曼编码
  • 分别计算原图像和量化后的图像进行概率分布
  • 分别计算原图像经过熵编码和经过DPCM+熵编码的图像的压缩比
  • 比较二者压缩效率
  • 计算重建图像的PSNR

代码

#include <iostream>
#include <fstream>
#include <cmath>

using namespace std;
const string inPath = "seed.yuv";
const int maxn = 1E5 + 5;
const int maxV = 255, N = 256;

int height = 500, width = 500;
unsigned char *yBuffer, *rebuild, *predict, *tmp, *code;
int bitNum; double delta;
unsigned char limit( int val )
{
    if( val > 255 ) return 255;
    if( val < 0 ) return 0;
    return val;
}
//量化
unsigned char Q( int val )
{
    val += 255;
    int ans = val / delta;
    if( ans == (1 << bitNum) ) ans --;
    return ans;
}
//反量化
int deQ( unsigned char val )
{
    int ans = (val + 0.5)* delta + 0.5;
    ans -= 255;
    if( ans > 255 ) ans = 255;
    return ans;
}
//计算PSNR
double PSNR( unsigned char* standard, unsigned char* image )
{
    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 length = height * width;
    for( int i = 0; i < length; ++ i ) frequency[buffer[i]] ++;
    for( int i = 0; i < N; ++ i ) frequency[i] /= length;
}

int main()
{

    {
        int length = height * width;
        ifstream in( inPath, ios::binary );
        yBuffer = new unsigned char[length];
        rebuild = new unsigned char[length];
        predict = new unsigned char[length];
        code = new unsigned char[length];
        tmp = new unsigned char[length];
        in.read(( char * ) yBuffer, length );
        in.read(( char * ) tmp, length );
        //获得原图像的概率分布
        ofstream out;
        out.open(inPath + ".csv", ios::out);
        double num[N] = {0};
        GetFrequency( yBuffer, num );
        for( int i = 0; i < N; ++ i )
            out << i << "," << num[i] << endl;
        out.close();
        //进行不同bit数的差分脉冲编码
        for ( auto i : { 1, 2, 4, 8 } )
        {
            bitNum = i;
            int n = 1 << bitNum;
            delta = 1.0 * ( 2 * maxV ) / n;
            for ( int h = 0; h < height; ++h )
            {
                for ( int w = 0; w < width; ++w )
                {
                    int val = 0;
                    if ( !w ) val = int( yBuffer[h * width + w] ) - 128;
                    else val = int( yBuffer[h * width + w] ) - rebuild[h * width + w - 1];
                    predict[h * width + w] = val + 128;
                    code[h * width + w] = Q( val );
                    int d = deQ( code[h * width + w] );
                    if ( !w ) rebuild[h * width + w] = d + 128;
                    else rebuild[h * width + w] = limit(d + rebuild[h * width + w - 1]);
                }
            }
            //输出结果
            string suf = "";
            suf += ( '0' + i );
            suf += "bit";
            ofstream out;
            out.open( "predict" + suf + ".yuv", ios::binary );
            out.write(( char * ) predict, length );
            out.write(( char * ) tmp, length );
            out.write(( char * ) tmp, length );
            out.close();

            out.open( "rebuild" + suf + ".yuv", ios::binary );
            out.write(( char * ) rebuild, length);
            out.write(( char * ) tmp, length );
            out.write(( char * ) tmp, length);
            out.close();

            out.open( "code" + suf + ".code", ios::binary );
            out.write((char*)code, length);
            out.close();
            //获得误差的概率分布
            double residualFre[N] = {0};
            GetFrequency( code, residualFre );
            out.open("code" + suf + ".csv", ios::out);
            for( int i = 0; i < (1 << bitNum); ++ i ) out << i << "," << residualFre[i] << endl;
            cout << i << "bit psnr: "<< PSNR( yBuffer, rebuild ) << endl;
        }

        delete[] yBuffer;
        delete[] predict;
        delete[] rebuild;
        delete[] tmp;
        in.close();
    }
    return 0;
}

实验过程

  • 通过上面的代码生成编码、预测误差图像、重建图像;计算原图及编码的概率分布,以及重建图像的PSNR

  • 利用Huffman编码程序对原图像及量化后的编码进行熵编码。
    在这里插入图片描述

  • 比较文件大小、压缩比等参数。

实验结果

此处原文件大小为500* 500 * 3 = 750,000 字节

原图像8bit量化4bit量化2bit量化1bit量化
量化误差图像在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
重建图像在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
概率分布在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
熵编码后的文件大小(Byte)252993139725517414687146871
压缩比33.7%18.6%6.90%6.25%6.25%
PSNR51.152928.653416.82210.8348

可见经过熵编码之后,图像的大小会减小。
经过DCMP+熵编码之后,图像大小比直接使用熵编码减小的更多,随着量化比特数的减小,压缩效率越来越高,但是变化越来越缓慢;于此同时,图像质量,也就是PSNR的值迅速恶化,重建出的图像质量越来越差,到1bit是就已经很模糊了。
综上,量化应采用合适的量化比特数,使之既不至于太影响画面,又可以达到较高的压缩效率。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值