数据压缩学习实验(五)均匀量化器设计即DPCM编码的C++实现

实验目的

掌握DPCM编解码系统的基本原理。设计均匀量化器对误差图像进行量化,观察分析量化及预测结果;使用Huffman编码程序对原始图像和预测误差图像进行编码,统计符号概率并分析其压缩效率。

实验内容

  1. DPCM编解码原理
    在这里插入图片描述
    在预测编码DPCM中,先根据前几个抽样值计算出一个预测值,再取当前抽样值和预测值之差。将此差值编码并传输。此差值称为预测误差。由于抽样值及其预测值之间有较强的相关性,即抽样值和其预测值非常接近,使此预测误差的可能取值范围,比抽样值的变化范围小。所以,可以少用编码比特来对预测误差编码,从而降低其比特率。此预测误差的变化范围较小,它包含的冗余度也小。这就是说,利用减小冗余度的办法,降低了编码比特率。
    需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是
    因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
    本次实验中,预测器逐行预测像素,预测值为当前像素的左方像素。对于每行第一个像素预测值为128。

  2. 量化器设计
    采用均匀量化方式对预测误差进行量化
    在这里插入图片描述
    在这里插入图片描述
    编码器将每个区间/bin的索引发给解码器,解码器用重构水平表示该区间内所有的值。
    考虑到8bit灰度图像量化后误差值范围[-255,255],在8bit量化时,将该范围 / 2 压缩至[-127,127]并进行+127电平提升使得其可以被8bit unsigned char 型数据表示;在4bit量化时,将该范围 / 32 压缩至[-7,7],理论上需要进行7电平提升,但编程时考虑到令unsigned char型数据表示方便,同时便于计算误差,将其电平提升至127。

  3. 图像量化误差分析
    MSE(Mean squared error)均方误差:
    对于给定m*n大小图片,有
    在这里插入图片描述
    其中,I为参考图像,K为噪声图像。
    PSNR (Peak Signal-to-Noise Ratio) 峰值信噪比:
    在这里插入图片描述
    因为图像像素最大值为255,所以MAXi取255即可,PSNR使用dB表示图像峰值信噪比,越大说明图像质量越好。实验采用使用PSNR分析图片量化后误差。

  4. Huffman编码
    Huffman编码原理在这里不深入讨论,使用现有Huffman编码器对数据进行编码,实验关心的内容在于预测和量化对于图像压缩率的影响。当符号集较大且概率分布不是很悬殊时,即经过DPCM系统的误差值,Huffman的平均码长可接近于熵。而对于概率分布较为悬殊的消息集合,如原始图像,上限可能很大,意味着编码剩余度较大。

实验结果

  1. DPCM预测误差以及PSNR:
    在这里插入图片描述
    实验中仅对部分图像进行了4bit量化。
    可见,在8bit量化时由于解码值与原始像素值误差为±1,且不会发生过载,所以恢复的图像仍能保持较高的PSNR,质量较好。而4bit量化时由于量化区间过宽,量化误差较大,图片PSNR低且主观感受上质量很差。

  2. 编码文件尺寸:
    在这里插入图片描述
    可见,DPCM+Huffman编码对于大部分像素间具有较强相关性的图像有着比单纯Huffman编码更好的编码效率,且8bit均匀量化时量化误差几乎无法察觉,图像质量令人满意。但是对于噪声图像这种像素间不存在相关性的图像而言,DPCM+Huffman编码压缩效率可能不降反升。需要指出的是,YUV灰度文件中U、V色度通道的值均为127,这部分数据存在很强的相关性。

  3. 像素概率分布分析
    从中选择4号,5号图片的Y通道做概率统计分析,两张图片均为8bit量化:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    由图片原始概率分布及预测误差概率分布可以看出DPCM编码具有很好的概率集中的作用,编码后将码字集中于127附近,使得最终文件的概率分布更适应于如Huffman编码这样基于概率分布的编码,进一步提高了媒体文件文件压缩的效率。

程序代码

读取YUV文件的程序见:BMP文件读取及转换至YUV色彩空间
使用此代码时需要在构造函数加入对YUV三个通道均赋值为127的语句,改动如下:

YUV_raw_image::YUV_raw_image(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m, uint4 num)
{
	init(w, h, f_rate, m);
	frame_num = num;
	data = new YUV_per_frame[frame_num];
	for (int i = 0; i < frame_num; i++) {
		uint1* ptr = new uint1[frame_size]{ 0 };
		if (ptr == nullptr) {
			cerr << __func__ << "\tCan not get memory!" << endl;
			exit(1);
		}
		(data + i)->Y_data = ptr;  //set values of pixel to 0
		(data + i)->U_data = ptr + frame_Y_size;
		(data + i)->V_data = ptr + frame_Y_size + frame_U_size;
	}
	for (int i = 0; i < frame_num; i++) {
		for (int u = 0; u < frame_size; u++) {
			*((data + i)->Y_data + u) = 127;
		}
	}
}

dpcm.h:

#pragma once
#include "YUV_raw.h"

uint1 clamp(int value);
bool DpcmEncoder(uint1* data, int width, int height, int level, 
				uint1* rebuild, uint1* diff, uint1* err);
double calculate_MSE(uint1* data, int width, int height);
double calculate_PSNR(uint1* data, int width, int height);

dpcm.cpp

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

uint1 clamp(int value)
{
	if (value > 255) {
		return 255;
	}
	else if (value < 0) {
		return 0;
	}
	else {
		return (uint1)value;
	}
}

bool DpcmEncoder(uint1* data, int width, int height, int level, uint1* rebuild, uint1* diff, uint1* err)
{
	uint1* DataLine = nullptr;
	uint1* DiffLine = nullptr;
	uint1* RebuildLine = nullptr;
	uint1* ErrLine = nullptr;
	int last_pix = 0;  // last pixel
	int different = 0;  //  different before quantify
	uint1 q_key = 0;  // quantify output
	int q_value = 0;  // value after quantify

	for (int loop_line = 0; loop_line < height; loop_line++) {
		DataLine = data + loop_line * width;  //get data ptr per line
		DiffLine = diff + loop_line * width;
		RebuildLine = rebuild + loop_line * width;
		ErrLine = err + loop_line * width;
		for (int loop_i = 0; loop_i < width; loop_i++) {
			//  get value of prediction pixel
			//  predictor
			last_pix = (loop_i) ? RebuildLine[loop_i - 1] : 128;
			//  caulate diff
			different = DataLine[loop_i] - last_pix;
			  quantify
			switch (level) {
			case(2): {
				DiffLine[loop_i] = (different / 128) + 127;
				q_value = (DiffLine[loop_i] - 127) * 128;
			}break;
			case(4): {
				DiffLine[loop_i] = (different / 32) + 127;
				q_value = (DiffLine[loop_i] - 127) * 32;
			}break;
			case(8): {
				DiffLine[loop_i] = (different / 2) + 127;
				q_value = (DiffLine[loop_i] - 127) * 2;
			}break;
			default: {
				cout << __func__ <<
					"\tInvalid quantification bit num! run as default value 8"
					<< endl;
				DiffLine[loop_i] = (different / 2) + 127;
				q_value = (DiffLine[loop_i] - 127) * 2;
			}break;
			}
			//  storage decode value and calculate error
			RebuildLine[loop_i] = clamp(last_pix + q_value);
			ErrLine[loop_i] = clamp(127 + RebuildLine[loop_i] - DataLine[loop_i]);
		}
	}
	return true;
}

double calculate_MSE(uint1* data_err, int width, int height)
{
	int num = width * height;
	int accumulate_error = 0;
	double avg_error = 0;
	for (int i = 0; i < num; i++) {
		accumulate_error += (data_err[i] - 127) * (data_err[i] - 127);
	}
	avg_error = (double)accumulate_error / (double)num;
	return avg_error;
}

double calculate_PSNR(uint1* data, int width, int height)
{
	double mse = calculate_MSE(data, width, height);
	double psnr = 10 * log10((double)65025 / mse);
	return psnr;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值