jpg转yuv420抠图后转为jpg
最近遇到个需求,已有全景图和其中的人脸坐标,将人脸小图从全景图中抠出来,最开始使用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):
#include <windows.h>
#include <iostream>
#include <vector>
#include <memory>
#include <jpeglib.h> #define NEW_BUFFER(param, len) if(!param)\
{ param = new unsigned char[len];}\
else { delete param; param = new unsigned char[len];}; using namespace std; unsigned char** yuvptr_[];
std::vector< std::vector<unsigned char*> > yuvbuf_();
unsigned char* m_pYbuffer = NULL;
unsigned char* m_pUbuffer = NULL;
unsigned char* m_pVbuffer = NULL; std::string jpg_to_yuv420p(char* pBuffer, int nSize, int &uPicWidth, int &uPicHeight)
{
struct jpeg_error_mgr e_;
jpeg_decompress_struct info_;
info_.err = jpeg_std_error(&e_);
jpeg_create_decompress(&info_);
jpeg_mem_src(&info_, (unsigned char*)pBuffer, nSize); //// 指定图片在内存的地址及大小
jpeg_read_header(&info_, );
info_.out_color_space = JCS_YCbCr;
info_.raw_data_out = ;
info_.do_fancy_upsampling = FALSE;
jpeg_start_decompress(&info_); for (int i = ; i < ; ++i)
{
yuvbuf_[i].resize(info_.output_width);
yuvptr_[i] = &yuvbuf_[i][];
} int nLen = info_.output_width * info_.output_height; NEW_BUFFER(m_pYbuffer, nLen);
NEW_BUFFER(m_pUbuffer, nLen);
NEW_BUFFER(m_pVbuffer, nLen); unsigned char* row = m_pYbuffer;
yuvptr_[][] = row;
for (int i = ; i < info_.output_height; ++i, row += info_.output_width)
{
yuvptr_[][i] = row; //y 分量空间初始化
} row = m_pUbuffer;
for (int i = ; i < info_.output_height; i += , row += info_.output_width / )
{
yuvptr_[][i / ] = row; //u 分量初始化 } row = m_pVbuffer;
for (int i = ; i < info_.output_height; i += , row += info_.output_width / )
{
yuvptr_[][i / ] = row; //v 分量初始化
} for (int i = ; i < info_.output_height; i += )
{
int nRows = ;
if ((info_.output_height) < (i + ))
{
nRows = info_.output_height - i;
}
jpeg_read_raw_data(&info_, yuvptr_, nRows);
yuvptr_[] += ;
yuvptr_[] += ;
yuvptr_[] += ;
}
uPicWidth = info_.image_width;
uPicHeight = info_.image_height; std::string Y((char*)yuvbuf_[][], uPicWidth*uPicHeight); std::string U((char*)yuvbuf_[][], uPicWidth*uPicHeight / ); std::string V((char*)yuvbuf_[][], uPicWidth*uPicHeight / ); std::string YUV = Y + U + V; cout << YUV.size() << endl; jpeg_finish_decompress(&info_);
jpeg_destroy_decompress(&info_); return YUV;
} int yuv420p_to_jpeg(unsigned char* pdata, int image_width, int image_height, int quality)
{
if (image_width == || image_height == ) {
cout << "err param\n";
return ;
}
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
unsigned char *outbuffer;
unsigned long size = ;
jpeg_mem_dest(&cinfo, &outbuffer, &size); cinfo.image_width = image_width; // image width and height, in pixels
cinfo.image_height = image_height;
cinfo.input_components = ; // # of color components per pixel
cinfo.in_color_space = JCS_YCbCr; //colorspace of input image
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE); //////////////////////////////
// cinfo.raw_data_in = TRUE;
cinfo.jpeg_color_space = JCS_YCbCr;
cinfo.comp_info[].h_samp_factor = ;
cinfo.comp_info[].v_samp_factor = ;
/////////////////////////
jpeg_start_compress(&cinfo, TRUE); JSAMPROW row_pointer[]; unsigned char *yuvbuf;
if ((yuvbuf = (unsigned char *)malloc(image_width * )) != NULL)
memset(yuvbuf, , image_width * ); unsigned char *ybase, *ubase, *vbase;
ybase = pdata;
ubase = pdata + image_width*image_height;
vbase = ubase + image_width*image_height / ;
int j = ;
while (cinfo.next_scanline < cinfo.image_height)
{
int idx = ;
for (int i = ; i < image_width; i++)
{
yuvbuf[idx++] = ybase[i + j * image_width];
yuvbuf[idx++] = ubase[j / * image_width/ + (i / )];
yuvbuf[idx++] = vbase[j / * image_width/ + (i / )];
}
row_pointer[] = yuvbuf;
jpeg_write_scanlines(&cinfo, row_pointer, );
j++;
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
if (yuvbuf != NULL)
{
free(yuvbuf);
} FILE *fp = NULL;
errno_t err;
err = fopen_s(&fp, "C:/Users/chenwenjun/Desktop/ok.jpg", "wb");
if (fp) {
fwrite(outbuffer, size, , fp);
fclose(fp);
}
else {
cout << "nok\n";
} free(outbuffer);
} int main()
{
std::shared_ptr<char> PicData = NULL; //get pic data
FILE* fp = NULL;
errno_t err;
err = fopen_s(&fp, "C:/Users/chenwenjun/Desktop/test.jpg", "rb");
if (!err) {
PicData.reset(new char[ * * ]);
fread((void*)PicData.get(), * * , , fp);
fclose(fp);
}
else {
cout << "nok\n";
} //jpg -> yuv
int uPicWidth = , uPicHeight = ;
std::string YUVData = jpg_to_yuv420p(PicData.get(), * * , uPicWidth, uPicHeight); //yuv -> jpg
yuv420p_to_jpeg((unsigned char*)const_cast<char*>(YUVData.c_str()), uPicWidth, uPicHeight, ); Sleep(); return ;
}
jpg转yuv420抠图后转为jpg的更多相关文章
- jquery中定义数组并给数组赋值后转为json格式为[]问题的解决
一.问题描述:jquery定义一个空数组,并赋值,结果转为json格式后打印值为空 我原本是这样写的,但是show_data值一直为[] var export_data = [];export_dat ...
- 成 功 的 背 后 !( 致给所有IT人员)
转载了这篇文章,希望能对自己和看到这篇博客的人有所激励. 成功的背后,有着许多不为人知的故事,而正是这些夹杂着泪水和汗水的过去,才成就了一个个走向成功的普通人. ------------------- ...
- PS教程1000例
http://www.missyuan.com/thread-446934-1-1.html Photoshop绘制逼真头发发丝效果http://www.missyuan.com/thread-446 ...
- 微信小程序音乐播放器
写在前面 1.入门几天小白的作品,希望为您有帮助,有好的意见或简易烦请赐教 2.微信小程序审核音乐类别已经下架,想要发布选题需慎重.附一个参考链接,感谢https://www.hishop.com.c ...
- AES加密
package com.edu.hpu; import java.math.BigInteger; import java.security.MessageDigest; import java.se ...
- 解决IE8下不兼容rgba()的解决办法
rgba()是css3的新属性,所以IE8及以下浏览器不兼容,这怎么办呢?终于我找到了解决办法. 解决办法 我们先来解释以下rgba rgba: rgba的含义,r代表red,g代表green,b代表 ...
- C#中POST数据和接收的几种方式(抛砖引玉)
POST方式提交数据,一种众所周知的方式: html页面中使用form表单提交,接收方式,使用Request.Form[""]或Request.QueryString[" ...
- ie6,ie7,ie8 css bug兼容解决方法
IE浏览器以不支持大量的css 属性出名,同时也因其支持的css属性中存在大量bug. 这里收集了好多的bug以及其解决的办法,都在这个文章里面记录下来了!希望以后解决类似问题的时候能够快速解决,也希 ...
- 案例1.用Ajax实现用户名的校验
用Ajax实现用户名的校验 java的验证类 public class UserDao { public boolean checkUserName(String name) { //这里的name是 ...
随机推荐
- Python测试框架之Unittest梳理
1. 2.
- 人群密度估计 CrowdCount
最近在看人群密度估计方面的东西,把博客看到的一些方法简单总结一下,后续继续添加. 1.论文<CrowdNet: A Deep Convolutional Network for DenseCro ...
- ios 中pickerView城市选择和UIDatePicker生日选择
代码详见压缩包
- C# ModBus Tcp读写数据 与服务器进行通讯
前言 本文将使用一个NuGet公开的组件技术来实现一个ModBus TCP的客户端,方便的对Modbus tcp的服务器进行读写,这个服务器可以是电脑端C#设计的,也可以是PLC实现的,也可以是其他任 ...
- 基于 Jenkins 构建持续集成任务
1.1 Jenkins 配置使用心得 我是在windows10上安装的,安装过程很简单,从官网上下载下来msi安装包,双击执行就好了.安装程序完成后会自动打开http://localhost:8080 ...
- if 循环
age_of_princal = 56guess_age = int(input(">>:")) if guess_age == age_of_princal: pri ...
- 查看linux系统CPU及内存配置
总核数 = 物理CPU个数 X 每颗物理CPU的核数 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 查看物理CPU个数 cat /proc/cpuinfo| grep & ...
- Spring Boot/Spring Cloud
104.什么是 spring boot? 在Spring框架这个大家族中,产生了很多衍生框架,比如 Spring.SpringMvc框架等,Spring的核心内容在于控制反转(IOC) ...
- bootstrap弹出模态框会给body加padding的解决方法
bootstrap弹出模态框会给body加padding,导致页面缩放的解决方法: 在页面或是css文件里加上($paddingSize为less变量,需要改成像素或是其他单位,如12px,1rem) ...
- 根据CPU核心数确定线程池并发线程数
一.抛出问题 关于如何计算并发线程数,一般分两派,来自两本书,且都是好书,到底哪个是对的?问题追踪后,整理如下: 第一派:<Java Concurrency in Practice>即&l ...