找到一张采用霍夫曼通用DC,AC编码表的图片,提取出此图片的比特流准备对它解码,再反推怎样编码。
下图是此图片比特流前100个字节。解码是每次读一字节,对这8比特解码,如8比特不能解码,再读入一字节。因为霍夫曼表最多是16比特位编码,意思是说超过16位比特还没有被解码就是错误的。当然不一定都是8比特,有时候是2比特等,这就涉及到比特的移位等操作。但操作单位是1字节8比特。
图1
上传此图片文件
下面是霍夫曼4张通用表;
此图片帧全局(0xffc0)
ff ,c0 ,0 ,11 ,8 ,1 ,67 ,1 ,da ,3 ,1 ,22 ,0 ,2 ,11 ,1 ,3 ,11 ,1 ,
对全局头分析:
总长0x11=17位,8 代表采样精度是8位,1,67 是图片行数=1×256+0 x67=359行,1,da 代表图片列数:1x256+0xda=474, 3 代表图片的分量数:亮度Y,色度U,V 三个。
后9字节分为3组,每组3字节,第一组id为1(Y),第二为2(U),第三3(V),id号为第一字节,第3字节为每个分量采样的量化表id,第二个字节高4位代表水平采样个数,低4位垂直采样个数。
1,22,0 表示Y 在MCU中水平垂直隔有2个
2, 11 ,1 U 各一次
3, 11, 1 V 各一次
意思就是说:此图片采样的是YUV 422 格式,有2个Y,1个U,1个V。
------------------------------------------------
此图片扫描头 SOS (0xffda)
ff ,da ,0 ,c ,3 ,1 ,0 ,2 ,11 ,3 ,11 ,0 ,3f ,0 ,
0,c: 扫描头长度 0×256+0xc=12 字节
3: 3分量 ,Y,U,V
1,0,2,11,3,11 分位三组,每组2字节,第一个是id号
第二字节高4位是DC 号,低4位为AC 号
0 :代表Z排序是从0开始编号
3f: 0x3f=63 表示Z排序是63结束,最后一字节默认是0
有这些信息就可以解码了。
因为采用的是yuv422格式,那比特流开始就是2个Y,.紧跟1个U ,1个V,组成一个MCU。比特流以此MCU格式为单位循环直到结束。
首先是亮度DC解码,马上是亮度AC解码,再是第二个亮度DC解码,再是第二个亮度AC解码。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h>
int main(void) {
// unsigned char dc0[28]={0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0xa,0xb};
char y_dc(unsigned char len,int bit ){ //亮度DC
if((len==2)&&(bit==0b00)){
return 0;
}
if((len==3)&&(bit==0b010)){
return 1;
}
if((len==3)&&(bit==0b011)){
return 2;
}
if((len==3)&&(bit==0b100)){
return 3;
}
if((len==3)&&(bit==0b101)){
return 4;
}
if((len==3)&&(bit==0b110)){
return 5;
}
if((len==4)&&(bit==0b1110)){
return 6;
}
if((len==5)&&(bit==0b11110)){
return 7;
}
if((len==6)&&(bit==0b111110)){
return 8;
}
if((len==7)&&(bit==0b1111110)){
return 9;
}
if((len==8)&&(bit==0b11111110)){
return 10;
}
if((len==9)&&(bit==0b111111110)){
return 11;
}
else return -1;
}
//----------------------------------
char ali(char len,char i){ //ALI
char o;
if (len == 0) {
o = 0;
}
if ((len == 1) && (i == 0)) {
o = -1;
}
if ((len == 1) && (i == 1)) {
o = 1;
}
//--------------------------
if ((i >= pow(2, len - 1)) && (i <= pow(2, len))) {
o = i;
}
if ((i >= 0) && (i < pow(2, len - 1))) {
o = i - pow(2, len) + 1;
}
return o;
}
//------------------------------------------------------------------
unsigned char data[100]={0xf5,0xe6,0x24,0x4e,0xff,0,0x41,0xfd,0x68};
// 0xf5 , 0xe6 , 0x24
// 1 1 1 1 0 1 0 1 ,1 1 1 0 0 1 1 0,0 0 1 0 0 1 0 0
//11110 得到7,马上取7位 1011110=x 现在就可把7位加x,利用ALI 反退出真实的DC系数=94。
int a,b;
char c;
a=data[0]; //0xf5=0b11110101 5位=11110 输出7
for(int n=2;n<7;n++){
b=a;
b=b>>(8-n);
c=y_dc(n,b);
if(c>0) break;
}
//马上读7位,这7位不用转码
printf("%d\n",ali(7,0b1011110)); //94
return 0;
}
现在在验证转换的方法是否正确:对,正确!现在解码出亮度DC=94,马上就是对亮度AC解码。
下一步搞一个读取变长度比特流的通用方法。