利用DPCM编码进行图像压缩

一幅图像往往像素与像素之间是有关联的,比如说一张人脸的照片,肉色像素的隔壁基本还是肉色像素,那么这两个像素之间的差别就会很小。因此当我们处理一幅图片的时候,可以考虑到这种特性,在压缩时减小这种相关的空间冗余度,DPCM就是基于这种思想一种编码手段。
DPCM的思想十分简单,就是用前一个像素(左边或者上边)来作为下一个像素值的预测值,而一个像素存储值的就会变成预测值与当前实际值的差值。
计算机中8bit能存储的无符号数是从0到255,而差值的取值范围为-255到255,因此需要在得到的差值上加一个255来使之全部变为正值。但这么一来会发现明明8bit就可以存储的图像现在需要9bit才能存储了,这时候就需要将差值也量化一下,例如如果是6bit量化,则需要将差值加255再除以2^(9-6),从而使最后得到的量化值区间在0到63之间。然后再将当前值反方向计算回去得到重建后的值来再作为下一个像素的预测值,原理图如下:
在这里插入图片描述

代码如下:

void DpcmEn(unsigned char* yBuff, unsigned char* preerr, unsigned char* level, int h, int w, int q){
    int prediction;
    int err;
    int i;
    int j;
    int a;
    int b;
    for (i = 0; i < h; i++){
        prediction = 128;
        err = yBuff[i*w] - prediction;
        a = (err + 128)/pow(2, 8 - q);
        if(a > pow(2, q) - 1){
            a = pow(2, q) - 1;
        }
        if(a < 0){
            a = 0;
        }
        preerr[i*w] = a;
        b = preerr[i*w]*pow(2, 8 - q) - 128 + prediction;
        if(b > 255){
            b = 255;
        }
        if(b < 0){
            b = 0;
        }
        level[i*w] = b;
        for (j = 1; j < w; j++){
            prediction = level[i*w + j - 1];
            err = yBuff[i*w + j] - prediction;
            a = (err + 255)/pow(2, 9 - q);
            if(a > pow(2, q) - 1){
                a = pow(2, q) - 1;
            }
            if(a < 0){
                a = 0;
            }
            preerr[i*w + j] = a;
            b = preerr[i*w + j]*pow(2, 9 - q) - 255 + prediction;
            if(b > 255){
                b = 255;
            }
            if(b < 0){
                b = 0;
            }
            level[i*w + j] = b;
        }
    }
}

以8bit量化为例,原始图片、输出出来的重建图片以及预测差值图片如下:
在这里插入图片描述

为了对比不同量化比特数带来的熵值差异,我调用了之前编写过的计算熵值和概率密度函数的代码:
原图:
在这里插入图片描述
对比8bit到5bit的图:
在这里插入图片描述
8bit到1bit熵值对比:
在这里插入图片描述
可以看出来相对原图来说,熵值越来越低,去除了相关性。

接下来我计算了原图与还原出来的图的PSNR从而分析压缩质量,PSNR值越高说明还原的图像质量越好,代码如下:

void PrintPSNR(unsigned char* ybuffer, unsigned char* levelbuffer, int w, int h) {
 double mse;
 double psnr;
    double sum = 0;
 double temp;
 int i;
 for (i = 0; i < w*h; i++) {
  temp = pow((ybuffer[i] - levelbuffer[i]), 2);
  sum += temp;
 }
 mse = sum/(w * h);
 psnr = 10*log10((pow(2,8)-1)*(pow(2,8)-1)/mse);
 cout<<"the psnr is: "<<psnr<<endl;
}

从8bit量化到1bit量化的PSNR结果如下:
在这里插入图片描述
在这里插入图片描述

我又尝试调用了Huffman编码进行压缩的图像文件:
在这里插入图片描述
原图大小为96kb,运用了Huffman编码后压缩后图像大小为72.2KB,我们再将转化过的8bit图像到1bit图像的差值文件用Huffman码编码,压缩后文件大小如下:
在这里插入图片描述
折线图如下:
在这里插入图片描述
最后附上完整代码:
main.c:

#include"iostream"
#include"math.h"
#include"stdio.h"
#include"malloc.h"
#include"DCPM.h"
using namespace std;
int main(int argc, char* argv[]){
    char* yuvaddr = argv[1];
    char* yuv2addr = argv[2];
    int W = atoi(argv[3]);
    int H = atoi(argv[4]);
    char* yuverraddr = argv[5];
    int imgsize = W*H*3/2;
    int q = atoi(argv[6]);
    unsigned char* yuvbuffer = new unsigned char[imgsize];
    unsigned char* ybuffer = new unsigned char[imgsize*2/3];
    unsigned char* ubuffer = new unsigned char[imgsize/6];
    unsigned char* vbuffer = new unsigned char[imgsize/6];
    unsigned char* preerrbuffer = new unsigned char[imgsize*2/3];
    unsigned char* levelbuffer = new unsigned char[imgsize*2/3];
    FILE* imgopen = fopen(yuvaddr,"rb");
    if(imgopen == NULL){
        cout<<"打开yuv文件失败"<<endl;
    }
    FILE* yuvsave = fopen(yuv2addr,"w");
    if(yuvsave == NULL){
        cout<<"创建yuv空白文件失败"<<endl;
    }
    FILE* yuvsave2 = fopen(yuverraddr,"w");
    if(yuvsave2 == NULL){
        cout<<"创建yuv空白文件失败"<<endl;
    }
    fread(yuvbuffer, sizeof(unsigned char), imgsize, imgopen);
    int i;
    for(i = 0; i < imgsize*2/3; i++){
        ybuffer[i] = yuvbuffer[i];
    }
    for(i = 0; i < imgsize/6; i++){
        ubuffer[i] = yuvbuffer[i+imgsize*2/3];
    }
    for(i = 0; i < imgsize/6; i++){
        vbuffer[i] = yuvbuffer[i+imgsize*2/3+imgsize/6];
    }
    DpcmEn(ybuffer, preerrbuffer, levelbuffer, H, W, q);
    fwrite(levelbuffer, sizeof(unsigned char), W*H, yuvsave);
    fwrite(ubuffer, sizeof(unsigned char), W*H/4, yuvsave);
    fwrite(vbuffer, sizeof(unsigned char), W*H/4, yuvsave);
    for(i = 0; i < imgsize/6; i++){
        ubuffer[i] = 128;
    }
    for(i = 0; i < imgsize/6; i++){
        ubuffer[i] = 128;
    }
    fwrite(preerrbuffer, sizeof(unsigned char), W*H, yuvsave2);
    fwrite(ubuffer, sizeof(unsigned char), W*H/4, yuvsave2);
    fwrite(vbuffer, sizeof(unsigned char), W*H/4, yuvsave2);
    PrintPSNR(ybuffer, levelbuffer, W, H);
}

DCPM.h:

#ifndef DCPM_H_INCLUDED
#define DCPM_H_INCLUDED
#include"iostream"
#include"math.h"
#include"stdio.h"
#include"malloc.h"
using namespace std;
void DpcmEn(unsigned char* yBuff, unsigned char* preerr, unsigned char* level, int h, int w, int q){
    int prediction;
    int err;
    int i;
    int j;
    int a;
    int b;
    for (i = 0; i < h; i++){
        prediction = 128;
        err = yBuff[i*w] - prediction;
        a = (err + 128)/pow(2, 8 - q);
        if(a > pow(2, q) - 1){
            a = pow(2, q) - 1;
        }
        if(a < 0){
            a = 0;
        }
        preerr[i*w] = a;
        b = preerr[i*w]*pow(2, 8 - q) - 128 + prediction;
        if(b > 255){
            b = 255;
        }
        if(b < 0){
            b = 0;
        }
        level[i*w] = b;
        for (j = 1; j < w; j++){
            prediction = level[i*w + j - 1];
            err = yBuff[i*w + j] - prediction;
            a = (err + 255)/pow(2, 9 - q);
            if(a > pow(2, q) - 1){
                a = pow(2, q) - 1;
            }
            if(a < 0){
                a = 0;
            }
            preerr[i*w + j] = a;
            b = preerr[i*w + j]*pow(2, 9 - q) - 255 + prediction;
            if(b > 255){
                b = 255;
            }
            if(b < 0){
                b = 0;
            }
            level[i*w + j] = b;
        }
    }
}
void PrintPSNR(unsigned char* ybuffer, unsigned char* levelbuffer, int w, int h) {
 double mse;
 double psnr;
    double sum = 0;
 double temp;
 int i;
 for (i = 0; i < w*h; i++) {
  temp = pow((ybuffer[i] - levelbuffer[i]), 2);
  sum += temp;
 }
 mse = sum/(w * h);
 psnr = 10*log10((pow(2,8)-1)*(pow(2,8)-1)/mse);
 cout<<"the psnr is: "<<psnr<<endl;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值