最近遇到個需求,已有全景圖和其中的人臉坐標,將人臉小圖從全景圖中摳出來,最開始使用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 }