資料壓縮實驗3——DPCM編碼
- 一、DPCM原理
- 二、代碼實作
-
- 1、重要代碼講解
- 2、整體代碼展示
- 三、實驗結果
一、DPCM原理
預測編碼是根據某一模型利用以往的樣本值對于新樣本值進行預測,然後将樣本的實際值與其預測值相減得到一個誤內插補點,對于這一誤內插補點進行編碼。如果模型足夠好且樣本序列在時間上相關性較強,那麼誤差信号的幅度将遠遠小于原始信号,進而得到較大的資料壓縮結果。
預測編碼方法分線性預測和非線性預測編碼方法。線性預測編碼方法,也稱內插補點脈沖編碼調制法,簡稱DPCM。
encoder圖中輸入信号Xn是某一像素點的實際灰階值,Pn是對該像素點的預測值,在本實驗中就是與它相鄰的前一個像素點的重建值。dn 是預測誤差。(dn)是量化預測誤差,(Xn)=量化預測誤差^(dn)+預測灰階值Pn,得到目前像素點的重建值,作為下一個像素點的預測值。
預測器的輸入是已經解碼以後的樣本,因為在解碼端隻能得到存在誤差的樣本。
由圖可見,encoder中包含了一個decoder
編碼框圖(encoder)
解碼框圖(decoder)
二、代碼實作
在這裡我實作了左向預測8bit
1、重要代碼講解
因為是左向預測,是以圖檔中每一行的左側第一個值沒有前面的值,是以直接将預測值設為本身的值,插值設為0。
if (j == 0)
{
yForecastBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
yDifferenceBuf[j + i * frameWidth] = 0;
}
如果不是每一行的第一個值,那就需要先先計算插值a,将這個像素值減去它左邊像素的預測值。但是因為圖像是8bit有256個值,是以如果相減值就會變為512個值,是以如果想進行8bit量化,需要将插值進行變換,本像素插值d=a/2+127(量化插值b=a/2)。之後需要進行反量化值c,将量化插值b乘2(c=b*2)。之後再将反量化值c與左側像素預測值相加得到本像素預測值
a = yBuf[j + i * frameWidth] - yForecastBuf[j + i * frameWidth - 1];//計算插值
b = a / 2;//進行量化
c = b * 2;//進行反量化
d = b + 128;//進行+128
yDifferenceBuf[j + i * frameWidth] = d;
yForecastBuf[j + i * frameWidth] = yForecastBuf[j + i * frameWidth - 1] + c;
2、整體代碼展示
#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* yForecastBuf = NULL;
u_int8_t* yDifferenceBuf = NULL;
u_int8_t* Buf128 = NULL;
FILE* yuvFile = NULL;
FILE* differenceFile = NULL;
FILE* forecastFile = NULL;
u_int frameWidth = 0;
u_int frameHeight = 0;
int a = 0;//量化前插值
int b = 0;//量化後值
int c = 0;//反量化後值
int d = 0;//+128之後值
//讀取資料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);
yForecastBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
yDifferenceBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
Buf128 = (u_int8_t*)malloc(frameWidth * frameHeight / 2);
fread(yuvBuf, 1, frameWidth * frameHeight * 3 / 2, yuvFile);
forecastFile = fopen(yuceFileName, "wb");
differenceFile = 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];
}
//計算內插補點與預測值
for (int i = 0; i < frameHeight; i++)
{
for (int j = 0; j < frameWidth; j++)
{
if (j == 0)
{
yForecastBuf[j + i * frameWidth] = yBuf[j + i * frameWidth];
yDifferenceBuf[j + i * frameWidth] = 0;
}
else
{
a = yBuf[j + i * frameWidth] - yForecastBuf[j + i * frameWidth - 1];//計算插值
b = a / 2;//進行量化
c = b * 2;//進行反量化
d = b + 128;//進行+128
yDifferenceBuf[j + i * frameWidth] = d;
yForecastBuf[j + i * frameWidth] = yForecastBuf[j + i * frameWidth - 1] + c;
}
}
}
fwrite(yForecastBuf, frameWidth * frameHeight, 1, forecastFile);
fwrite(yDifferenceBuf, frameWidth * frameHeight, 1, differenceFile);
fwrite(Buf128, frameWidth * frameHeight / 2, 1, differenceFile);
fwrite(Buf128, frameWidth * frameHeight / 2, 1, forecastFile);
fclose(differenceFile);
fclose(forecastFile);
fclose(yuvFile);
free(yBuf);
free(yuvBuf);
free(yForecastBuf);
free(yDifferenceBuf);
free(Buf128);
return 0;
}
三、實驗結果
預測圖像
插值圖像
通過插值圖像可以看出,在模特肩膀部位插值圖像為一條黑色曲線,這是因為右側像素比左側像素黑,也就是右側像素值小于左側像素值,是以右像素-左像素為負值是以為黑。這樣也就能根據插值圖像知到為左向預測。