DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,
需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是
因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实
际内嵌了一个解码器,如编码器中虚线框中所示。
二、实验步骤 1、基本流程 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为例,画出概率分布图如下:
(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 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)量化与反量化
依据设定的量化比特数,将-255~+255均匀划分为2N_bits个区间,取区间中值作为重构水平。
编码器将每个区间的索引发给解码器。
解码器用重构水平表示该区间内所有的值。
// 量化
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计算公式如下:
PSNR=10log10(MAX2MSE)
\text{} PSNR=10log_{10}\left( \frac{MAX^{2}}{MSE} \right)
PSNR=10log10(MSEMAX2)
MSE=1mn∑i=1n∑j=1m∣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=1∑nj=1∑m∣K(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));
}
}
三、实验结果
原始图像
将原始图像文件输入Huffman编码器后,压缩比为33.83%
量化比特数 | PSNR | 压缩比 |
---|---|---|
1bit | 51.1587 | 16.78% |
2bit | 29.291 | 18.83% |
4bit | 16.9087 | 19.37% |
8bit | 10.9245 | 27.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 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
#include
#include
#include
#include
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));
}
}
柠檬树上柠檬果
原创文章 9获赞 1访问量 581
关注
私信
展开阅读全文