自己做的编解码算法。
一、差分编码
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文件。