【OpenCV图像处理入门学习教程一】OpenCV2 + 3的安装教程与VS2013的开发环境配置 + JPEG压缩源码分析与取反运算修改
2018-01-26 13:05
1296 查看
OpenCV简介
OpenCV官网在此OpenCV是大名鼎鼎的开源计算机视觉库,想做图像处理又不想用OpenCV的,不是大佬就是那啥,你懂的……官网是这么介绍自己的:OpenCV(开源计算机视觉库)是在BSD许可下发布的,因此它在学术和商业上的使用都是免费的。 它具有C ++,C,Python和Java接口,同时支持Windows,Linux,Mac OS,iOS和Android。OpenCV的设计目的是为了提高计算效率,并将重点放在实时应用程序上。采用优化的C / C ++编写,库可以利用多核处理。通过使用OpenCL,可以利用底层异构计算平台的硬件加速。OpenCV在世界各地被广泛采用,拥有超过4.7万人的用户群,预计下载量超过1400万。 其使用范围从交互式艺术到矿检,从网上拼接地图到高级机器人中都有它的身影。来来来,敲黑板了,其实重点就是①开源②高效,心动不如行动,赶快下载吧!
一、OpenCV安装教程
IDE:Visual Studio 2013语言:C++
依赖:OpenCV 2.4.9、3.3.0
安装教程网上已经有很多了,关于OpenCV2和3的争议也很多,我觉得不要想那么多,两个都装上就好嘛,2.4.9版本直接支持VS2013,安装比较简单,参考以下@浅墨_毛星云大大的教程即可:
【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
而3.3.0版的则需要通过CMake对OpenCV3.3源码生成VS2013的x64位解决方案sln,稍微复杂一丢丢,不过没关系,我们有教程呀,可以参考以下@阿木寺大大的教程:
OpenCV3.3安装教程(VS2013编译版)
以上两个教程亲测有效,如果你想要OpenCV2 + 3共存使用的话,只需分别使用VS属性管理器中的的Win32和x64分别对不同的库进行添加即可(建议默认使用2.4.9版本,按以上两个教程走就可以达到目的),下图以DEBUG的为例:
不管你是32位还是64位操作系统,只用管你用win32编译器还是X64 编译器。其实配置选择什么跟64位还是32位系统没有直接的关系,而是在于你在编译你的程序的时候是使用那个编译器。编译器是win32,就用x86编译器是X64,就用X64。不过一般情况下,都是用的win32的X86编译器。因此将2.4.9版本添加进Win32的属性管理器:
将3.3.0版本添加进x64的属性管理器:
之后在使用中根据需要(一般默认是Win32也就是使用2.4.9版本),切换对应版本(想使用3.3.0就将配置好的x64的属性管理器添加进工程,然后在配置管理器中修改使用x64进行编译调试)
二、JPEG压缩源码分析
JPEG 是Joint Photographic Experts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。示例程序是一个BMP转JPEG的压缩源码,整个工程文件见下载页面IDE:DEV-C++
语言:C
下面简单地做一下解析:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "JTYPES.H" #include "JGLOBALS.H" #include "JTABLES.H" void write_APP0info() //Nothing to overwrite for APP0info { writeword(APP0info.marker); writeword(APP0info.length); writebyte('J'); writebyte('F'); writebyte('I'); writebyte('F'); writebyte(0); writebyte(APP0info.versionhi); writebyte(APP0info.versionlo); writebyte(APP0info.xyunits); writeword(APP0info.xdensity); writeword(APP0info.ydensity); writebyte(APP0info.thumbnwidth); writebyte(APP0info.thumbnheight); } void write_SOF0info() // We should overwrite width and height { writeword(SOF0info.marker); writeword(SOF0info.length); writebyte(SOF0info.precision); writeword(SOF0info.height); writeword(SOF0info.width); writebyte(SOF0info.nrofcomponents); writebyte(SOF0info.IdY); writebyte(SOF0info.HVY); writebyte(SOF0info.QTY); writebyte(SOF0info.IdCb); writebyte(SOF0info.HVCb); writebyte(SOF0info.QTCb); writebyte(SOF0info.IdCr); writebyte(SOF0info.HVCr); writebyte(SOF0info.QTCr); } /******************************************************************** 写量化表的信息,这些以被赋予初始值 *********************************************************************/ void write_DQTinfo() { BYTE i; writeword(DQTinfo.marker); writeword(DQTinfo.length); writebyte(DQTinfo.QTYinfo); for (i=0; i<64; i++) writebyte(DQTinfo.Ytable[i]); writebyte(DQTinfo.QTCbinfo); for (i=0; i<64; i++) writebyte(DQTinfo.Cbtable[i]); } /************************************************************************************ 设置量化表zigzag排序 109 *************************************************************************************/ void set_quant_table(BYTE *basic_table, BYTE scale_factor, BYTE *newtable) //设置量化表zigzag排序 109 // quantizSetation table and zigzag reorder it { BYTE i; long temp; for (i=0; i<64; i++) { temp = ((long) basic_table[i] * scale_factor + 50L) / 100L; // limit the values to the valid range if (temp <= 0L) temp = 1L; if (temp > 255L) temp = 255L; newtable[zigzag[i]] = (BYTE) temp; } } /********************************************************************************* 量化因子的设定,按z秩序进行亮度与色度量化步长的排序 **********************************************************************************/ void set_DQTinfo() { BYTE scalefactor = 50;// scalefactor controls the visual quality of the image // the smaller is the better image we'll get, and the smaller // compression we'll achieve DQTinfo.marker = 0xFFDB; DQTinfo.length = 132; DQTinfo.QTYinfo = 0; DQTinfo.QTCbinfo = 1; /****************************************************************************** 四 重点讲解亮度表和色度表,注意量化因子 *******************************************************************************/ set_quant_table(std_luminance_qt, scalefactor, DQTinfo.Ytable); set_quant_table(std_chrominance_qt, scalefactor, DQTinfo.Cbtable); } /********************************************************************************* 哈傅曼编码的量化排序 **********************************************************************************/ void write_DHTinfo() { BYTE i; writeword(DHTinfo.marker); writeword(DHTinfo.length); writebyte(DHTinfo.HTYDCinfo); for (i=0; i<16; i++) writebyte(DHTinfo.YDC_nrcodes[i]); for (i=0; i<12; i++) writebyte(DHTinfo.YDC_values[i]); writebyte(DHTinfo.HTYACinfo); for (i=0; i<16; i++) writebyte(DHTinfo.YAC_nrcodes[i]); for (i=0; i<162; i++) writebyte(DHTinfo.YAC_values[i]); writebyte(DHTinfo.HTCbDCinfo); for (i=0; i<16; i++) writebyte(DHTinfo.CbDC_nrcodes[i]); for (i=0; i<12; i++) writebyte(DHTinfo.CbDC_values[i]); writebyte(DHTinfo.HTCbACinfo); for (i=0; i<16; i++) writebyte(DHTinfo.CbAC_nrcodes[i]); for (i=0; i< 162; i++) writebyte(DHTinfo.CbAC_values[i]); } /*************************************************** 同上 ****************************************************/ void set_DHTinfo() { BYTE i; // fill the DHTinfo structure [get the values from the standard Huffman tables] DHTinfo.marker = 0xFFC4; DHTinfo.length = 0x01A2; DHTinfo.HTYDCinfo = 0; for (i=0; i<16; i++) DHTinfo.YDC_nrcodes[i] = std_dc_luminance_nrcodes[i+1]; for (i=0; i<12; i++) DHTinfo.YDC_values[i] = std_dc_luminance_values[i]; DHTinfo.HTYACinfo = 0x10; for (i=0; i<16; i++) DHTinfo.YAC_nrcodes[i] = std_ac_luminance_nrcodes[i+1]; for (i=0; i<162; i++) DHTinfo.YAC_values[i] = std_ac_luminance_values[i]; DHTinfo.HTCbDCinfo = 1; for (i=0; i<16; i++) DHTinfo.CbDC_nrcodes[i] = std_dc_chrominance_nrcodes[i+1]; for (i=0; i<12; i++) DHTinfo.CbDC_values[i] = std_dc_chrominance_values[i]; DHTinfo.HTCbACinfo = 0x11; for (i=0; i<16; i++) DHTinfo.CbAC_nrcodes[i] = std_ac_chrominance_nrcodes[i+1]; for (i=0; i<162; i++) DHTinfo.CbAC_values[i] = std_ac_chrominance_values[i]; } /**************************************************************** 设置编码开始的标记 *****************************************************************/ void write_SOSinfo() //Nothing to overwrite for SOSinfo { writeword(SOSinfo.marker); writeword(SOSinfo.length); writebyte(SOSinfo.nrofcomponents); writebyte(SOSinfo.IdY); writebyte(SOSinfo.HTY); writebyte(SOSinfo.IdCb); writebyte(SOSinfo.HTCb); writebyte(SOSinfo.IdCr); writebyte(SOSinfo.HTCr); writebyte(SOSinfo.Ss); writebyte(SOSinfo.Se); writebyte(SOSinfo.Bf); } void write_comment(BYTE *comment) { WORD i, length; writeword(0xFFFE); // The COM marker length = strlen((const char *)comment); writeword(length + 2); for (i=0; i<length; i++) writebyte(comment[i]); } void writebits(bitstring bs) // A portable version; it should be done in assembler { WORD value; SBYTE posval;// bit position in the bitstring we read, should be <=15 and >=0 value = bs.value; posval = bs.length - 1; while (posval >= 0) { if (value & mask[posval]) bytenew |= mask[bytepos]; posval--; bytepos--; if (bytepos < 0) { // write it if (bytenew == 0xFF) { // special case writebyte(0xFF); writebyte(0); } else writebyte(bytenew); // reinit bytepos = 7; bytenew = 0; } } } /******************************************************************************************** 对准Huffman表的改变,利用静态数据作初始化 *********************************************************************************************/ void compute_Huffman_table(BYTE *nrcodes, BYTE *std_table, bitstring *HT) //修改标准Huffman码表,可以修改压缩率 { BYTE k,j; BYTE pos_in_table; WORD codevalue; codevalue = 0; pos_in_table = 0; for (k=1; k<=16; k++) { for (j=1; j<=nrcodes[k]; j++) { HT[std_table[pos_in_table]].value = codevalue; HT[std_table[pos_in_table]].length = k; pos_in_table++; codevalue++; } codevalue <<= 1; } } void init_Huffman_tables() //初始化Huffman编码表 { // Compute the Huffman tables used for encoding //计算编码所需的Huffman码表 compute_Huffman_table(std_dc_luminance_nrcodes, std_dc_luminance_values, YDC_HT); //YDC-HT:亮度直流信号 compute_Huffman_table(std_ac_luminance_nrcodes, std_ac_luminance_values, YAC_HT); //YDC-HT:亮度交流信号 compute_Huffman_table(std_dc_chrominance_nrcodes, std_dc_chrominance_values, CbDC_HT); compute_Huffman_table(std_ac_chrominance_nrcodes, std_ac_chrominance_values, CbAC_HT); } void exitmessage(char *error_message) { printf("%s\n",error_message); exit(EXIT_FAILURE); } /************************************************************************** 作用未知??????????????????????????????????????????????????????????????????? ***************************************************************************/ void set_numbers_category_and_bitcode() { SDWORD nr; SDWORD nrlower, nrupper; BYTE cat; category_alloc = (BYTE *)malloc(65535*sizeof(BYTE)); if (category_alloc == NULL) exitmessage("Not enough memory."); //allow negative subscripts category = category_alloc + 32767; bitcode_alloc=(bitstring *)malloc(65535*sizeof(bitstring)); if (bitcode_alloc==NULL) exitmessage("Not enough memory."); bitcode = bitcode_alloc + 32767; nrlower = 1; nrupper = 2; for (cat=1; cat<=15; cat++) { //Positive numbers for (nr=nrlower; nr<nrupper; nr++) { category[nr] = cat; bitcode[nr].length = cat; bitcode[nr].value = (WORD)nr; } //Negative numbers for (nr=-(nrupper-1); nr<=-nrlower; nr++) { category[nr] = cat; bitcode[nr].length = cat; bitcode[nr].value = (WORD)(nrupper-1+nr); } nrlower <<= 1; nrupper <<= 1; } } /**************************************************************************** 从rgb->ycbcr的转换 *****************************************************************************/ void precalculate_YCbCr_tables() //RGB --> YCbCr { WORD R,G,B; for (R=0; R<256; R++) { YRtab[R] = (SDWORD)(65536*0.299+0.5)*R; CbRtab[R] = (SDWORD)(65536*-0.16874+0.5)*R; CrRtab[R] = (SDWORD)(32768)*R; } for (G=0; G<256; G++) { YGtab[G] = (SDWORD)(65536*0.587+0.5)*G; CbGtab[G] = (SDWORD)(65536*-0.33126+0.5)*G; CrGtab[G] = (SDWORD)(65536*-0.41869+0.5)*G; } for (B=0; B<256; B++) { YBtab = (SDWORD)(65536*0.114+0.5)*B; CbBtab[B] = (SDWORD)(32768)*B; CrBtab[B] = (SDWORD)(65536*-0.08131+0.5)*B; } } // Using a bit modified form of the FDCT routine from IJG's C source: // Forward DCT routine idea taken from Independent JPEG Group's C source for // JPEG encoders/decoders /* For float AA&N IDCT method, divisors are equal to quantization coefficients scaled by scalefactor[row]*scalefactor[col], where scalefactor[0] = 1 scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 We apply a further scale factor of 8. What's actually stored is 1/divisor so that the inner loop can use a multiplication rather than a division. */ void prepare_quant_tables() //准备量化表。通过下面给出的参数修改标准量化表来定制自己所需的量化表 { double aanscalefactor[8] = {1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379}; BYTE row, col; BYTE i = 0; //8*8的宏块设置 for (row = 0; row < 8; row++) { for (col = 0; col < 8; col++) { fdtbl_Y[i] = (float) (1.0 / ((double) DQTinfo.Ytable[zigzag[i]] * aanscalefactor[row] * aanscalefactor[col] * 8.0)); fdtbl_Cb[i] = (float) (1.0 / ((double) DQTinfo.Cbtable[zigzag[i]] * aanscalefactor[row] * aanscalefactor[col] * 8.0)); i++; } } } /************************************************************************************ 快速离散余弦变换(fdct) 先行变换,8行变换后,再按列变换,按时域抽取来划分 *************************************************************************************/ void fdct_and_quantization(SBYTE *data, float *fdtbl, SWORD *outdata) //使用快速离散余弦变换方法实现(FDCT)来实现离散余弦变化并量化 { float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; float tmp10, tmp11, tmp12, tmp13; float z1, z2, z3, z4, z5, z11, z13; float *dataptr; float datafloat[64]; float temp; SBYTE ctr; BYTE i; for (i=0; i<64; i++) datafloat[i] = data[i]; /* Pass 1: process rows. */ dataptr = datafloat; for (ctr = 7; ctr >= 0; ctr--) { tmp0 = dataptr[0] + dataptr[7]; tmp7 = dataptr[0] - dataptr[7]; tmp1 = dataptr[1] + dataptr[6]; tmp6 = dataptr[1] - dataptr[6]; tmp2 = dataptr[2] + dataptr[5]; tmp5 = dataptr[2] - dataptr[5]; tmp3 = dataptr[3] + dataptr[4]; tmp4 = dataptr[3] - dataptr[4]; /* Even part */ tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[0] = tmp10 + tmp11; /* phase 3 */ dataptr[4] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ dataptr[2] = tmp13 + z1; /* phase 5 */ dataptr[6] = tmp13 - z1; e485 /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ z3 = tmp11 * ((float) 0.707106781); /* c4 */ z11 = tmp7 + z3; /* phase 5 */ z13 = tmp7 - z3; dataptr[5] = z13 + z2; /* phase 6 */ dataptr[3] = z13 - z2; dataptr[1] = z11 + z4; dataptr[7] = z11 - z4; dataptr += 8; /* advance pointer to next row */ } /* Pass 2: process columns. */ dataptr = datafloat; for (ctr = 7; ctr >= 0; ctr--) { tmp0 = dataptr[0] + dataptr[56]; tmp7 = dataptr[0] - dataptr[56]; tmp1 = dataptr[8] + dataptr[48]; tmp6 = dataptr[8] - dataptr[48]; tmp2 = dataptr[16] + dataptr[40]; tmp5 = dataptr[16] - dataptr[40]; tmp3 = dataptr[24] + dataptr[32]; tmp4 = dataptr[24] - dataptr[32]; /* Even part */ tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[0] = tmp10 + tmp11; /* phase 3 */ dataptr[32] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ dataptr[16] = tmp13 + z1; /* phase 5 */ dataptr[48] = tmp13 - z1; /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ z3 = tmp11 * ((float) 0.707106781); /* c4 */ z11 = tmp7 + z3; /* phase 5 */ z13 = tmp7 - z3; dataptr[40] = z13 + z2; /* phase 6 */ dataptr[24] = z13 - z2; dataptr[8] = z11 + z4; dataptr[56] = z11 - z4; dataptr++; /* advance pointer to next column */ } /* Quantize/descale the coefficients, and store into output array */ for (i = 0; i < 64; i++) { /* Apply the quantization and scaling factor */ temp = datafloat[i] * fdtbl[i]; /* Round to nearest integer. Since C does not specify the direction of rounding for negative quotients, we have to force the dividend positive for portability. The maximum coefficient size is +-16K (for 12-bit data), so this code should work for either 16-bit or 32-bit ints. */ outdata[i] = (SWORD) ((SWORD)(temp + 16384.5) - 16384); } } /************************************************************************************ FDCT变换,DC,AC量化编码(Z扫描),哈夫曼编码存储 *************************************************************************************/ void process_DU(SBYTE *ComponentDU,float *fdtbl,SWORD *DC, bitstring *HTDC,bitstring *HTAC) { bitstring EOB = HTAC[0x00]; bitstring M16zeroes = HTAC[0xF0]; BYTE i; BYTE startpos; BYTE end0pos; BYTE nrzeroes; BYTE nrmarker; SWORD Diff; fdct_and_quantization(ComponentDU, fdtbl, DU_DCT); //使用快速离散余弦变换方法实现(FDCT)来实现离散余弦变化并量化 { // zigzag reorder for (i=0; i<64; i++) //使用zigzag方式对数据进行排序 DU[zigzag[i]]=DU_DCT[i]; // Encode DC //对直流分量进行专门的差分编码 Diff = DU[0] - *DC; *DC = DU[0]; /**************************************************************************************** 哈夫曼编码存储 *****************************************************************************************/ if (Diff == 0) writebits(HTDC[0]); //Diff might be 0 else { writebits(HTDC[category[Diff]]); writebits(bitcode[Diff]); } // Encode ACs //对交流分量进行按zigzag顺序编码 for (end0pos=63; (end0pos>0)&&(DU[end0pos]==0); end0pos--) ; //end0pos = first element in reverse order != 0 i = 1; while (i <= end0pos) { startpos = i; for (; (DU[i]==0) && (i<=end0pos); i++) ; nrzeroes = i - startpos; if (nrzeroes >= 16) { for (nrmarker=1; nrmarker<=nrzeroes/16; nrmarker++) writebits(M16zeroes); nrzeroes = nrzeroes%16; } writebits(HTAC[nrzeroes*16+category[DU[i]]]); writebits(bitcode[DU[i]]); i++; } if (end0pos != 63) writebits(EOB); } } /*************************************************************************** 按块64*64从rgb缓存中读取数据 ****************************************************************************/ void load_data_units_from_RGB_buffer(WORD xpos, WORD ypos) { BYTE x, y; BYTE pos = 0; DWORD location; BYTE R, G, B; location = ypos * width + xpos; for (y=0; y<8; y++) { for (x=0; x<8; x++) //图像取反前的注释 { R = RGB_buffer[location].R; G = RGB_buffer[location].G; B = RGB_buffer[location].B; // for (x=0; x<8; x++) //图像取反处理 // { // R = 0xff - RGB_buffer[location].R; // G = 0xff - RGB_buffer[location].G; // B = 0xff - RGB_buffer[location].B; // convert to YCbCr YDU[pos] = Y(R,G,B); CbDU[pos] = Cb(R,G,B); CrDU[pos] = Cr(R,G,B); location++; pos++; } location += width - 8; } } void main_encoder() // { SWORD DCY = 0, DCCb = 0, DCCr = 0; //DC coefficients used for differential encoding WORD xpos, ypos; for (ypos=0; ypos<height; ypos+=8) { for (xpos=0; xpos<width; xpos+=8) { load_data_units_from_RGB_buffer(xpos, ypos); //载入数据 process_DU(YDU, fdtbl_Y, &DCY, YDC_HT, YAC_HT); //DU:Data Units 分别处理Y、Cb、Cr三个分量 编码 process_DU(CbDU, fdtbl_Cb, &DCCb, CbDC_HT, CbAC_HT); process_DU(CrDU, fdtbl_Cb, &DCCr, CbDC_HT, CbAC_HT); } } } /**************************************************************** 原始图像读入内存 ********************************************************************/ void load_bitmap(char *bitmap_name, WORD *width_original, WORD *height_original) { WORD widthDiv8, heightDiv8; // closest multiple of 8 [ceil] BYTE nr_fillingbytes;//The number of the filling bytes in the BMP file // (the dimension in bytes of a BMP line on the disk is divisible by 4) colorRGB lastcolor; WORD column; BYTE TMPBUF[256]; WORD nrline_up, nrline_dn, nrline; WORD dimline; colorRGB *tmpline; FILE *fp_bitmap = fopen(bitmap_name,"rb"); if (fp_bitmap==NULL) exitmessage("Cannot open bitmap file.File not found ?"); //注意文件指针指过了BMP的信息头 if (fread(TMPBUF, 1, 54, fp_bitmap) != 54) exitmessage("Need a truecolor BMP to encode."); if ((TMPBUF[0]!='B')||(TMPBUF[1]!='M')||(TMPBUF[28]!=24)) exitmessage("Need a truecolor BMP to encode."); /************************************************************************************* 三 参见bmp格式详解 ,白度文库;新浪爱问;csdn;pudn; 使用ultraedit剖析单个图片的格式 与单帧的格式 **************************************************************************************/ width = (WORD)TMPBUF[19]*256+TMPBUF[18]; height = (WORD)TMPBUF[23]*256+TMPBUF[22]; // Keep the old dimensions of the image *width_original = width; *height_original = height; //字节对齐 if (width%8 != 0) widthDiv8 = (width/8)*8+8; else widthDiv8 = width; if (height%8 != 0) heightDiv8 = (height/8)*8+8; else heightDiv8 = height; // The image we encode shall be filled with the last line and the last column // from the original bitmap, until width and height are divisible by 8 // Load BMP image from disk and complete X /******************************************************************************* 注意:将全局变量分配内存,将文件读取的图像数据复制到此内存中 讲解内存分配 ********************************************************************************/ RGB_buffer = (colorRGB *)(malloc(3*widthDiv8*heightDiv8)); if (RGB_buffer == NULL) exitmessage("Not enough memory for the bitmap image."); if ( (width*3)%4 != 0) nr_fillingbytes = 4 - ( (width*3)%4); else nr_fillingbytes = 0; /******************************************************************************* 按行读取拷入内存 ********************************************************************************/ for (nrline=0; nrline<height; nrline++) { fread(RGB_buffer + nrline*widthDiv8, 1, width*3, fp_bitmap); //注意乘3 fread(TMPBUF, 1, nr_fillingbytes, fp_bitmap); // complete X 作用未知? memcpy(&lastcolor, RGB_buffer + nrline*widthDiv8 + width-1, 3); for (column=width; column<widthDiv8; column++) memcpy(RGB_buffer+nrline*widthDiv8+column, &lastcolor, 3); } width = widthDiv8; dimline = width*3; tmpline = (colorRGB *)malloc(dimline); if (tmpline == NULL) exitmessage("Not enough memory."); // Reorder in memory the inversed bitmap /********************************************************************************************** 图像倒置 ***********************************************************************************************/ for (nrline_up=height-1,nrline_dn=0; nrline_up>nrline_dn; nrline_up--,nrline_dn++) { memcpy(tmpline, RGB_buffer+nrline_up*width, dimline); memcpy(RGB_buffer+nrline_up*width, RGB_buffer+nrline_dn*width, dimline); memcpy(RGB_buffer+nrline_dn*width, tmpline, dimline); } // Y completion: memcpy(tmpline, RGB_buffer+(height-1)*width, dimline); for (nrline=height; nrline<heightDiv8; nrline++) memcpy(RGB_buffer+nrline*width, tmpline, dimline); height = heightDiv8; free(tmpline); fclose(fp_bitmap); } /******************************************************************* 初始化各种量化表 ********************************************************************/ void init_all() { set_DQTinfo(); set_DHTinfo(); init_Huffman_tables(); set_numbers_category_and_bitcode(); precalculate_YCbCr_tables(); prepare_quant_tables(); } /********************************************************************************** 零:讲解如何设置输入参数 ***********************************************************************************/ int main(int argc, char *argv[]) { char BMP_filename[64]; char JPG_filename[64]; WORD width_original,height_original; //the original image dimensions, // before we made them divisible by 8 BYTE len_filename; bitstring fillbits; //filling bitstring for the bit alignment of the EOI marker //EOI图像结束的标记 /******************************************************************************* ******************************************************************************** 一. 讲解参数的个数及其的含义 ******************************************************************************** ********************************************************************************/ if (argc>1) { strcpy(BMP_filename,argv[1]); if (argc>2) strcpy(JPG_filename,argv[2]); else { // replace ".bmp" with ".jpg" strcpy(JPG_filename, BMP_filename); len_filename=strlen(BMP_filename); strcpy(JPG_filename+(len_filename-3),"jpg"); } } else exitmessage("Syntax: enc fis.bmp [fis.jpg]"); //BMP_filename=""; /************************************************************************* 二:讲解如何跳转和回到重前,设置断点及调试,查看当前运行情况 **************************************************************************/ load_bitmap(BMP_filename, &width_original, &height_original); fp_jpeg_stream = fopen(JPG_filename,"wb"); init_all(); /************************************************************************* 桢图像开始的标记sof **************************************************************************/ SOF0info.width = width_original; SOF0info.height = height_original; /************************************************************************* 图像开始的标记soi **************************************************************************/ writeword(0xFFD8); // SOI /**************************************************************** 版本信息 *****************************************************************/ write_APP0info(); // write_comment("Cris made this JPEG with his own encoder"); /****************************************************************** 定义量化表 *******************************************************************/ write_DQTinfo(); /****************************************************************** *******************************************************************/ write_SOF0info(); write_DHTinfo(); write_SOSinfo(); // init global variables bytenew = 0; // current byte bytepos = 7; // bit position in this byte main_encoder(); // Do the bit alignment of the EOI marker if (bytepos >= 0) { fillbits.length = bytepos + 1; fillbits.value = (1<<(bytepos+1)) - 1; writebits(fillbits); } writeword(0xFFD9); // EOI free(RGB_buffer); free(category_alloc); free(bitcode_alloc); fclose(fp_jpeg_stream); }[b]1.通过对以上BMP转JPEG的源码的分析来熟悉JPEG压缩和解压缩流程:①主程序中先是声明变量的过程,接着通过字符串操作把主程序参数输入图像的后缀由.bmp改成了.jpg,这步主要就是获取到输入图像的名字。如果没有读到图像,则会打印出一串错误信息:
②接着装载图片,将原始图像读入内存,并且进行字节对齐处理,把长宽定为8的倍数以便下一步的宏模块分割,然后将图像作倒置(X,Y轴)处理。之后通过fopen函数在当前目录下打开输入的图片赋给文件指针fp_jpeg_stream,并设置为wb即写二进制文件操作。
③初始化:初始化各种量化表,这里面调用了好多量化排序的函数(根据亮度表色度表以及哈傅曼编码)、把RGB转换成YCbCr、滤波,就是JPEG中预处理的过程,在初始化中的prepare_quant_tables()函数中还有一个8*8的宏模块设置(还在空间域)
④接着标记图像的帧开始、版本信息、定义量化表(量化因子选的是50)的过程
⑤接下来进行快速离散余弦变化并量化(分别处理三个分量Y、Cb、Cr,使用zigzag方式对数据进行排序,对直流分量进行专门的差分编码,交流分量进行按zigzag顺序编码,霍夫曼编码存储)
⑥释放缓冲区、空间、IO流
(2)编译并用测试照片(工程目录下的1.bmp)进行实验,观察源文件和生成jpg文件大小,计算出压缩率:
压缩率 = (95.9/(5.49*1024)) * 100% ≈1.7%
2.熟悉图像处理程序框架,完成图像取反运算。在源码中,有个函数:load_data_units_from_RGB_buffer,是负责从RGB缓存中读取数据的,只需对这段函数中RGB的值稍作修改即可完成图像取反运算。
就是拿255去减RGB值,出来的就是取反图像:
BMP转JPEG压缩源码与取反运算修改及分析,整个工程文件见下载页面OpenCV图像处理入门学习教程系列,下一篇第二篇:不同阈值二值化图像
相关文章推荐
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 2.4.8或OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 开发环境配置(转)