最近遇到个需求,已有全景图和其中的人脸坐标,将人脸小图从全景图中抠出来,最开始使用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的代码分享出来下,供大家参考(参考博文:https://www.cnblogs.com/zhq-blog/p/8858293.html):
1 #include <windows.h> 2 #include <iostream> 3 #include <vector> 4 #include <memory> 5 #include <jpeglib.h> 6 7 #define NEW_BUFFER(param, len) if(!param)\ 8 { param = new unsigned char[len];}\ 9 else { delete param; param = new unsigned char[len];}; 10 11 using namespace std; 12 13 unsigned char** yuvptr_[3]; 14 std::vector< std::vector<unsigned char*> > yuvbuf_(3); 15 unsigned char* m_pYbuffer = NULL; 16 unsigned char* m_pUbuffer = NULL; 17 unsigned char* m_pVbuffer = NULL; 18 19 std::string jpg_to_yuv420p(char* pBuffer, int nSize, int &uPicWidth, int &uPicHeight) 20 { 21 struct jpeg_error_mgr e_; 22 jpeg_decompress_struct info_; 23 info_.err = jpeg_std_error(&e_); 24 jpeg_create_decompress(&info_); 25 jpeg_mem_src(&info_, (unsigned char*)pBuffer, nSize); //// 指定图片在内存的地址及大小 26 jpeg_read_header(&info_, 1); 27 info_.out_color_space = JCS_YCbCr; 28 info_.raw_data_out = 1; 29 info_.do_fancy_upsampling = FALSE; 30 jpeg_start_decompress(&info_); 31 32 33 for (int i = 0; i < 3; ++i) 34 { 35 yuvbuf_[i].resize(info_.output_width); 36 yuvptr_[i] = &yuvbuf_[i][0]; 37 } 38 39 int nLen = info_.output_width * info_.output_height; 40 41 NEW_BUFFER(m_pYbuffer, nLen); 42 NEW_BUFFER(m_pUbuffer, nLen); 43 NEW_BUFFER(m_pVbuffer, nLen); 44 45 unsigned char* row = m_pYbuffer; 46 yuvptr_[0][0] = row; 47 for (int i = 0; i < info_.output_height; ++i, row += info_.output_width) 48 { 49 yuvptr_[0][i] = row; //y 分量空间初始化 50 } 51 52 row = m_pUbuffer; 53 for (int i = 0; i < info_.output_height; i += 2, row += info_.output_width / 2) 54 { 55 yuvptr_[1][i / 2] = row; //u 分量初始化 56 57 } 58 59 row = m_pVbuffer; 60 for (int i = 0; i < info_.output_height; i += 2, row += info_.output_width / 2) 61 { 62 yuvptr_[2][i / 2] = row; //v 分量初始化 63 } 64 65 for (int i = 0; i < info_.output_height; i += 16) 66 { 67 int nRows = 16; 68 if ((info_.output_height) < (i + 16)) 69 { 70 nRows = info_.output_height - i; 71 } 72 jpeg_read_raw_data(&info_, yuvptr_, nRows); 73 yuvptr_[0] += 16; 74 yuvptr_[1] += 8; 75 yuvptr_[2] += 8; 76 } 77 uPicWidth = info_.image_width; 78 uPicHeight = info_.image_height; 79 80 81 std::string Y((char*)yuvbuf_[0][0], uPicWidth*uPicHeight); 82 83 std::string U((char*)yuvbuf_[1][0], uPicWidth*uPicHeight / 4); 84 85 std::string V((char*)yuvbuf_[2][0], uPicWidth*uPicHeight / 4); 86 87 88 std::string YUV = Y + U + V; 89 90 cout << YUV.size() << endl; 91 92 jpeg_finish_decompress(&info_); 93 jpeg_destroy_decompress(&info_); 94 95 return YUV; 96 } 97 98 int yuv420p_to_jpeg(unsigned char* pdata, int image_width, int image_height, int quality) 99 { 100 if (image_width == 0 || image_height == 0) { 101 cout << "err param\n"; 102 return 0; 103 } 104 struct jpeg_compress_struct cinfo; 105 struct jpeg_error_mgr jerr; 106 cinfo.err = jpeg_std_error(&jerr); 107 jpeg_create_compress(&cinfo); 108 unsigned char *outbuffer; 109 unsigned long size = 0; 110 jpeg_mem_dest(&cinfo, &outbuffer, &size); 111 112 cinfo.image_width = image_width; // image width and height, in pixels 113 cinfo.image_height = image_height; 114 cinfo.input_components = 3; // # of color components per pixel 115 cinfo.in_color_space = JCS_YCbCr; //colorspace of input image 116 jpeg_set_defaults(&cinfo); 117 jpeg_set_quality(&cinfo, quality, TRUE); 118 119 ////////////////////////////// 120 // cinfo.raw_data_in = TRUE; 121 cinfo.jpeg_color_space = JCS_YCbCr; 122 cinfo.comp_info[0].h_samp_factor = 2; 123 cinfo.comp_info[0].v_samp_factor = 2; 124 ///////////////////////// 125 jpeg_start_compress(&cinfo, TRUE); 126 127 JSAMPROW row_pointer[1]; 128 129 unsigned char *yuvbuf; 130 if ((yuvbuf = (unsigned char *)malloc(image_width * 3)) != NULL) 131 memset(yuvbuf, 0, image_width * 3); 132 133 unsigned char *ybase, *ubase, *vbase; 134 ybase = pdata; 135 ubase = pdata + image_width*image_height; 136 vbase = ubase + image_width*image_height / 4; 137 int j = 0; 138 while (cinfo.next_scanline < cinfo.image_height) 139 { 140 int idx = 0; 141 for (int i = 0; i < image_width; i++) 142 { 143 yuvbuf[idx++] = ybase[i + j * image_width]; 144 yuvbuf[idx++] = ubase[j / 2 * image_width/2 + (i / 2)]; 145 yuvbuf[idx++] = vbase[j / 2 * image_width/2 + (i / 2)]; 146 } 147 row_pointer[0] = yuvbuf; 148 jpeg_write_scanlines(&cinfo, row_pointer, 1); 149 j++; 150 } 151 jpeg_finish_compress(&cinfo); 152 jpeg_destroy_compress(&cinfo); 153 if (yuvbuf != NULL) 154 { 155 free(yuvbuf); 156 } 157 158 FILE *fp = NULL; 159 errno_t err; 160 err = fopen_s(&fp, "C:/Users/chenwenjun/Desktop/ok.jpg", "wb"); 161 if (fp) { 162 fwrite(outbuffer, size, 1, fp); 163 fclose(fp); 164 } 165 else { 166 cout << "nok\n"; 167 } 168 169 free(outbuffer); 170 } 171 172 int main() 173 { 174 std::shared_ptr<char> PicData = NULL; 175 176 //get pic data 177 FILE* fp = NULL; 178 errno_t err; 179 err = fopen_s(&fp, "C:/Users/chenwenjun/Desktop/test.jpg", "rb"); 180 if (!err) { 181 PicData.reset(new char[2 * 2448 * 3264]); 182 fread((void*)PicData.get(), 2 * 2448 * 3264, 1, fp); 183 fclose(fp); 184 } 185 else { 186 cout << "nok\n"; 187 } 188 189 //jpg -> yuv 190 int uPicWidth = 0, uPicHeight = 0; 191 std::string YUVData = jpg_to_yuv420p(PicData.get(), 2 * 2448 * 3264, uPicWidth, uPicHeight); 192 193 //yuv -> jpg 194 yuv420p_to_jpeg((unsigned char*)const_cast<char*>(YUVData.c_str()), uPicWidth, uPicHeight, 100); 195 196 Sleep(1000000); 197 198 return 0; 199 }