c MJPG

yuv格式的照片是纯yuv的数据,如果不告诉图片查看程序此数据流的长与宽,是无法显示图片的。

MJPG是由多帧jpg图片组成。jpg图片有文件头,里面就有必须的长,宽数据。jpg的图片数据是yuv压缩后的数据。所以jpg解码后的数据也是yuv,也必须转为RGB32显示器才能显示。

1.   查看文件头字节:

     



#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <linux/fb.h>
#include <stdlib.h>



int main(void)
{
    FILE *f=fopen("/home/wzpc/Pictures/my.jpg","rb");
	if(f==NULL){
		puts("file error");
		exit(1);
	}
	fseek(f,0,SEEK_END);
	int len=ftell(f);
	fseek(f,0,SEEK_SET);
	int ft=fileno(f);
	char *fb=mmap(NULL,len,PROT_READ,MAP_SHARED,ft,0);
	
	unsigned char (*c)[16]=(unsigned char (*)[16])fb;
	
	for(int a=0;a<40;a++){
		for(int b=0;b<16;b++){
			printf("%x  ",c[a][b]);
		}
		puts("");
	}
	
	
	return 0;
}

3.c摄像头 生成jpg图片

ff  d8  ff  db  0  43  0  5  3  3  4  3  3  5  4  3                    ff d8:jpg 开始    ff db 0 43 0:量化表1开始  64字节   8*8 
4  5  5  5  5  7  c  8  7  6  6  7  f  a  b  8  
c  12  f  12  12  11  f  11  10  14  16  1c  18  14  15  1a  
15  10  11  19  21  19  1a  1d  1e  1f  20  1f  13  17  23  25               
23  1f  25  1c  1f  1f  1e  ff  db  0  43  1  5  5  5  7               ff db 0 43 1: 量化表2开始       64字节   8*8
6  7  e  8  8  e  1e  14  11  14  1e  1e  1e  1e  1e  1e  
1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  
1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  
1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  1e  ff  c0  0  11           ff c0:量化表后面有一个“开始帧(SOF)”,由“FFC0”等标记表示。据说图像的大小是在SOF中定义的。
8  2  d0  5  0  3  1  21  0  2  11  1  3  11  1  ff                     ff c4:以标记“FFC4”开头的“霍夫曼表”是一步压缩数据的霍夫曼编码
c4  0  1f  0  0  1  5  1  1  1  1  1  1  0  0  0                        ff c4 0 1f:  0x1f=31=29+2   所以数据长度为29个
0  0  0  0  0  1  2  3  4  5  6  7  8  9  a  b  
ff  c4  0  b5  10  0  2  1  3  3  2  4  3  5  5  4                      ff c4 0 b5:  0xb5=16*11+5=181=179+2   数据长度为179
 0  0  1  7d  1  2  3  0  4  11  5  12  21  31  41  
6  13  51  61  7  22  71  14  32  81  91  a1  8  23  42  b1  
c1  15  52  d1  f0  24  33  62  72  82  9  a  16  17  18  19  
1a  25  26  27  28  29  2a  34  35  36  37  38  39  3a  43  44  
45  46  47  48  49  4a  53  54  55  56  57  58  59  5a  63  64        
6  67  68  69  6a  73  74  75  76  77  78  79  7a  83  84  
85  86  87  88  89  8a  92  93  94  95  96  97  98  99  9a  a2  
a3  a4  a5  a6  a7  a8  a9  aa  b2  b3  b4  b5  b6  b7  b8  b9  
ba  c2  c3  c4  c5  c6  c7  c8  c9  ca  d2  d3  d4  d5  d6  d7  
d8  d9  da  e1  e2  e3  e4  e5  e6  e7  e8  e9  ea  f1  f2  f3  
f4  f5  f6  f7  f8  f9  fa  ff  c4  0  1f  1  0  3  1  1               ff c4 0 1f:
  1  1  1  1  1  0  0  0  0  0  0  1  2  3                 
4  5  6  7  8  9  a  b  ff  c4  0  b5  11  0  2  1                      ff c4 0 b5:
4  4  3  4  7  5  4  4  0  1  2  77  0  1  2  

4. SOF (ff c0) 帧开始的定义
    标记代码(2字节)   0xffc0
       数据长度   2      sof 长度
       精度      1      每个样本数据的位数 ,通常是8
       图像高度   2      单位是像素点
       图像宽度   2       单位是像素点
       颜色分级   1       YCbCr 3
       颜色分量信息  颜色分量数X3(一般为9字节)     每个颜色分量: 1字节分量id  1字节水平垂直采样因子  1字节分量使用的量化表id

      ff  c0   0   11   8   2  d0   5   0   3   1   21   0   2   11   1  3    11   1 
      sof数据`长度  0x11=17-2=15个字节
      图像高度  [2][d0]=2*256+13*16=720
      图像高度  [5][0]=5*256+0=1280
      颜色分级  3: YCbCr
      分量信息:
                   id          采样因子         使用的量化表id
        Y  :        1           21                0 
        Cb          2           11                1
        Cr          3           11                1
         
5.
标记名字        标记识别符              说明
SOI            0xd8                jpg图像开始
APP0           0xe0               JFIF应用领域
               0xe1-0xef          其他APP细分市场
              0xdb                量化表
              0xc0                 帧开始
              0xc4                霍夫曼表
              0xda                 扫描行开始
              0xd9                jpg图片结束
到这里基本上就得到了jpg图片的基本数据,剩下的就是用代码把它解码还原成YCbCr。

文件头最重要必须的数据结构有3种类型:4张向量表,SOF 帧数据,多个霍夫曼编码数据。得到这三种类型数据后,就可把编码数据解码成YUV格式数据了。

//----------------------------------------------------------------

摄像头生成的jpg文件结构如下:

0xff d8   开始

0xff db

oxff db      两张量化表

0xff c0       帧全局参数

0x ff   c4

0xff    c4

0xff   c4

0xff  c4     4张范式霍夫曼表

0xff   dd     差分编码累计复位间隔

0x ff   da     扫描数据开始

0xff   d9       结束

----------------------------------------------------------

开始从 范式霍夫曼解码开始下手。

下面是4张霍夫曼表数据

  ff  c4  0  1f  0  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  a  b    

        ff  c4  0  b5  10  0  2  1  3  3  2  4  3  5  5  4  4  0  0  1  7d  1  2  3  0  4  11  5  12  21  31  41  
6  13  51  61  7  22  71  14  32  81  91  a1  8  23  42  b1  c1  15  52  d1  f0  24  33  62  72  82  9  a  16  17  18  19  
1a  25  26  27  28  29  2a  34  35  36  37  38  39  3a  43  44  45  46  47  48  49  4a  53  54  55  56  57  58  59  5a  63  64  
65  66  67  68  69  6a  73  74  75  76  77  78  79  7a  83  84  85  86  87  88  89  8a  92  93  94  95  96  97  98  99  9a  a2  
a3  a4  a5  a6  a7  a8  a9  aa  b2  b3  b4  b5  b6  b7  b8  b9  ba  c2  c3  c4  c5  c6  c7  c8  c9  ca  d2  d3  d4  d5  d6  d7  
d8  d9  da  e1  e2  e3  e4  e5  e6  e7  e8  e9  ea  f1  f2  f3  f4  f5  f6  f7  f8  f9  fa 

        ff  c4  0  1f  1  0  3  1  1  
1  1  1  1  1  1  1  0  0  0  0  0  0  1  2  3  4  5  6  7  8  9  a  b 

         ff  c4  0  b5  11  0  2  1  
2  4  4  3  4  7  5  4  4  0  1  2  77  0  1  2  3  11  4  5  21  31  6  12  41  51  7  61  71  13  22  32  
81  8  14  42  91  a1  b1  c1  9  23  33  52  f0  15  62  72  d1  a  16  24  34  e1  25  f1  17  18  19  1a  26  27  28  29  
2a  35  36  37  38  39  3a  43  44  45  46  47  48  49  4a  53  54  55  56  57  58  59  5a  63  64  65  66  67  68  69  6a  73  
74  75  76  77  78  79  7a  82  83  84  85  86  87  88  89  8a  92  93  94  95  96  97  98  99  9a  a2  a3  a4  a5  a6  a7  a8  
a9  aa  b2  b3  b4  b5  b6  b7  b8  b9  ba  c2  c3  c4  c5  c6  c7  c8  c9  ca  d2  d3  d4  d5  d6  d7  d8  d9  da  e2  e3  e4  
e5  e6  e7  e8  e9  ea  f2  f3  f4  f5  f6  f7  f8  f9  fa

--------------------------------------------------------------------

ff  c4  0  1f  0  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  a  b                // ff c4  0  1f  0   (ff c4  0)固定,1f 长度  0:表id

 n0  n1  n2  n3  n4  n5   n6  n7  n8  n9  n10  n11  n12  n13  n14  n15         //生成编码长度 1-16位

  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  a  b  (从0-b)是要被编的字符 总共12个

范式霍夫曼三个规则:

最小编码长度的第一个编码必须从0开始。
相同长度编码必须是连续的。
编码长度为 j 的第一个符号可以从编码长度为 j − 1 的最后一个符号所得知,即 c( j) = 2 ∗ ( c (j − 1) + 1 ) 


        编码位数     被编码个数        生成范式霍夫曼编码 (括号内是被编码数)
n0: 0   表示1位      0个           
n1: 1   表示2位      1个               00(0)
n2: 5   表示3位      5个               010(1) , 011(2),100(3),101(4),110(5)
n3: 1     4         1                1110 (6)
n4: 1     5         1                11110 (7)
n5: 1     6         1                111110 (8)
N6: 1     7         1                1111110(9)
n7: 1     8         1                11111110(a)
n8: 1     9         1                111111110(b)

现在就体会了压缩的好处:平时如果是char,存储0要用8bit位,现在只用2个bit位00  就行了。

现在有一个问题,用这种位数不同的编码怎样存入内存,程序又是怎样区分哪几位是代表0,哪几位是代表1  难道不同位数之间又要加分隔符号?

下面的这段话似乎解释了上面的问题。

霍夫曼编码后,二进制位数据是连续的,中间没有分隔符。需要保证各个符号编码不会冲突,也就是说,不会存在某一个编码是另一个编码的前缀。

把霍夫曼码流解码的方法是:从2bit位开始,先读两位bit位,去比对范式霍夫曼编码表中二位的编码,如有,就确定,如没有,就再读一bit位,再去比对3位码表查找,,,,依次循环,直到读到真实唯一的被编码数。所以不存在用分隔符的问题。

所以解码的过程就是依次+1bit读取被编码流去对比码表的过程。

现在正考虑怎样进行bit位操作,因为霍夫曼编码是变长位的数据,c中除了一个位字段操作以外,就没有任何对位数据的操作了。我在想如果有一个办法把扫描的霍夫曼编码流全部转为bit流,那操作就容易多了。(现在我们用的mmap操作的是字节流,如有办法能用mmap操作bit流就完美了)。

利用位字段把整数转为二进制



#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <linux/fb.h>


int main(void) {
	unsigned char t=201;
	
	typedef	struct {
		unsigned char b0:1;
		unsigned char b1:1;
		unsigned char b2:1;
		unsigned char b3:1;
		unsigned char b4:1;
		unsigned char b5:1;
		unsigned char b6:1;
		unsigned char b7:1;
	} bit;

    bit ss;
	memcpy(&ss,&t,1);
	printf("%d\n",ss.b0);
	printf("%d\n",ss.b1);
    printf("%d\n",ss.b2);
	printf("%d\n",ss.b3);
	printf("%d\n",ss.b4);
	printf("%d\n",ss.b5);
	printf("%d\n",ss.b6);
	printf("%d\n",ss.b7);
	return 0;
}

利用位字段把连续的字节流转为比特流



#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <linux/fb.h>


int main(void) {
	
	
	typedef	struct {
		unsigned char b0:1;
		unsigned char b1:1;
		unsigned char b2:1;
		unsigned char b3:1;
		unsigned char b4:1;
		unsigned char b5:1;
		unsigned char b6:1;
		unsigned char b7:1;
	} bit;

    bit ss;
	
    unsigned char p[3]={254,254,254};   //内存中连续存储的以字节为单位的数据

    unsigned char o[30]={0};           //以字节为单位用来存储一个比特的数据
	unsigned char *sp=o;               //数组o 中的数据就是p 的连续的比特数

	for(int n=0;n<3;n++){              
//	 memset(&ss,0,1);
     memcpy(&ss,p+n,1);              //把一个字节的数据赋值给struct bit
		
	*(sp+8*n+0)=ss.b7;               //把位赋值给字节存储
	*(sp+8*n+1)=ss.b6;               //反序排列的
	*(sp+8*n+2)=ss.b5;           
	*(sp+8*n+3)=ss.b4;
	*(sp+8*n+4)=ss.b3;
	*(sp+8*n+5)=ss.b2;
	*(sp+8*n+6)=ss.b1;
	*(sp+8*n+7)=ss.b0;
	}
    
	for(int c=0;c<24;c++){
			printf("%d ",o[c]);  //1 1 1 1 1 1 1 0    1 1 1 1 1 1 1 0    1 1 1 1 1 1 1 0 
	}
		
	return 0;
}

此比特流只能用于判断,因为现在是用8位的char 代替一位的bit数据。只有一个好处,可以用指针连续跟踪查询比特数据。省去几位bit数据在两个或多个字节中的拼接。

如果想把此char还原成一位的真实比特流,应该可以采用移位,再8位与运算拼接成一个字节的数据。

还有一个问题,因为c 的数组下标是有大小限制的,所以如遇到大数据,最好用动态分配内存malloc。

到目的为止,就可以用上面的程序把霍夫曼编码流转化为连续的比特流(用一个char表示一位bit)。按照霍夫曼的编码循环读取查询还原出真实的z字扫描数据。

猜你喜欢

转载自blog.csdn.net/m0_59802969/article/details/134617573