草庐IT

[数据压缩]_实验③ DPCM编码

Apyyxah_101333 2024-04-12 原文

实验4 DPCM压缩

文章目录


持续更改中。。。

一.实验名称

DPCM 压缩系统的实现和分析

二.实验目的

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

三.实验内容

1.DPCM编码原理

DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。

2.DPCM编码系统的设计

在本次实验中,我们采用固定预测器均匀量化器

在DPCM编码器实现的过程中可同时输出预测误差图像重建图像。将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。

将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。

最后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。

四.实验步骤

1.method.h头文件
#pragma once
#define uchar unsigned char
void dpcm(int h, int w, uchar* origin_buf, uchar* rebuild_buf, uchar* error_buf, int Qbit);
double psnr(int h, int w, uchar* origin_buf, uchar* rebuild_buf, int Qbit);
void prob(int h, int w, uchar* input_buf, double* output_buf);
2.main函数
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<windows.h>
#include"header.h"
#define uchar unsigned char
using namespace std;

int main()
{
	// 1.定义变量
	FILE* originFile = NULL;
	FILE* rebuildFile = NULL;
	FILE* errorFile = NULL;
	FILE* origintxt = NULL;
	FILE* errortxt = NULL;
	BITMAPINFOHEADER INFO_header;
	int Qbit = 2;
	
	
	// 2.打开文件
	if (fopen_s(&originFile, "Lena256B.yuv", "rb") == 0) cout << "原文件打开成功" << endl;
	if (fopen_s(&rebuildFile, "rebuildlena.yuv", "wb") == 0) cout << "重建文件创建成功" << endl;
	if (fopen_s(&errorFile, "errorlena.yuv", "wb") == 0) cout << "预测误差文件创建成功" << endl;
	if (fopen_s(&origintxt, "origin.txt", "wb") == 0) cout << "记录原图概率分布文件创建成功" << endl;
	if (fopen_s(&errortxt, "error.txt", "wb") == 0) cout << "记录预测误差的概率分布的文件创建成功" << endl;
	

	int height = 256;
	int weight = 256;

	// 3.开缓冲
	uchar* y_buffer = new uchar[weight * height];
	uchar* u_buffer = new uchar[(weight * height) /  4];
	uchar* v_buffer = new uchar[(weight * height) / 4];

	//uchar* y_buffer = (uchar*)malloc(sizeof(weight * height)*1.5);
	
	uchar* rebuild_buf = new uchar [height * weight];
	uchar* error_buf = new uchar[height * weight];
	double* origin_prob = new double[256];
	double* error_prob = new double[256];
	
	// 4.读取文件到buffer
	fread(y_buffer, sizeof(uchar), height * weight, originFile);
	fread(u_buffer, sizeof(uchar), (weight * height) / 4, originFile);
	fread(v_buffer, sizeof(uchar), (weight * height) / 4, originFile);

	// 5.dpcm
	dpcm(height, weight, y_buffer, rebuild_buf, error_buf,Qbit);

	// 6.psnr
	psnr(height, weight, y_buffer, rebuild_buf, Qbit);

	// 7.统计概率分布
	prob(height, weight, y_buffer, origin_prob);
	prob(height, weight, error_buf, error_prob);

	// 8.写入文件
	fwrite(rebuild_buf, sizeof(uchar), height * weight, rebuildFile);
	fwrite(u_buffer, sizeof(uchar), height * weight / 4, rebuildFile);
	fwrite(v_buffer, sizeof(uchar), height * weight / 4, rebuildFile);
	
	fwrite(error_buf, sizeof(uchar), height * weight, errorFile);
	fwrite(u_buffer, sizeof(uchar), height * weight / 4, errorFile);
	fwrite(v_buffer, sizeof(uchar), height * weight / 4, errorFile);
	// 概率分布写进txt中
	for (int i = 0; i < 256; i++)
	{
		fprintf(origintxt, "%lf\n", *(origin_prob + i));
		fprintf(errortxt, "%lf\n", *(error_prob + i));
	}
	
	// 9.关闭文件
	
	fclose(originFile);
	fclose(rebuildFile);
	fclose(errorFile);
	fclose(origintxt);
	fclose(errortxt);

	delete[] y_buffer;
	delete[] u_buffer;
	delete[] v_buffer;
	delete[] rebuild_buf;
	delete[] error_buf;
	delete[] origin_prob;
	delete[] error_prob;
	return 0;
}
3.dpcm函数

采用左向预测,第一列像素的误差值设为128.

预测误差图像:当前值-前一个像素的重建值
e ( n ) = y ( n ) − r e ( n − 1 ) − − − 预 测 误 差 e(n)=y(n)-re(n-1)---预测误差 e(n)=y(n)re(n1)

e ^ ( n ) = e ( n ) + 255 🔺 ∗ 0.5 🔺 − − − 量 化 后 的 预 测 误 差 \hat{e}(n)=\frac{e(n)+255}{🔺}*0.5🔺---量化后的预测误差 e^(n)=🔺e(n)+2550.5🔺

重建图像:预测误差值+前一个像素的重建值
r e ( n ) = e ^ ( n ) + r e ( n − 1 ) re(n)=\hat{e}(n)+re(n-1) re(n)=e^(n)+re(n1)

#include<iostream>
#include<math.h>
#define uchar unsigned char
using namespace std;

double Quant(int Qbit, int error)
{
	double k = ((error + 255) / pow(2, 9 - Qbit));
	return k;
}

double InverseQuant(int Qbit, uchar pred)
{
	double k = (pred * pow(2, 9 - Qbit) - 255);
	return k;
}
void dpcm(int h, int w, uchar* origin_buf, uchar* rebuild_buf, uchar* error_buf, int Qbit)
{
	int error;
	for(int i=0;i<h;i++)
	{
		for (int j = 0; j < w; j++)
		{
			if (j == 0)
			{
				// 当前像素-预测值=误差值
				error = (origin_buf[i * w + j]) - 128;//假设第一列的预测值为128;
				// 对第一列像素值做量化
				error_buf[i * w + j] = Quant(Qbit, error);
				cout <<(int) error_buf[i * w + j] << endl;
				// 重建像素做反量化
				rebuild_buf[i * w + j] = InverseQuant(Qbit, error_buf[i * w + j])+128;
			}
			else
			{
				// e(n)=y(n)-re(n-1)
				error = (origin_buf[i * w + j]) - rebuild_buf[i * w + j - 1];
				error_buf[i * w + j] = Quant(Qbit, error);
				cout << (int)error_buf[i * w + j] << endl;
				// re(n)=\hat e(n)+re(n-1)
				rebuild_buf[i * w + j] = InverseQuant(Qbit, error_buf[i * w + j]) + rebuild_buf[i * w + j-1];
			}
		}
	}
	int max = pow(2, Qbit) - 1;
	for (int i = 0; i < h * w; i++)
	{
		if (error_buf[i] < 0)
			error_buf[i] = 0;
		if (error_buf[i] > max)
			error_buf[i] = max;
		if (rebuild_buf[i] < 0)
			rebuild_buf[i] = 0;
		if (rebuild_buf[i] > 255)
			rebuild_buf[i] = 255;
	}
}
4.psnr函数

PSNR,峰值信噪比,通过判断psnr的值来衡量压缩质量。计算规则如下:
P S N R = 10 l g ( 2 b i t s − 1 ) 2 M S E PSNR=10lg\frac{(2^{bits}-1)^2}{MSE} PSNR=10lgMSE(2bits1)2
均方误差MSE:
M S E = ∑ i = 0 M ∑ j = 0 N [ f ( I , j ) − f ‘ ( I , j ) ] 2 M N MSE=\frac{\sum_{i=0}^{M}\sum_{j=0}^{N}[f(I,j)-f^‘(I,j)]^2}{MN} MSE=MNi=0Mj=0N[f(I,j)f(I,j)]2
图像压缩质量衡量规则:

  • PSNR>40dB,图形质量非常好,接近于原图像
  • 30dB<PSNR<40dB,图像有可查觉的失真,质量仍可接受
  • 20dB<PSNR<30dB,图像质量较差
  • PSNR<20,图像质量无法接受
double psnr(int h, int w, uchar* origin_buf, uchar* rebuild_buf, int Qbit)
{
	double mse = 0, psnr = 0;
	for (int i = 0; i < h * w; i++)
	{
		mse += pow((origin_buf[i] - rebuild_buf[i]), 2);
	}
	mse = mse / (h * w);
	psnr = 10 * log10(pow(255, 2) / mse);
	cout << Qbit << "比特量化时psnr=" << psnr << endl;
	return psnr;
}
5. prob函数
void prob(int h, int w, uchar* input_buf, double* output_buf)
{
	double size = h * w;
	int num[256] = { 0 };
	double prob[256] = { 0 };
	
	for (int i = 0; i < size; i++)
	{
		num[(int)*(input_buf + i)]++;
		prob[(int)*(input_buf + i)] = num[(int)*(input_buf + i)] / size;
	}
	for (int i = 0; i < 256; i++)
	{
		*(output_buf + i) = prob[i];
	}
}

五.实验结果

1.dpcm

原图:

原图原图的灰度值的概率分布
2.熵编码
  • 批处理文件

熵编码结果:

  • 原图的熵编码文件+预测误差图像的熵编码文件

  • 原图和预测误差图像的字符、码长、码字组成的txt文件

3.对比:
压缩比大小/kB压缩比
原图961
仅熵编码691.6
8bitDPCM+熵编码462.1
4bitDPCM+熵编码244
2bitDPCM+熵编码234.17
4.总结:

量化器的选择

  • 图像失真方面:量化比特数越小,图像失真越明显。
  • 图像压缩效率方面:量化比特数越小,压缩效率越高。
  • 折中考虑图像的失真与码率,选择8bit以上的量化器。

压缩方案的选择

  • 仅用熵编码压缩的系统的压缩效率远不如量化+熵编码的压缩系统.

为什么先有失真编码再无失真编码?

  • 老师讲信息论的时候,这个问题也是要求重点掌握的知识点。做过实验后才真正明白其中的意义。根据无失真信源编码定理(香农第一定理),平均码长的下界为信源熵。在压缩方面,信源熵越小,平均码长越短,压缩效率越高。

  • 这样一来,问题转变为要使得信源熵变小。也就是要使得信源序列的分布变得不均匀。利用信源间的相关性进行差分预测编码或去除信源间的相关性的变换编码可以使得信源序列的分布变得不均匀。

和实际信号的分布相比,差分预测误差是关于0的高尖峰。因此,预测误差总是具有比原始密度更小的熵。这意味着,预测的过程是把样值间的大部分冗余给去掉了。

疑问

❓huffman编码后的概率分布有点问题

128那里有个奇怪的值,不懂为啥。

有关[数据压缩]_实验③ DPCM编码的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  5. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  8. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  9. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  10. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

随机推荐