【编解码】差分编解码算法

 

 

自己做的编解码算法。

一、差分编码

1、图像源分类

原始图像序列分别标识为关键帧(Fkey)和参考帧(Frefer)。示例为每隔15帧标识为关键帧。将一帧关键帧与相连15帧参考帧编码存储为一个DIFF文件。

2、分块操作

将关键帧与参考帧中每帧图像分块为pixelBlockSize(8)* pixelBlockSize(8)。

3、计算差值图像

在当前的一个16帧图像序列中:Fkey;Frefer1;Frefer2;Frefer3;Frefer4;Frefer5;Frefer6;Frefer7;Frefer8;Frefer9;Frefer10;Frefer11;Frefer12;Frefer13;Frefer14;Frefer15。计算差值图像得到关键帧与差分序列:Fkey;Fdiff1;Fdiff2;Fdiff3;Fdiff4;Fdiff5;Fdiff6;Fdiff7;Fdiff8;Fdiff9;Fdiff10;Fdiff11;Fdiff12;Fdiff13;Fdiff14;Fdiff15。其中Fdiff1 = Frefer1 – Fkey;Fdiff1 = Frefer2 – Frefer1;依此类推得出差分序列。同时差值图像由所有差值像素块组成。

(1)标记图像像素块Mask

计算出一个图像像素块标记Mask,相同Mask[blockIndex] = 0,不同Mask[blockIndex]  = 1;设置了一个容差(tolerance = 10)。

int BlockOffset = 0;
for(int i = 0; i < pixelBlockSize; i++)
{
	for(int j = 0; j < pixelBlockSize*3; j++)
	{
		BlockOffset = i*pixelBlockSize*3 + j;
		//该像素块中任意像素的任意RGB通道的对应差值diff>容差torlerance则块不同。
		if( abs(preBlockData[BlockOffset] - nextBlockData[BlockOffset]) > 10)
		{
			Mask = 1;
		}
		else
		{
			Mask = 0;
		}
	}
}

差值图像像素块组成:块索引(UINT16);pixelBlockSize(8)* pixelBlockSize(8)*3个uchar结构块;不定长的short结构块。块索引标记以行储存方式的该块索引坐标。

(2)计算像素块数据DIFF存储结构

uchar结构和short结构计算如下。

int shortOffset = 0
int blockOffset = 0;
if(Mask[index])
{
	for(int i = 0; i < pixelBlockSize; i++)
	{
		for(int j = 0; j < pixelBlockSize*3; j++)
		{
			//差值diff在[-127,127]之间则diff  +=128转换为uchar类型存储在相应的uchar			块结构中,若diff < -127或者diff > 127则将其存储在后面的short块结构中,同				时uchar结构块的相应位置标记为0 
			if( abs(nextBlockData[blockOffset] - preBlockData[blockOffset]) <= 127)
			{
				uchar[blockOffset] = nextBlockData[blockOffset] -  
                               preBlockData[blockOffset] + 128;
			}
			else
			{
				uchar[blockOffset] =0;
				short[shortOffset] = nextBlockData[blockOffset] - 
                               preBlockData[blockOffset];
				shortOffset++;
			}
			
		}
	}
}

4、写DIFF文件头

typedef unsigned short UINT16;
struct ImgDifferCodeHeader
{
	char		migc[4];                        ///<DIFF
	UINT16	width;                          ///<图像宽
	UINT16    height;                    		///<图像高
	uchar		bitCount;  ///<像素位数,8-灰度图;24-真彩色;32-RGBA图像
	uchar		pixelBlockSize;						///<分块大小
	UINT16    pixelBlockDataSize;        	   ///<块像素字节大小
	uchar	    pixleBlockCoordinateIndexSize;	   ///<块索引坐标字节大小
	UINT16    imgDataBegin;            ///<0xD8FF表示图像数据段开始
	UINT16    imgDataEnd;             ///<0xD9ff表示图像数据段结束
};

5、写DIFF中关键帧数据

写入一个图像开始标识:0xD8FF;写入一个uchar类型的图像类型表示1,表示为关键帧。将图像所有pixelBlockSize(8)* pixelBlockSize(8)个像素块写入DIFF中,改图像为3通道,即每个块存储为 8* 8*3个uchar类型,每个像素的通道顺序为BGR。块存储顺序为行存储格式。最后写入图像结束标识:0xD9FF。

6、写DIFF中参考帧数据

写入一个图像开始标识:0xD8FF;写入一个uchar类型的图像类型表示0,表示为参考帧。写步骤3中计算出的标识为差异块的3个组成部分:索引,8*8*3个的uchar结构块,不定长的short结构块。继续写入下一个差异像素块,依此写完所有差异块,最后写入最后写入图像结束标识:0xD9FF。写完所有的参考帧,1个关键帧和15个参考帧存成一个DIFF文件。

7、根据上述方法将所有图像序列写成DIFF文件,完成差分编码工作。

二、差分解码

以解码一个DIFF文件为例进行说明。

下面首先给出一个解码流程图:

《【编解码】差分编解码算法》

 

1. 打开DIFF文件

    一个DIFF文件存有一个文件头、一帧关键帧数据、N帧参考帧数据(N可设)。其中每一帧图像还有附加信息参考前面差分编码中的5、6条所述。每次打开一个DIFF文件。

2. 读取DIFF文件头

    打开DIFF文件之后,首先读取文件头信息。根据读出的信息分配几块缓存:

    (1)存放关键帧数据的缓存,大小:width*height*3;乘3代表3通道

    (2)存放恢复的参考帧数据的缓存,大小:width*height*3;

    (3)存放差值数据块的缓存,大小:pixelBlockSize*  pixelBlockSize*3。

3. 读取图像开始标志0xD8FF

4. 读取帧信息标志

     1)关键帧:一次读取pixelBlockSize*3大小的数据存入关键帧缓存对应

               原图像行储存的位置直到读完所有关键帧数据。因为在写入

               数据时为以后压缩方便数据按块存储,这里恢复时需要转换

               成原来的行储存。

     2)参考帧:

    (1)读取像素块索引。

    (2)读取一个像素块,大小为pixelBlockSize*  pixelBlockSize*3的差值。

    (3)解码。

short diffR, diffG, diffB;
short keyR, keyG, keyB;
int lineOffset,RGBAOffset;
for (int i=0; i<pixelBlockSize; i++)
{
    RGBAOffset = 0;
	for (int j=0; j<pixelBlockSize*3; j+=3)
	{
		diffB = blockPixelDataRGB[i*pixelBlockSize*3+j];
		//若在该位置读取的uchar差值为0,则表示该位置对应的差值超过范围     
              -127~+127,需要在紧接该像素块后的位置读取存放为short型的差值
		if (diffB  == 0)
		{
			fread(&diffValueFromShort, sizeof(short), 1, m_globalFP);
		} 
		//否则,恢复该差值,计算差值时用uchar型存放+128,恢复时减去
		else
		{
			diffB -= 128;
		}
        //G、R通道类似
		......
		//恢复原图像R、G、B值
		keyB =参考帧缓存[dataOffset+lineOffset+RGBAOffset] + diffB;
		keyG =参考帧缓存[dataOffset+lineOffset+RGBAOffset+1] + diffG;
		keyR =参考帧缓存[dataOffset+lineOffset+RGBAOffset+2] + diffR;
		//比较计算时设有容差,因此恢复计算会有误差,造成超出范围,要做限制
		if (keyB > 255)
		{
			参考帧缓存[dataOffset+lineOffset+RGBAOffset] = 255;
		}
		else if (keyB < 0)
		{
			参考帧缓存[dataOffset+lineOffset+RGBAOffset] = 0;
		}
		else
		{
			参考帧缓存[dataOffset+lineOffset+RGBAOffset] =keyB;
		}
		//G、R通道类似
		......
		RGBAOffset += 4;
	}
	lineOffset += width * 4;
}

5. 读取图像开始标志0xD9FF

    若还未到文件末尾,则转向步骤3继续解码直到读完一个DIFF文件。

 

    原文作者:游程编码问题
    原文地址: https://blog.csdn.net/heiheiya/article/details/81226679
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞