数据压缩_实验四_DCPM编码实验

DPCM编码实验

一、实验原理

1、DPCM编解码原理

DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,

需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是

因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实

际内嵌了一个解码器,如编码器中虚线框中所示。

截屏2020-05-03上午9.59.41

二、实验步骤

1、基本流程

test

2、关键代码

(1)绘制原图像Y分量概率分布图
// 计算原始图像Y概率分布,输出至txt
void img_freq(unsigned char* y,int width,int height) {
	int count_Y[256] = { 0 };
	double freq_Y[256] = { 0 };
	for (int i = 0; i < width*height; i++) {
		count_Y[*(y + i)]++;
	}
	ofstream Y_sat;
	Y_sat.open("Y_sat.txt", ios::out, ios::trunc);
	if (!Y_sat) {
		cout << "Error opening Y_sat.txt" << endl;
	}
	else {
		Y_sat << "symbol\tfreq" << endl;
		for (int i = 0; i < 256; i++) {
			freq_Y[i] = count_Y[i] / (double)(width * height);
			Y_sat << i << "\t" << freq_Y[i] << endl;
		}
	}
	Y_sat.close();
}

以图像seed.yuv为例,画出概率分布图如下:

image-20200503102423291
(2)取图像第一列直接赋值

由于DPCM预测编码是取左侧像素重建值与当前像素真实值相减,第一列像素无前序重建值,故直接将第一列像素值赋给重建图像,取预测误差为0。

    // 将第一列全部写入重建图像
    for (int i = 0; i < Height; i++) {
        ybuff_recon[i * Width] = ybuff[i * Width];
        ybuff_pre_d[i * Width] = 0;
        ybuff_pre[i * Width] = Quantify(N_bits, ybuff_pre_d[i * Width]);
        val_error[i * Width] = 0;
    }
(3)DPCM编码

DPCM基本框架如下:

 // DPCM
    for (int j = 0; j < Height; j++) {
        for (int i = 1; i < Width; i++) {
            // 取左侧像素重建值与当前像素真实值相减
            ybuff_pre_d[j * Width + i] =ybuff[j * Width + i] - ybuff_recon[j * Width + i - 1];
            // 对预测误差进行量化
            ybuff_pre[j * Width + i] = Quantify(N_bits,ybuff_pre_d[j * Width + i]);
            int temp;
            // 反量化得到重建值
            temp = ybuff_recon[j * Width + i - 1] + Re_Quantify(N_bits,ybuff_pre[j * Width + i]);
            temp = temp > 255 ? 255 : temp;
            temp = temp < 0 ? 0 : temp;
            ybuff_recon[j * Width + i] = (unsigned char)temp;

            // 求解重建值和与实际值误差
            val_error[j * Width + i] = (int)ybuff_recon[j * Width + i] - (int)ybuff[j * Width + i];
        }
    }
(4)量化与反量化
  1. 依据设定的量化比特数,将-255~+255均匀划分为2N_bits个区间,取区间中值作为重构水平。
  2. 编码器将每个区间的索引发给解码器。
  3. 解码器用重构水平表示该区间内所有的值。
// 量化
unsigned char Quantify(int N_Bits,int x) {
	int temp = 0;
	switch (N_Bits) {
	case 1:
		temp = (int)((x + 255) / 255.5);
		break;
	case 2:
		temp = (int)((x + 255) / 127.75);
		break;
	case 4:
		temp = (int)((x + 255) / 31.9375);
		break;
	case 8:
		temp = (int)((x + 255) / 2.0);
		break;
	default:
		cout << "error" << endl;
		exit(0);
		break;
	}
	return (unsigned char)temp;
}
// 反量化
int Re_Quantify(int N_Bits, unsigned char x) {
	int temp = (int)x;
	switch (N_Bits) {
	case 1:
		temp = (int)((temp * 255.5 + 127.75) - 255 + 0.5);
		break;
	case 2:
		temp = (int)((temp * 127.5 + 63.75) - 255+0.5);
		break;
	case 4:
		temp = (int)((temp * 31.9375 + 15.96875) - 255+0.5);
		break;
	case 8:
		temp = (temp * 2) - 255;
		break;
	default:
		cout << "error" << endl;
		exit(0);
		break;
	}
	return temp;
}
(5)计算PSNR

峰值信噪比(PSNR), 一种评价图像的客观标准。PSNR一般是用于最大值信号和背景噪音之间的一个工程项目。通常在经过影像压缩之后,通常输 出的影像都会在某种程度与原始影像不同。为了衡量经过处理后的影像品质,我们通常会参考PSNR值来衡量某个处理程序能否令人满意。

PSNR计算公式如下:
P S N R = 10 l o g 10 ( M A X 2 M S E ) \text{} PSNR=10log_{10}\left( \frac{MAX^{2}}{MSE} \right) PSNR=10log10(MSEMAX2)

M S E = 1 m n ∑ i = 1 n ∑ j = 1 m ∣ K ( i , j ) − I ( i , j ) ∣ 2 MSE=\frac{1}{mn} \sum^{n}_{i=1} \sum^{m}_{j=1} \left\vert K\left( i,j\right) -I\left( i,j\right) \right\vert^{2} MSE=mn1i=1nj=1mK(i,j)I(i,j)2

// 计算PSNR
double Count_PSNR(int N_bits, int width,int height,int* delta) {
	double psnr = 0;
	double mse = 0;
	for (int i = 0; i < width*height; i++) {
		mse += pow(*(delta + i), 2);
	}
	mse = mse / (width * height);
	psnr = 10 * log10(pow((pow(2, N_bits) - 1), 2) / mse);
	return psnr;
}
(6)计算预测图像量化索引概率分布
// 计算预测图像Y概率分布,输出至txt
void predict_freq(int N_bits, int width, int height, unsigned char* ybuff) {
	double* freq = new double[pow(2, N_bits)];
	for (int i = 0; i < pow(2, N_bits); i++) {
		*(freq + i) = 0;
	}
	for (int i = 0; i < width*height; i++) {
		int temp = (int)*(ybuff + i);
		*(freq + temp) = *(freq + temp) + 1;
	}

	// 写入txt
	ofstream Predict_sat;
	Predict_sat.open("Predict_sat.txt", ios::out, ios::trunc);
	if (!Predict_sat) {
		cout << "Error opening Predict_sat.txt" << endl;
	}
	else {
		Predict_sat << "symbol\tfreq" << endl;
		for (int i = 0; i < pow(2, N_bits); i++) {
			*(freq + i) = *(freq + i) / (double)(width * height);
			Predict_sat << i << "\t" << *(freq + i) << endl;
		}
	}
	Predict_sat.close();
	if (freq!=NULL) { delete[]freq; }
}
(7)将量化索引映射至0~255

将1bit、2bit、4bit量化后的量化区间索引映射至0~255,便于直接在图像中观察量化误差。

// 映射至8bit
void Map_To_8(int N_bits, int width,int height,unsigned char* ybuff) {
	double N = pow(2, N_bits);
		for (int i = 0; i < width* height; i++) {
			*(ybuff + i) =(unsigned char) (*(ybuff + i) * (255.0 / N) + (255 / (2 * N)+0.5));
		}
}

三、实验结果

原始图像

截屏2020-05-03下午1.02.54
截屏2020-05-03下午1.03.50截屏2020-05-03下午1.04.04
截屏2020-05-03下午1.04.52截屏2020-05-03下午1.05.04
截屏2020-05-03下午1.05.38截屏2020-05-03下午1.05.50
截屏2020-05-03下午1.06.22截屏2020-05-03下午1.06.35
image-20200503125917917image-20200503125838429
image-20200503125555322image-20200503125738685

将原始图像文件输入Huffman编码器后,压缩比为33.83%

量化比特数PSNR压缩比
1bit10.924516.78%
2bit16.908718.83%
4bit29.29119.37%
8bit51.158727.97%

由表可知,8bit量化的图像质量最好但压缩效率最低。1bit量化的图像质量最差但压缩效率最高。
当量化比特数过低时,图像出现颗粒杂波、边缘忙乱和伪轮廓失真。

四、完整代码

// Lab_4_DPCM.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//


#include "DPCM.h"
int main(int argc, char* argv[])
{
    int N_bits=8;
    int Width = 500;
    int Height = 500;
    unsigned char* ybuff = NULL, * ubuff = NULL, * vbuff = NULL;
    // 原始图像buffer
    ybuff = new unsigned char[Width * Height];
    ubuff = new unsigned char[Width * Height];
    vbuff = new unsigned char[Width * Height];

    // 预测误差buffer
    unsigned char* ybuff_pre = NULL; 
    int* ybuff_pre_d = NULL;
    ybuff_pre = new unsigned char[Width * Height];
    ybuff_pre_d = new int[Width * Height];
    // 重建图像buffer
    unsigned char* ybuff_recon = NULL;
    ybuff_recon = new unsigned char[Width * Height];

    // 重建值和与实际值误差buffer
    int *val_error = NULL;
    val_error = new int[Width * Height];

    ifstream ImageFile;
    ImageFile.open("seed.yuv", ios::in | ios::binary);
    if (!ImageFile.is_open()) {
        cout << "ImageFile open failed." << endl;
    }

    ImageFile.read((char*)ybuff, Width * Height);
    ImageFile.read((char*)ubuff, Width * Height);
    ImageFile.read((char*)vbuff, Width * Height);
    // 计算原始图像Y概率分布,输出至txt
    img_freq(ybuff, Width, Height);

    // 将第一列全部写入重建图像
    for (int i = 0; i < Height; i++) {
        ybuff_recon[i * Width] = ybuff[i * Width];
        ybuff_pre_d[i * Width] = 0;
        ybuff_pre[i * Width] = Quantify(N_bits, ybuff_pre_d[i * Width]);
        val_error[i * Width] = 0;
    }

    // DPCM
    for (int j = 0; j < Height; j++) {
        for (int i = 1; i < Width; i++) {
            ybuff_pre_d[j * Width + i] =ybuff[j * Width + i] - ybuff_recon[j * Width + i - 1];
            ybuff_pre[j * Width + i] = Quantify(N_bits,ybuff_pre_d[j * Width + i]);
            int temp;
            temp = ybuff_recon[j * Width + i - 1] + Re_Quantify(N_bits,ybuff_pre[j * Width + i]);
            temp = temp > 255 ? 255 : temp;
            temp = temp < 0 ? 0 : temp;
            ybuff_recon[j * Width + i] = (unsigned char)temp;

            // 求解重建值和与实际值误差
            val_error[j * Width + i] = (int)ybuff_recon[j * Width + i] - (int)ybuff[j * Width + i];
        }
    }

    cout << "量化比特数:" << N_bits << endl;
    cout <<"PSNR = "<< Count_PSNR(8,Width,Height,val_error) << endl;
    // 计算预测图像Y概率分布, 输出至txt
    predict_freq(N_bits, Width, Height, ybuff_pre);
    // 将输出文件映射至8bit
    if (N_bits != 8) {
        Map_To_8(N_bits, Width, Height, ybuff_pre);
    }

    WriteYUV("Predict_img.yuv",Height, Width, ybuff_pre, ubuff, vbuff);

    WriteYUV("Recon_img.yuv",Height, Width, ybuff_recon, ubuff, vbuff);

    ImageFile.close(); 
    if (ybuff != NULL) { delete[]ybuff; }
    if (ubuff != NULL) { delete[]ubuff; }
    if (vbuff != NULL) { delete[]vbuff; }
    if (ybuff_pre != NULL) { delete[]ybuff_pre; }
    if (ybuff_pre_d != NULL) { delete[]ybuff_pre_d; }
    if (ybuff_recon != NULL) { delete[]ybuff_recon; }
    if (val_error != NULL) { delete[]val_error; }
}
#pragma once
#ifndef DPCM_H_
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <malloc.h>
#include <windows.h>
using namespace std;

void WriteYUV(const char* File_name, int Height, int Width, unsigned char* Y, unsigned char* U, unsigned char* V);

unsigned char Quantify(int N_Bits, int x);
int Re_Quantify(int N_Bits, unsigned char x);
double Count_PSNR(int N_bits, int width, int height, int* delta);
void img_freq(unsigned char* y, int width, int height);
void Map_To_8(int N_bits, int width, int height, unsigned char* ybuff);
void predict_freq(int N_bits, int width, int height, unsigned char* ybuff);
#endif // !DPCM_H_
#include "DPCM.h"
// 写入YUV文件444格式
void WriteYUV(const char* File_name,int Height, int Width, unsigned char* Y, unsigned char* U, unsigned char* V) {
	ofstream File_out;
	File_out.open(File_name, ios::binary, ios::trunc);
	if (!File_out) {
		cout << "Failed to open NewImage.yuv" << endl;
	}
	else {
		File_out.write((char*)Y, Height * Width);
		File_out.write((char*)U, Height * Width);
		File_out.write((char*)V, Height * Width);
		File_out.close();
	}
}

// 量化
unsigned char Quantify(int N_Bits,int x) {
	int temp = 0;
	switch (N_Bits) {
	case 1:
		temp = (int)((x + 255) / 255.5);
		break;
	case 2:
		temp = (int)((x + 255) / 127.75);
		break;
	case 4:
		temp = (int)((x + 255) / 31.9375);
		break;
	case 8:
		temp = (int)((x + 255) / 2.0);
		break;
	default:
		cout << "error" << endl;
		exit(0);
		break;
	}
	return (unsigned char)temp;
}

// 反量化
int Re_Quantify(int N_Bits, unsigned char x) {
	int temp = (int)x;
	switch (N_Bits) {
	case 1:
		temp = (int)((temp * 255.5 + 127.75) - 255 + 0.5);
		break;
	case 2:
		temp = (int)((temp * 127.5 + 63.75) - 255+0.5);
		break;
	case 4:
		temp = (int)((temp * 31.9375 + 15.96875) - 255+0.5);
		break;
	case 8:
		temp = (temp * 2) - 255;
		break;
	default:
		cout << "error" << endl;
		exit(0);
		break;
	}
	return temp;
}

// 计算PSNR
double Count_PSNR(int N_bits, int width,int height,int* delta) {
	double psnr = 0;
	double mse = 0;
	for (int i = 0; i < width*height; i++) {
		mse += pow(*(delta + i), 2);
	}
	mse = mse / (width * height);
	psnr = 10 * log10(pow((pow(2, N_bits) - 1), 2) / mse);
	return psnr;
}

// 计算原始图像Y概率分布,输出至txt
void img_freq(unsigned char* y,int width,int height) {
	int count_Y[256] = { 0 };
	double freq_Y[256] = { 0 };
	for (int i = 0; i < width*height; i++) {
		count_Y[*(y + i)]++;
	}
	ofstream Y_sat;
	Y_sat.open("Y_sat.txt", ios::out, ios::trunc);
	if (!Y_sat) {
		cout << "Error opening Y_sat.txt" << endl;
	}
	else {
		Y_sat << "symbol\tfreq" << endl;
		for (int i = 0; i < 256; i++) {
			freq_Y[i] = count_Y[i] / (double)(width * height);
			Y_sat << i << "\t" << freq_Y[i] << endl;
		}
	}
	Y_sat.close();
}

// 计算预测图像Y概率分布,输出至txt
void predict_freq(int N_bits, int width, int height, unsigned char* ybuff) {
	double* freq = new double[pow(2, N_bits)];
	for (int i = 0; i < pow(2, N_bits); i++) {
		*(freq + i) = 0;
	}
	for (int i = 0; i < width*height; i++) {
		int temp = (int)*(ybuff + i);
		*(freq + temp) = *(freq + temp) + 1;
	}

	// 写入txt
	ofstream Predict_sat;
	Predict_sat.open("Predict_sat.txt", ios::out, ios::trunc);
	if (!Predict_sat) {
		cout << "Error opening Predict_sat.txt" << endl;
	}
	else {
		Predict_sat << "symbol\tfreq" << endl;
		for (int i = 0; i < pow(2, N_bits); i++) {
			*(freq + i) = *(freq + i) / (double)(width * height);
			Predict_sat << i << "\t" << *(freq + i) << endl;
		}
	}
	Predict_sat.close();
	if (freq!=NULL) { delete[]freq; }
}
// 映射至8bit
void Map_To_8(int N_bits, int width,int height,unsigned char* ybuff) {
	double N = pow(2, N_bits);
		for (int i = 0; i < width* height; i++) {
			*(ybuff + i) =(unsigned char) (*(ybuff + i) * (255.0 / N) + (255 / (2 * N)+0.5));
		}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值