最近遇到个需求,已有全景图和其中的人脸坐标,将人脸小图从全景图中抠出来,最开始使用libjpeg,奈何使用libjpeg将jpg转为yuv420的资料实在少,libjpeg自身的readme和example也是异常简陋,只介绍了常用函数,却没有具体的yuv数据计算方法,搞了两天最后jpg解码yuv成功后,再切图后继续转为jpg后将数据写到文件发现问题,小图始终没有数据。
无奈只能copy代码中原有的一份算法解码库,进行类似操作后,小图转为jpg后还是空的,求助算法大佬后发现自己一开始就给自己写了个大bug,就是小图yuv转为jpg后是存储在malloc的一段内存里的,类型为char*,一开始为了数据copy方便,就直接将jpg的指针赋给了std::string,再利用string中数据写到文件里,导致文件一直是空的,其实小图的jpg数据已经成功的转化了,只是自己没理解char*指针和string的区别,以为所有指针都可以直接赋给std::string,其实char*型指针里如果保存的是类似图片数据这种不是标准的字符串的话,是很容易出现数据被截断的情况的,如果实在要赋值的话,需要加上需要复制的长度才行,实在汗颜,期间遇到很多指针操作,如三级指针、指针数组、指针分配内存(要使用指针必须分配内存或者指向已分配内存的地址,还要注意不要提前释放掉还在使用的指针指向的内存)、指针转string(指针若指向的非标准字符串则赋给std::string时需要加上需要复制的长度),char*和unsigned char*的区别(例如16进制0xffffffff存储在char*里表示为-1,存储在unsigned char*里表示为0xff),感觉自己对C的指针了解还是太少。
除此之外,我发现一个更深层次的问题就是自己遇到新的东西不愿意去思考和了解,固步自封,只知道百度现成代码复制粘贴,也不去思考复制过来的代码是什么原理,适不适用现有业务,也不知道看官方的例子(虽然libjpeg官方的例子实在是有点垃圾,但是最好还是要先看下官方文档),也让我了解到需要学习的地方还有很多,基础知识还是相当的薄弱,还需要不断学习。
说了这么多,还是把这次研究了好几天的jpg转yuv420p,yuv420p转jpg,从yuv420p和yuv420sp取数组方法(yuv420还分为420p和420sp,420p为yyy...uuu...vvv..., 420sp为yyy...uvuvuv......)的代码分享出来下,供大家参考:
这里参考了这篇博文:https://www.cnblogs.com/zhq-blog/p/8858293.html
jpg转yuv420p:
1 #define NEW_BUFFER(param, len) if(!param)\ 2 { param = new unsigned char[len];}\ 3 else { delete param; param = new unsigned char[len];}; 4 5 unsigned char** yuvptr_[3]; 6 std::vector< std::vector<unsigned char*> > yuvbuf_; 7 unsigned char* m_pYbuffer = NULL; 8 unsigned char* m_pUbuffer = NULL; 9 unsigned char* m_pVbuffer = NULL; 10 11 std::string jpg_to_yuv420p(char* pBuffer, int nSize, ULONG_32 &uPicWidth, ULONG_32 &uPicHeight) 12 { 13 jpeg_decompress_struct info_; 14 info_.err = jpeg_std_error(&e_); 15 jpeg_create_decompress(&info_); 16 jpeg_mem_src(&info_, (unsigned char*)pBuffer, nSize); //// 指定图片在内存的地址及大小 17 jpeg_read_header(&info_, 1); 18 info_.out_color_space = JCS_YCbCr; 19 info_.raw_data_out = 1; 20 info_.do_fancy_upsampling = FALSE; 21 jpeg_start_decompress(&info_); 22 23 24 for (int i = 0; i < 3; ++i) 25 { 26 yuvbuf_[i].resize(info_.output_width); 27 yuvptr_[i] = &yuvbuf_[i][0]; 28 } 29 30 int nLen = info_.output_width * info_.output_height; 31 32 NEW_BUFFER(m_pYbuffer, nLen); 33 NEW_BUFFER(m_pUbuffer, nLen); 34 NEW_BUFFER(m_pVbuffer, nLen); 35 36 unsigned char* row = m_pYbuffer; 37 yuvptr_[0][0] = row; 38 for (int i = 0; i < info_.output_height; ++i, row += info_.output_width) 39 { 40 yuvptr_[0][i] = row; //y 分量空间初始化 41 } 42 43 row = m_pUbuffer; 44 for (int i = 0; i < info_.output_height; i += 2, row += info_.output_width / 2) 45 { 46 yuvptr_[1][i / 2] = row; //u 分量初始化 47 48 } 49 50 row = m_pVbuffer; 51 for (int i = 0; i < info_.output_height; i += 2, row += info_.output_width / 2) 52 { 53 yuvptr_[2][i / 2] = row; //v 分量初始化 54 } 55 56 for (int i = 0; i < info_.output_height; i += 16) 57 { 58 int nRows = 16; 59 if ((info_.output_height) < (i + 16)) 60 { 61 nRows = info_.output_height - i; 62 } 63 jpeg_read_raw_data(&info_, yuvptr_, nRows); 64 yuvptr_[0] += 16; 65 yuvptr_[1] += 8; 66 yuvptr_[2] += 8; 67 } 68 uPicWidth = info_.image_width; 69 uPicHeight = info_.image_height; 70 71 72 std::string Y((char*)yuvbuf_[0][0], uPicWidth*uPicHeight); 73 74 std::string U((char*)yuvbuf_[1][0], uPicWidth*uPicHeight/4); 75 76 std::string V((char*)yuvbuf_[2][0], uPicWidth*uPicHeight/4); 77 78 std::string YUV = Y+U+V; 79 80 jpeg_finish_decompress(&info_); 81 jpeg_destroy_decompress(&info_); 82 83 return YUV; 84 }
yuv420p转jpg:
1 int yuv420p_to_jpeg(unsigned char* out, unsigned char* pdata, int image_width, int image_height, int quality) 2 { 3 if (image_width == 0 || image_height == 0) { 4 LOG(ERROR) << "param error..."; 5 return IA_ERROR_BASE; 6 } 7 struct jpeg_compress_struct cinfo; 8 struct jpeg_error_mgr jerr; 9 cinfo.err = jpeg_std_error(&jerr); 10 jpeg_create_compress(&cinfo); 11 unsigned char *outbuffer; 12 unsigned long size = 0; 13 jpeg_mem_dest(&cinfo, &outbuffer, &size); 14 15 LOG(ERROR) << "size" << size; 16 cinfo.image_width = image_width; // image width and height, in pixels 17 cinfo.image_height = image_height; 18 cinfo.input_components = 3; // # of color components per pixel 19 cinfo.in_color_space = JCS_YCbCr; //colorspace of input image 20 jpeg_set_defaults(&cinfo); 21 jpeg_set_quality(&cinfo, quality, TRUE); 22 23 ////////////////////////////// 24 // cinfo.raw_data_in = TRUE; 25 cinfo.jpeg_color_space = JCS_YCbCr; 26 cinfo.comp_info[0].h_samp_factor = 2; 27 cinfo.comp_info[0].v_samp_factor = 2; 28 ///////////////////////// 29 jpeg_start_compress(&cinfo, TRUE); 30 31 JSAMPROW row_pointer[1]; 32 33 unsigned char *yuvbuf; 34 if ((yuvbuf = (unsigned char *)malloc(image_width * 3)) != NULL) 35 memset(yuvbuf, 0, image_width * 3); 36 37 unsigned char *ybase, *ubase; 38 ybase = pdata; 39 ubase = pdata + image_width*image_height; 40 int j = 0; 41 while (cinfo.next_scanline < cinfo.image_height) 42 { 43 int idx = 0; 44 for (int i = 0; i < image_width; i++) 45 { 46 yuvbuf[idx++] = ybase[i + j * image_width]; 47 yuvbuf[idx++] = ubase[j / 2 * image_width + (i / 2) * 2]; 48 yuvbuf[idx++] = ubase[j / 2 * image_width + (i / 2) * 2 + 1]; 49 } 50 row_pointer[0] = yuvbuf; 51 jpeg_write_scanlines(&cinfo, row_pointer, 1); 52 j++; 53 } 54 jpeg_finish_compress(&cinfo); 55 jpeg_destroy_compress(&cinfo); 56 if (yuvbuf != NULL) 57 { 58 free(yuvbuf); 59 } 60 LOG(ERROR) << "size" << size << "outbuffer" << strlen((char*)outbuffer); 61 std::string tmp = (char*)outbuffer; 62 LOG(ERROR) << "tmp size" << tmp.size(); 63 if (size) 64 { 65 memcpy(out, outbuffer, size); 66 free(outbuffer); 67 } 68 }
yuv420p内存取数组:
1 for(int j=0;j<DataHeight;j++) 2 { 3 for(int i=0;i<DataWidth;i++) 4 { 5 //yyyyyy ... uuuu ...vvv 6 y=ybase[i + j * DataWidth]; 7 u=ubase[j/2 * DataWidth/2+(i/2)]; 8 v=vbase[j/2 * DataWidth/2+(i/2)]; 9 } 10 }
yuv420sp内存取数组:
1 for(int j=0;j<DataHeight;j++) 2 { 3 for(int i=0;i<DataWidth;i++) 4 { 5 y=ybase[i + j * DataWidth];// 每四个y对应一个uv 6 u=ubase[j/2 * DataWidth+(i/2)*2]; 7 v=ubase[j/2 * DataWidth+(i/2)*2+1]; //一定要注意是u+1 8 } 9 }