使用C++实现DPCM编码(左向预测8bit、4bit、2bit、1bit和上向预测8bit)(更新过)

一、DPCM是什么?

DPCM–差分脉冲编码调制(Differential Pulse code modulation,DPCM),是一种对模拟信号的编码模式,与PCM不同每个抽样值不是独立的编码,而是先根据前一个抽样值计算出一个预测值,再取当前抽样值和预测值之差作编码用。此差值称为预测误差.抽样值和预测值非常接近(因为相关性强),预测误差的可能取值范围比抽样值变化范围小。所以可用少几位编码比特来对预测误差编码,从而降低其比特率.这是利用减小冗余度的办法,降低了编码比特率。
➢ 根据某一模型利用以往的样本值对新样本进行预测,然后将样本的实际值与其预测值相减得到一个误差值,最后对这一误差值进行编码。
➢ 如果模型足够好,且样本序列在时间上相关性较强,则误差信号的幅度将远远小于原始信号,从而得到较大的数据压缩。
在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
在这里插入图片描述
在这里插入图片描述

二、使用C++实现DPCM编码(左向预测8bit、4bit、2bit、1bit和上向预测8bit)

1、实验数据描述

此次用的是Lena.yuv,YUV灰度图像,如下图
在这里插入图片描述

因为所用图片为灰度图像,所以需要先把文件中所有值赋为128,以便将UV分量填满。

//给Buf128全赋值为128
	for (int i = 0; i < frameWidth * frameHeight / 2; i++)
	{
		Buf128[i] = 128;
	}

2、左向预测8bit、4bit、2bit、1bit

1)左向预测8bit

因为是左向预测,所以图片中每一行的第一个值无法预测,所以直接将预测值设为本身的值,插值设为0。

if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}

若不是每一行的第一个值,那就需要先先计算插值a,将这个像素值减去左侧像素的预测值。但是因为图像是8bit(灰度值为0-255),所以如果相减值就会变为-255——+255因此如果想进行8bit量化,需要将插值a/2+127(更新插值d=a/2+127)(量化插值b=a/2)。之后需要进行反量化值c,将量化插值b乘2(c=b*2)。之后再将反量化值c与左侧像素预测值相加得到本像素预测值

				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 2;//进行量化
					c = b * 2;//进行反量化
					d = b + 128;//进行+128
					yChazhiBuf[j + i * frameWidth] = d;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}

2)左向预测4bit

4bit量化与8bit量化的区别在于需要将插值a(-255–+255)转变为4bit插值d,所以需要将插值a/32+7(更新插值d=a/32+7)(量化插值b=a/32),。之后需要进行反量化值c,将插值a乘16(c=b*16)。之后再将反量化值c与左侧像素预测值相加得到本像素预测值

if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 32;//进行量化
					c = b * 32;//进行反量化
					d = b + 7;//进行+7
					yChazhiBuf[j + i * frameWidth] = d*16;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}

3)左向预测2bit

2bit量化与8bit量化的区别在于需要将插值a(-255–+255)转变为2bit插值d,所以需要将插值a/128+2(更新插值d=a/128+2)(量化插值b=a/128),。之后需要进行反量化值c,将插值a乘128(c=b*128)。之后再将反量化值c与左侧像素预测值相加得到本像素预测值

if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 128;//进行量化
					c = b * 128;//进行反量化
					d = b + 2;//进行+2
					yChazhiBuf[j + i * frameWidth] = d*64;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}

4)左向预测1bit

1bit量化与8bit量化的区别在于需要将插值a(-255–+255)转变为1bit插值d,所以需要将插值a/256+1(更新插值d=a/256+1)(量化插值b=a/256)。之后需要进行反量化值c,将插值a乘256(c=b*256)。之后再将反量化值c与左侧像素预测值相加得到本像素预测值

if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 256;//进行量化
					c = b * 256;//进行反量化
					d = b + 1;//进行+1
					yChazhiBuf[j + i * frameWidth] = d*256;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}

5)左向预测8bit、4bit、2bit、1bit整体代码

//左向预测8bit、4bit、2bit、1bit-计算差值与预测值
	for (int i = 0; i < frameHeight; i++)
	{
		for (int j = 0; j < frameWidth; j++)
		{
			if (x == 8)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 2;//进行量化
					c = b * 2;//进行反量化
					d = b + 128;//进行+128
					yChazhiBuf[j + i * frameWidth] = d;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
			if (x == 4)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 16;//进行量化
					c = b * 16;//进行反量化
					d = b + 16;//进行+64
					yChazhiBuf[j + i * frameWidth] = d*8;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
			if (x == 2)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 64;//进行量化
					c = b * 64;//进行反量化
					d = b + 4;//进行+4
					yChazhiBuf[j + i * frameWidth] = d*32;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
			if (x == 1)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 128;//进行量化
					c = b * 128;//进行反量化
					d = b + 2;//进行+2
					yChazhiBuf[j + i * frameWidth] = d*64;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
		}
	}

3、上向预测8bit

因为是上向预测,所以图片中每一列的第一个值无法预测,所以直接将预测值设为本身的值,插值设为0。
若不是每一列的第一个值,那就需要先先计算插值a,将这个像素值减去上侧像素的预测值。但是因为图像是8bit(灰度值为0-255),所以如果相减值就会变为-255——+255因此如果想进行8bit量化,需要将插值a/2+127(更新插值d=a/2+127)(量化插值b=a/2),。之后需要进行反量化值c,将量化插值b乘2(c=b*2)。之后再将反量化值c与上侧像素预测值相加得到本像素预测值

//上预测8bit
	for (int i = 0; i < frameWidth; i++)
	{
		for (int j = 0; j < frameHeight; j++)
		{
			if (x == 99)
			{
				if (j == 0)
				{
					yYuceBuf[i] = yBuf[i];
					yChazhiBuf[i] = 0;
				}
				else
				{
					a = yBuf[i + j * frameWidth] - yYuceBuf[i + (j - 1) * frameWidth];//计算插值
					b = a / 2;//进行量化
					c = b * 2;//进行反量化
					d = b + 127;//进行+127
					yChazhiBuf[i + j * frameWidth] = d;
					yYuceBuf[i + j * frameWidth] = yYuceBuf[i + (j - 1) * frameWidth] + c;
				}
			}
		}
	}

4、整体代码

#include "iostream"
#include"stdio.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include"math.h"
#pragma warning (disable:4996)
using namespace std;
#define u_int8_t	unsigned __int8
#define u_int		unsigned __int32


int main(int argc, char** argv)
{
	char* yuvFileName = NULL;
	char* yuceFileName = NULL;
	char* chazhiFileName = NULL;
	u_int8_t* yBuf = NULL;
	u_int8_t* yuvBuf = NULL;
	u_int8_t* yYuceBuf = NULL;
	u_int8_t* yChazhiBuf = NULL;
	u_int8_t* Buf128 = NULL;
	FILE* yuvFile = NULL;
	FILE* chazhiFile = NULL;
	FILE* yuceFile = NULL;
	u_int frameWidth = 0;
	u_int frameHeight = 0;
	int a = 0;//量化前插值
	int b = 0;//量化后值
	int c = 0;//反量化后值
	int d = 0;//+128之后值
	int x = 0;//选择4bit,8bit量化

	//读取数据yuv文件数据
	yuvFileName = argv[1];
	yuceFileName = argv[2];
	chazhiFileName = argv[3];
	frameWidth = atoi(argv[4]);
	frameHeight = atoi(argv[5]);
	yuvFile = fopen(yuvFileName, "rb");
	yuvBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3 / 2);
	yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
	yYuceBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
	yChazhiBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
	Buf128 = (u_int8_t*)malloc(frameWidth * frameHeight / 2);

	fread(yuvBuf, 1, frameWidth * frameHeight * 3 / 2, yuvFile);

	yuceFile = fopen(yuceFileName, "wb");
	chazhiFile = fopen(chazhiFileName, "wb");

	//给Buf128全赋值为128
	for (int i = 0; i < frameWidth * frameHeight / 2; i++)
	{
		Buf128[i] = 128;
	}

	//读取y数据
	for (int i = 0; i < frameWidth * frameHeight; i++)
	{
		yBuf[i] = yuvBuf[i];
	}

	//选择4bit,8bit量化,2bit量化,1bit量化
	while ((x != 4) && (x != 8) && (x != 2) && (x != 1) && (x != 99))
	{
		cout << "如想左向预测:选择8bit量化或者4bit量化或者2bit量化或者1bit量化,请输入“8”或“4”或“2”或“1”" << endl<< "如想上向预测请输入“99”(8bit)" << endl;
		cin >> x;
		if (x == 8)
		{
			cout << "您选择了左向预测8bit量化";
		}
		else if (x == 4)
		{
			cout << "您选择了左向预测4bit量化";
		}
		else if (x == 2)
		{
			cout << "您选择了左向预测2bit量化";
		}
		else if (x == 1)
		{
			cout << "您选择了左向预测1bit量化";
		}
		if (x == 99)
		{
			cout << "您选择了上向预测";
		}
		else
		{
			cout << "您输入有误请重新输入";
		}
	}

	//左向预测8bit、4bit、2bit、1bit-计算差值与预测值
	for (int i = 0; i < frameHeight; i++)
	{
		for (int j = 0; j < frameWidth; j++)
		{
			if (x == 8)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 2;//进行量化
					c = b * 2;//进行反量化
					d = b + 128;//进行+128
					yChazhiBuf[j + i * frameWidth] = d;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
			if (x == 4)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 16;//进行量化
					c = b * 16;//进行反量化
					d = b + 16;//进行+64
					yChazhiBuf[j + i * frameWidth] = d*8;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
			if (x == 2)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 64;//进行量化
					c = b * 64;//进行反量化
					d = b + 4;//进行+4
					yChazhiBuf[j + i * frameWidth] = d*32;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
			if (x == 1)
			{
				if (j == 0)
				{
					yYuceBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
					yChazhiBuf[j + i * frameWidth] = 0;
				}
				else
				{
					a = yBuf[j + i * frameWidth] - yYuceBuf[j + i * frameWidth - 1];//计算插值
					b = a / 128;//进行量化
					c = b * 128;//进行反量化
					d = b + 2;//进行+2
					yChazhiBuf[j + i * frameWidth] = d*64;
					yYuceBuf[j + i * frameWidth] = yYuceBuf[j + i * frameWidth - 1] + c;
				}
			}
		}
	}
	//上预测
	for (int i = 0; i < frameWidth; i++)
	{
		for (int j = 0; j < frameHeight; j++)
		{
			if (x == 99)
			{
				if (j == 0)
				{
					yYuceBuf[i] = yBuf[i];
					yChazhiBuf[i] = 0;
				}
				else
				{
					a = yBuf[i + j * frameWidth] - yYuceBuf[i + (j - 1) * frameWidth];//计算插值
					b = a / 2;//进行量化
					c = b * 2;//进行反量化
					d = b + 128;//进行+128
					yChazhiBuf[i + j * frameWidth] = d;
					yYuceBuf[i + j * frameWidth] = yYuceBuf[i + (j - 1) * frameWidth] + c;
				}
			}
		}
	}
	fwrite(yYuceBuf, frameWidth * frameHeight, 1, yuceFile);
	fwrite(yChazhiBuf, frameWidth * frameHeight, 1, chazhiFile);
	fwrite(Buf128, frameWidth * frameHeight / 2, 1, chazhiFile);
	fwrite(Buf128, frameWidth * frameHeight / 2, 1, yuceFile);
	fclose(chazhiFile);
	fclose(yuceFile);
	fclose(yuvFile);
	free(yBuf);
	free(yuvBuf);
	free(yYuceBuf);
	free(yChazhiBuf);
	free(Buf128);

	return 0;
}

5、实验结果分析

1)左向预测8bit、4bit、2bit、1bit结果对比

在这里插入图片描述
由图像可知,量化bit数的减少会导致插值边缘越来越不明显,并且得到的恢复图像与原始图像的差距也越来越大,与预期相符

1)上向预测8bit与左向预测8bit对比

在这里插入图片描述
由图可知,圈出的地方,上向预测是白,左向预测是黑,因为通过原图可看出,在这个部分,下侧像素比上侧像素要白,也就是下侧灰度值(down)大于上册灰度值(up),所以up-down为正值因此为白;而这个部分的右侧像素比左侧像素黑,也就是右侧像素值(right)小于左侧像素值(left),所以right-left为负值因此为黑。这样也就能分辨出上向预测与左向预测。

三、归纳总结

通过此次实验进一步了解DPCM编码,并完成了DPCM左向8bit、4bit、2bit、1bit以及上向8bit的代码实现。同时,经过详细分析左向预测8bit、4bit、2bit、1bit和上向预测8bit的实验结果,明白了其之间的差异,并会通过结果区分左向预测与上向预测。并且熟悉巩固了C++语言的使用,为后续进一步学习数据压缩打下了基础。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值