最近遇到个需求,已有全景图和其中的人脸坐标,将人脸小图从全景图中抠出来,最开始使用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 }

 

 

 

  

 

posted on 2018-06-24 00:30  陈不白  阅读(3151)  评论(0编辑  收藏  举报