1七、MJPG编码和AVI封装

1、JPEG和MJPG编码介绍算法

 一、JPEG编码数组

我我的简单的理解是,JPEG便是Joint Photographic Experts Group(联合图像专家组)的缩写,更是一种图像压缩编码算法。JPEG编码算法过程简单能够归结于下:其中DCT变换和量化是有损的,而熵编码(通常是哈夫曼编码)是无损的。量化和编码均可以经过量化表和编码表查询获得。函数

二、MJPG编码测试

Motion JPEG是一种基于静态图像JPEG 压缩标准的动态图像压缩标准,压缩时将连续图像的每个帧视为一幅静止图像进行压缩,从而能够生成序列化运动图像。压缩时不对帧间的时间冗余进行压缩,也就是说没有MJPEG或者H264那样的帧间编码,阵内预测编码也没有,因此较于实现,只是压缩效率较低。一个MJPG帧序列能够看出是N帧JPEG编码后的数据流链接而成。ui

 

2、基于MJPG编码的AVI视频封装介绍this

AVI是一种RIFF(Resource Interchange File Format)文件格式,多用于音视频捕捉、编辑、回放等应用程序。AVI包含三个部分:文件头、数据块和索引块。其中文件头包括文件的通用信息,定义数据格式,所用的压缩算法等参数。数据块包含实际数据流,即图像和声音序列数据。这是文件的主体,也是决定文件容量的主要部分。视频文件的大小等于该文件的数据率乘以该视频播放的时间长度。索引块包括数据块列表和它们在文件中的位置,以提供文件内数据随机存取能力。AVI文件的整体结构:编码

 

3、ZED上JPEG编码实现spa

整个编码过程比较繁琐,这里只作简单介绍。后续若是有时间,专门开辟一篇博客介绍JPEG编码过程。3d

一、主编码指针

 1 void mjpg::jpeg_encode(unsigned char **yuv_buffer_pointer)  2 {  3     unsigned int remnant;  4     yuv_p = *yuv_buffer_pointer;  5 
 6         bitstring fillbits; //filling bitstring for the bit alignment of the EOI marker  7 
 8         //fp_jpeg_stream=fopen("000.jpg","wb");
 9         jpgsize = 0; 10         // 505 bytes
11         writeword(0xFFD8); // SOI 2
12         write_APP0info();//JIFF 13         // write_comment("Cris made this JPEG with his own encoder");
14         write_DQTinfo();//= 0xFFDB
15         write_SOF0info();//FFC0
16  write_DHTinfo(); 17  write_SOSinfo(); 18 
19         //jpgsize = 505; 20 
21         // init global variables
22         bytenew = 0; // current byte
23         bytepos = 7; // bit position in this byte
24  main_encoder(); 25 
26         // Do the bit alignment of the EOI marker
27         if (bytepos >= 0) 28  { 29                 fillbits.length = bytepos + 1; 30                 fillbits.value = (1<<(bytepos+1)) - 1; 31  writebits(fillbits); 32  } 33         writeword(0xFFD9); // EOI 34 
35         //remnant = (~(jpgsize&0x00000003))&0x00000003;// important
36         remnant = (4-(jpgsize&0x00000003))&0x00000003;// important
37         jpgsize = jpgsize + remnant; 38         movisize = movisize + jpgsize; 39         while(remnant > 0) 40  { 41             fputc(0,avi_jpeg_stream); 42             remnant--; 43  } 44 }

其中remnant是一个计算一帧图像大小的余数,由于后续AVI封装要求每帧图像大小都是4的整数倍。

二、获取8x8阵列数据

每次处理都是以8x8大小的数据矩阵进行处理的。因为USB摄像头采集到的图像数据是YUV422打包格式,而JPEG编码中比较多的是使用YUV411,因此优先考虑将其转换。其中yuv_p是原始YUV422图像数据指针,YDU1~YDU4是四个连续存储Y份量大小为64字节的数组,CbDU和CrDU分别为Cb和Cr份量。为了提升计算效率,乘法均由移位完成

 1 void mjpg::load_data_units_from_YUV_buffer(WORD xpos, WORD ypos)  2 {  3  BYTE x, y;  4         BYTE pos = 0;  5  DWORD location;  6        // SBYTE Cr_temp,Cb_temp;  7 
 8         //location = ypos * 640+ xpos;
 9         location = (ypos<<7) + (ypos<<9) + xpos; 10         for (y=0; y<8; y++) 11  { 12             for (x=0; x<8; x++) 13  { 14                 YDU1[pos] = *(yuv_p+((location)<<1)) -128; 15                 YDU2[pos] = *(yuv_p+((location+8)<<1)) -128; 16                 YDU3[pos] = *(yuv_p+((location+5120)<<1)) -128;//location = (ypos+8) * 640+ xpos+8;
17                 YDU4[pos] = *(yuv_p+((location+5128)<<1)) -128;//location = (ypos+8) * 640+ xpos+8;
18                 pos++; 19                 location++; 20  } 21             location += 632;//640 - 8;
22  } 23 
24         pos = 0; 25         //location = ypos * 640+ xpos;
26         location = (ypos<<7) + (ypos<<9) + xpos; 27         for (y=0; y<8; y++) 28  { 29             for (x=0; x<8; x++) 30  { 31                 CbDU[pos] = *(yuv_p+(location)*2+1)-128; 32                 CrDU[pos] = *(yuv_p+(location+1)*2+1)-128; 33                 pos++; 34                 location++; 35                 location++; 36  } 37             location += 1264;//640*2 - 16;
38  } 39 }

三、对每一个8x8数据阵列进行JPEG处理

void mjpg::main_encoder() { SWORD DCY = 0, DCCb = 0, DCCr = 0; //DC coefficients used for differential encoding
 WORD xpos, ypos; for (ypos=0; ypos<IMG_HEIGTH; ypos+=16) { for (xpos=0; xpos<(IMG_WIDTH); xpos+=16) { load_data_units_from_YUV_buffer(xpos, ypos); process_DU(YDU1, fdtbl_Y, &DCY, YDC_HT, YAC_HT); process_DU(YDU2, fdtbl_Y, &DCY, YDC_HT, YAC_HT); process_DU(YDU3, fdtbl_Y, &DCY, YDC_HT, YAC_HT); process_DU(YDU4, fdtbl_Y, &DCY, YDC_HT, YAC_HT); process_DU(CbDU, fdtbl_Cb, &DCCb, CbDC_HT, CbAC_HT); process_DU(CrDU, fdtbl_Cb, &DCCr, CbDC_HT, CbAC_HT); } } }

4、ZED上MJPG的编码实现以及AVI封装

 写avi文件第一步是写hdrl头信息,但是hdrl头信息须要肯定文件的总帧数和文件大小,而在采集过程当中这些都是不肯定的(由于不知道何时采集结束),为此采用了一个“偷懒”方法:先写一个虚假的hdrl,而后每次对一帧图像进行JPEG编码后,将图像的数据量mjpgfile->movisize记录下来,并将数据帧数framecnt记录下来。中止采集后先结束avi文件的写入,再从新打开,而后对文件头进行修改;或者经过fseek寻找的头文件位置,一样修改hdrl信息。两种方法我都试过,感受效率都差很少。

为了方便采集,添加按键来触发改变须要的状态,定义state为3个状态:

state--含义
0------idle,等待采集 1------正在采集
2------结束采集

state为0时,标明须要准备写一个新的avi文件;state为1时,标明如今正在采集图像数据,并对每一帧进行jpeg编码;state为2时,标明采集已经结束,fseek往回修改头文件。新的paintEvent函数:

 1 void Widget::paintEvent(QPaintEvent *)  2 {  3     rs = vd->get_frame(&yuv_buffer_pointer,&len);  4 
 5     if(last_state==2 && state == 0)  6  {  7         //write hdrl
 8         hdrl.avih.width =640;// (width);
 9         hdrl.avih.height = 480;//(height);
10         hdrl.strl.strf.width = 640;//(width);
11         hdrl.strl.strf.height = 480;//(height);
12         hdrl.strl.strf.image_sz = 640*480*3;//(width * height * 3);
13 
14 
15         sizeofhdrl=sizeof(hdrl); 16 
17         mjpgfile->avi_jpeg_stream = fopen(avifilename, "wb"); 18 
19         fputc('R', mjpgfile->avi_jpeg_stream); 20         fputc('I', mjpgfile->avi_jpeg_stream); 21         fputc('F', mjpgfile->avi_jpeg_stream); 22         fputc('F', mjpgfile->avi_jpeg_stream); 23         print_quartet(0/*riff_sz*/);//riff file size
24         fputc('A', mjpgfile->avi_jpeg_stream); 25         fputc('V', mjpgfile->avi_jpeg_stream); 26         fputc('I', mjpgfile->avi_jpeg_stream); 27         fputc(' ', mjpgfile->avi_jpeg_stream); 28 
29         fwrite(&hdrl, sizeofhdrl, 1, mjpgfile->avi_jpeg_stream);// write head
30 
31         fputc('L', mjpgfile->avi_jpeg_stream); 32         fputc('I', mjpgfile->avi_jpeg_stream); 33         fputc('S', mjpgfile->avi_jpeg_stream); 34         fputc('T', mjpgfile->avi_jpeg_stream); 35 
36         print_quartet(0/*jpg_sz + 8*TOTALFRAMES + 4*/);// size again
37         fputc('m', mjpgfile->avi_jpeg_stream); 38         fputc('o', mjpgfile->avi_jpeg_stream); 39         fputc('v', mjpgfile->avi_jpeg_stream); 40         fputc('i', mjpgfile->avi_jpeg_stream); 41 
42         avifilename[5]++; 43  } 44 
45     if(state==1) 46  { 47         framecnt++; 48         fputc('0', mjpgfile->avi_jpeg_stream); 49         fputc('0', mjpgfile->avi_jpeg_stream); 50         fputc('d', mjpgfile->avi_jpeg_stream); 51         fputc('c', mjpgfile->avi_jpeg_stream); 52         print_quartet(0); 53 
54         mjpgfile->jpeg_encode(&yuv_buffer_pointer); 55 
56         //printf("%ld\n",mjpgfile->jpgsize);
57 
58         fseek(mjpgfile->avi_jpeg_stream,-4-(long)mjpgfile->jpgsize,SEEK_CUR); 59         print_quartet(mjpgfile->jpgsize); 60 
61         fseek(mjpgfile->avi_jpeg_stream,6,SEEK_CUR); 62         fwrite("AVI1",4, 1, mjpgfile->avi_jpeg_stream); 63 
64         fseek(mjpgfile->avi_jpeg_stream,mjpgfile->jpgsize-10,SEEK_CUR); 65 
66 
67  } 68     if(last_state==1 && state==2) 69  { 70 
71         fseek(mjpgfile->avi_jpeg_stream,4,SEEK_SET); 72         print_quartet(mjpgfile->movisize+sizeofhdrl);//riff file size
73         fseek(mjpgfile->avi_jpeg_stream,4,SEEK_CUR); 74 
75         //overwrite hdrl
76         hdrl.avih.us_per_frame = 1000000/12;//(per_usec);
77         hdrl.avih.max_bytes_per_sec = mjpgfile->movisize*12/framecnt; 78         hdrl.avih.tot_frames = framecnt; 79         hdrl.strl.list_odml.frames =framecnt;// (TOTALFRAMES);
80         hdrl.strl.strh.scale = 1;// 81         hdrl.strl.strh.length =10;// 82         hdrl.strl.strh.rate = 12; 83 
84         fwrite(&hdrl, sizeofhdrl, 1, mjpgfile->avi_jpeg_stream);// write head
85         fseek(mjpgfile->avi_jpeg_stream,4,SEEK_CUR); 86 
87         print_quartet(mjpgfile->movisize);// size again
88         fclose(mjpgfile->avi_jpeg_stream); 89  } 90     last_state=state; 91 
92  convert_yuv_to_rgb_buffer(); 93 
94     frame->loadFromData(rgb_buffer,640 * 480 * 3); 95     ui->label->setPixmap(QPixmap::fromImage(*frame,Qt::AutoColor)); 96 
97 
98     rs = vd->unget_frame(); 99 }

最开始定义了视频名字的数组,char avifilename[11] = {'r','c','q','0','0','0','.','a','v','i','\0'};

在42行:avifilename[5]++;

表示让名字由"rcq000.avi"依次计数增长。

5、测试效果

 

可执行程序:

相关文章
相关标签/搜索