基于C++实现DPCM压缩系统

2021/4/27 22:26:25

本文主要是介绍基于C++实现DPCM压缩系统,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

基于C++实现DPCM压缩系统

  • 一、实验目的
  • 二、实验原理
  • 三、实验代码
    • 1、头文件 method.h
    • 2、主函数 main.cpp
    • 3、DPCM编码 dpcm.cpp
    • 4、概率分布 prob.cpp
    • 5、压缩质量 psnr.cpp
    • 6、概率分布图 probability.m
  • 四、实验结果
    • 1、DPCM编码
    • 2、熵编码
  • 五、结果分析
    • 1、压缩比
    • 2、压缩质量
  • 六、实验结论
  • 七、一些问题和收获

一、实验目的

掌握DPCM编解码系统的基本原理。初步掌握实验用C++语言编程实现DPCM编码器,并分析其压缩效率。

二、实验原理

在这里插入图片描述

  1. DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
  2. 在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。
  3. 在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧预测。量化器采用8比特均匀量化。本实验的目标是验证DPCM编码的编码效率。
  4. 首先读取一个256级的灰度图像,采用自己设定的预测方法计算预测误差,并对预测误差进行8比特均匀量化(基本要求)。还可对预测误差进行1比特、2比特和4比特的量化设计(提高要求)。
  5. 在DPCM编码器实现的过程中可同时输出预测误差图像和重建图像。
    ① 将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。
    ② 将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。
    ③ 最后比较两种系统(1.DPCM+嫡编码和2.仅进行嫡编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。
  6. PSNR(峰值信噪比)压缩质量:
    通常用来评价一幅图像压缩后和原图像相比质量的好坏,当然,压缩后图像一定会比原图像质量差的,所以就用这样一个评价指标来规定标准了。PSNR越高,压缩后失真越小。这里主要定义了两个值,一个是均方差MSE,另一个是峰值信噪比PSNR,公式如下:
    在这里插入图片描述
    这里的MAX通常是图像的灰度级,实验中为255。
    PSNR值越大,图像质量越高。
    (资料来源于这个地址)

三、实验代码

本次实验代码包含在以下几个文件中,下面分别进行展示和分析(以8bit量化为例)。
在这里插入图片描述

1、头文件 method.h

头文件声明了主函数要用到的方法函数。

#pragma once
int prob(int height, int width, unsigned char* inbuf, double* outpro);
int dpcm(int height, int width, unsigned char* oldbuf, unsigned char* newbuf, unsigned char* difbuf, int dep);
int psnr(int height, int width, unsigned char* oldbuf, unsigned char* newbuf, int dep);

2、主函数 main.cpp

主函数的内容即本次实验的步骤:

  • 文件打开、读取。
  • DPCM编码。
  • PSNR压缩质量计算。
  • 灰度值概率分布计算。
  • 文件写入。
  • 文件关闭。
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<math.h>
#include"method.h"

using namespace std;

int main()
{
	//相关参数
	int height = 768;	//高
	int width = 512;	//宽
	int dep = 8;		//量化比特数

	//缓冲区开辟	
	unsigned char* orbuf = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 1.5);
	unsigned char* rebuf = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 1.5);
	unsigned char* errbuf = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 1.5);
	double* orpro = (double*)malloc(sizeof(double) * 256);
	double* errpro = (double*)malloc(sizeof(double) * 256);


	//文件打开
	FILE* orfile = fopen("Birds.yuv", "rb");
	FILE* refile = fopen("outBirds.yuv", "wb");
	FILE* errfile = fopen("quanBirds.yuv", "wb");
	FILE* ortxt = fopen("orpro.txt", "wb");
	FILE* errtxt = fopen("errpro.txt", "wb");

	if (orfile == NULL || refile == NULL || errfile == NULL || ortxt == NULL || errtxt == NULL)
	{
		cout << "文件打开失败!" << endl;
		return 0;
	}

	//文件读取
	fread(orbuf, 1, height * width * 1.5, orfile);

	//dpcm
	dpcm(height, width, orbuf, rebuf, errbuf, dep);
	
	//psnr计算
	psnr(height, width, orbuf, rebuf, dep);
	
	//灰度值概率计算
	prob(height, width, orbuf, orpro);		//原图概率分布
	prob(height, width, errbuf, errpro);	//预测误差图概率分布 

	//文件写入
	fwrite(rebuf, 1, height * width * 1.5, refile);
	fwrite(errbuf, 1, height * width * 1.5, errfile);
	for (int i = 0; i < 256; i++)
	{
		fprintf(ortxt, "%lf\n", *(orpro + i));
		fprintf(errtxt, "%lf\n", *(errpro + i));
	}


	//文件关闭
	fclose(orfile);
	fclose(refile);
	fclose(errfile);
	fclose(ortxt);
	fclose(errtxt);

	return 0;
}

3、DPCM编码 dpcm.cpp

  • DPCM编码函数中传入参数为:图像高、图像宽、原图首指针、重建图首指针、预测误差图首指针、量化比特数。
  • 方法按照实验原理进行实现。
#include<iostream>
#include<math.h>
using namespace std;

int dpcm(int height, int width, unsigned char* orbuf, unsigned char* rebuf, unsigned char* errbuf, int dep)
{
	//dpcm编解码
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (j == 0)
			{
				*errbuf = (*orbuf - 128 + 255) / pow(2, 9 - dep);
				*rebuf = 128 + (*errbuf * pow(2, 9 - dep) - 255);

				rebuf++;
				orbuf++;
				errbuf++;
			}
			else
			{
				*errbuf = (*orbuf - *(rebuf - 1) + 255) / pow(2, 9 - dep);
				*rebuf = *(rebuf - 1) + (*errbuf * pow(2, 9 - dep) - 255);
				if (*rebuf < 0) *rebuf = 0;
				if (*rebuf > 255) *rebuf = 255;

				rebuf++;
				orbuf++;
				errbuf++;
			}
		}
	}

	//uv分量不处理,差值设为128
	for (int i = 0; i < height * width * 0.5; i++)
	{
		*rebuf = *orbuf;
		*errbuf = 128;

		rebuf++;
		orbuf++;
		errbuf++;
	}

	//指针归位
	for (int i = 0; i < height * width * 1.5; i++)
	{
		errbuf--;
		rebuf--;
		orbuf--;
	}

	return 0;
}

4、概率分布 prob.cpp

平平无奇统计频次,计算概率。

#include<iostream>
#include<math.h>
using namespace std;

int prob(int height, int width, unsigned char* inbuf, double* outpro)
{
	//变量定义
	double size = height * width * 1.5;	//总像素数
	int num[256] = { 0 };
	double pro[256] = { 0 };

	//计算各个灰度出现概率
	for (int i = 0; i < size; i++)
	{
		num[(int)*(inbuf + i)]++;
		pro[(int)*(inbuf + i)] = num[(int)*(inbuf + i)] / size;
	}

	//输出概率
	for (int i = 0; i < 256; i++)
		*(outpro + i) = pro[i];
		
	return 0;
}

5、压缩质量 psnr.cpp

根据实验原理中PSNR计算方法来实现。

#include<iostream>
#include<math.h>
using namespace std;

int psnr(int height, int width, unsigned char* orbuf, unsigned char* rebuf, int dep)
{
	double max = 255;
	double mse = 0;
	double psnr;

	for (int i = 0; i < height; i++)
		for (int j = 0; j < width; j++)
		{
			mse += (orbuf[i * width + j] - rebuf[i * width + j]) * (orbuf[i * width + j] - rebuf[i * width + j]);
		}
	mse = mse / (double)(width * height);
	psnr = 10 * log10((double)(max * max) / mse);
	cout << dep << "比特量化时PSNR = " << psnr << endl;

	return 0;
}

6、概率分布图 probability.m

本来打算如第一次实验那样复制一大堆进数组,这次探索了一下如何获取txt文件内容,探索成功了!上次就是懒

orpro = importdata('D:\CUC\SJYS\SJYS_L8\orpro.txt');
errpro = importdata('D:\CUC\SJYS\SJYS_L8\errpro.txt');

%原图概率分布
figure(1)
plot(orpro);
title('orBirds.yuv概率分布');
xlabel('灰度值');
ylabel('概率');
axis([0 256 0 0.02]);

%预测误差图概率分布
figure(2)
plot(errpro);
title('errBirds.yuv概率分布');
xlabel('灰度值');
ylabel('概率');
axis([0 256 0 0.55]);

四、实验结果

1、DPCM编码

① 运行代码得到4个输出文件,依次为预测误差图像、重建图像、预测误差图像概率分布、原图像概率分布
在这里插入图片描述
② YUVviewer查看图像,matlab输出概率分布图像。
原图及概率分布:

原图概率分布图 (matlab输出)
在这里插入图片描述在这里插入图片描述

不同量化比特数下的结果:

                        8bit4bit2bit1bit
预测误差图像在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
重建图像在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
psnr值在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
预测误差图像概率分布在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

2、熵编码

运用老师发来的huffcode.exe分别对原图预测误差图像进行熵编码。
在这里插入图片描述
运行批处理文件后得到:

  • 原图的熵编码文件和预测误差图像的熵编码文件
    在这里插入图片描述
  • 原图和预测误差图像的字符、码长、码字组成的文本文件
    在这里插入图片描述

五、结果分析

1、压缩比

图片大小 / KB压缩比
原图5761
仅熵编码5201.108
8bit DPCM+熵编码1932.984
4bit DPCM+熵编码1115.189
2bit DPCM+熵编码1244.645
1bit DPCM+熵编码1174.923

2、压缩质量

压缩质量8bit DPCM4bit DPCM2bit DPCM1bit DPCM
psnr值 / dB51.141722.82328.534079.11363

六、实验结论

  • 从图片质量来看,熵编码是无损编码,图片质量最好;对于不同量化比特数的DPCM编码,由PSNR值(实际有偏差,尝试解释见文末)可知量化比特数越高,压缩后的图片质量越高。
    即:熵编码>8bit DPCM+熵编码>4bit DPCM+熵编码>2bit DPCM+熵编码>1bit DPCM+熵编码。1
  • 从压缩方式来看,仅熵编码的压缩比远不如DPCM+熵编码的压缩比。
    即:DPCM+熵编码>熵编码。
  • 针对量化比特数,量化比特数并非越低,压缩比就越高。其中4bit量化压缩比最高。而4bit得出的图像质量过于失真,压缩比高没什么意义。

综上所述,DPCM+熵编码压缩效果整体优于仅使用熵编码,控制合适的量化比特数的情况下,DPCM+熵编码在压缩比和压缩质量(目测)上均有优势,虽然损失了一些质量,但对于人眼来说无伤大雅。

七、一些问题和收获

  • 2bit量化的PSNR小于1bit量化的PSNR,从原理和公式上来看都不合理,但想到之前学过,huffman无损编码的结果的确有可能比原来的文件大的,姑且可以作为解释但好牵强TT
  • 本来不知道怎么把概率输入到txt文件中,试了试原来用的fwrite不太行,毕竟概率是double类型的,查了查资料问了问同学采用了fprintf流式写入的方法。
  • 实不相瞒,把函数封装到单个cpp文件中确实挺爽的。

PS.您现在看到的下面



这篇关于基于C++实现DPCM压缩系统的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程