技术专栏
使用libjpeg将yuv数据转换为内存中的jpg
最近在做人脸识别的东西,需要将海思采集到的YUV视频数据扔给第三方人脸sdk接口进行人脸识别,然后将人脸的小图抠出来发送给一个第三方的盒子。
libjpeg
下载连接
http://www.ijg.org/files/
此处用的版本是 jpegsrc.v9d.tar.gz
交叉编译
libjpeg 的交叉编译比较简单,没有报错
# root @ gentoo4wzy in /hisi_ext/3rd/test_jpeg/jpeg-9d [11:44:43]
$ ./configure --prefix=/hisi_ext/3rd/test_jpeg/_install --host=aarch64-himix100-linux
# root @ gentoo4wzy in /hisi_ext/3rd/test_jpeg/jpeg-9d [11:46:02]
$ make && make install
# root @ gentoo4wzy in /hisi_ext/3rd/test_jpeg/jpeg-9d [11:46:23]
$ ls ../_install/lib
libjpeg.a libjpeg.la libjpeg.so libjpeg.so.9 libjpeg.so.9.4.0 pkgconfig
此后就可以在自己的代码中加上 #include "jpeglib.h",然后编译的时候加上 -ljpeg 参数
基本用法
具体的详细用法可以参考源码中的 cjpeg.c, example.c, djpeg.c
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
/* 创建 */
jpeg_create_compress(&cinfo);
/* 存到文件和存到内存,二选一 */
/* 存到文件 */
jpeg_stdio_dest(&cinfo, jfp);
/* 存到内存 */
jpeg_mem_dest(&cinfo, jpeg_data, (size_t *)&jpeg_size);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_YCbCr; /* 此处是 YUV444 格式 */
cinfo.dct_method = JDCT_FLOAT;
jpeg_set_defaults(&cinfo);
/* 设置jpeg图像质量,范围 [0,100] */
jpeg_set_quality(&cinfo, 40, TRUE);
/* 开始 */
jpeg_start_compress(&cinfo, TRUE);
/* 处理数据 */
JSAMPROW row_pointer[1];
while (cinfo.next_scanline < cinfo.image_height)
{
/* your code here */
jpeg_write_scanlines( &jpeg,row_pointer, 1);
}
/* 停止 */
jpeg_finish_compress(&cinfo);
/* 销毁 */
jpeg_destroy_compress(&cinfo);
简单说一下, jpeg_mem_dest 函数的第二个参数是个二级指针,指向的是完成编码后的存放jpeg数据的地址,不需要自己分配内存,但是需要自己手动释放内存。
YUV
格式
- planar 平面格式
- 先连续存Y分量,再连续存U(或V)分量,再连续存V(或U)分量
- packed 打包格式
- 每个像素点的 Y、U、V 分量连续交替存储
采样
- YUV420
- YUV 4:2:0 采样,并不是指只采样 U 分量而不采样 V 分量。而是指,在每一行扫描时,只扫描一种色度分量(U 或者 V),和 Y 分量按照 2 : 1 的方式采样。比如,第一行扫描时,YU 按照 2 : 1 的方式采样,那么第二行扫描时,YV 分量按照 2:1 的方式采样。对于每个色度分量来说,它的水平方向和竖直方向的采样和 Y 分量相比都是 2:1 [1]
- YUV422
- YUV 4:2:2 采样,意味着 UV 分量是 Y 分量采样的一半,Y 分量和 UV 分量按照 2 : 1 的比例采样。如果水平方向有 10 个像素点,那么采样了 10 个 Y 分量,而只采样了 5 个 UV 分量[2]
- YUV444
- YUV 4:4:4 采样,意味着 Y、U、V 三个分量的采样比例相同,因此在生成的图像里,每个像素的三个分量信息完整,都是 8 bit,也就是一个字节[3]
YUV 在内存中的排列(不是对应关系)
我这里海思编码出来的YUV格式是 YUV420SP
,对于此格式来说:
- 听说只保留Y分量数据即可得到灰度图像(本人没存过)
- 在内存中的大小:
- Y = w * h;
- U = Y / 4;
- V = Y / 4;
- Total = Y+U+V = w * h * 3 / 2; 同时可根据偏移计算得到各个分量的地址
范例
yuv420sp_to_yuv420p
void yuv420sp_to_yuv420p(unsigned char* yuv420sp, unsigned char* yuv420p, int width, int height)
{
int i, j;
int y_size = width * height;
unsigned char* y = yuv420sp;
unsigned char* uv = yuv420sp + y_size;
unsigned char* y_tmp = yuv420p;
unsigned char* u_tmp = yuv420p + y_size;
unsigned char* v_tmp = yuv420p + y_size * 5 / 4;
// y
memcpy(y_tmp, y, y_size);
// u
for (j = 0, i = 0; j < y_size/2; j+=2, i++)
{
u_tmp[i] = uv[j];
v_tmp[i] = uv[j+1];
}
}
yuv420p_to_yuv420sp
void yuv420p_to_yuv420sp(unsigned char* yuv420p, unsigned char* yuv420sp, int width, int height)
{
int i, j;
int y_size = width * height;
unsigned char* y = yuv420p;
unsigned char* u = yuv420p + y_size;
unsigned char* v = yuv420p + y_size * 5 / 4;
unsigned char* y_tmp = yuv420sp;
unsigned char* uv_tmp = yuv420sp + y_size;
// y
memcpy(y_tmp, y, y_size);
// u
for (j = 0, i = 0; j < y_size/2; j+=2, i++)
{
// 此处可调整U、V的位置,变成NV12或NV21
#if 01
uv_tmp[j] = u[i];
uv_tmp[j+1] = v[i];
#else
uv_tmp[j] = v[i];
uv_tmp[j+1] = u[i];
#endif
}
}
yuv422sp_to_yuv422p
void yuv422sp_to_yuv422p(unsigned char* yuv422sp, unsigned char* yuv422p, int width, int height)
{
int i, j;
int y_size;
int uv_size;
unsigned char* p_y1;
unsigned char* p_uv;
unsigned char* p_y2;
unsigned char* p_u;
unsigned char* p_v;
y_size = uv_size = width * height;
p_y1 = yuv422sp;
p_uv = yuv422sp + y_size;
p_y2 = yuv422p;
p_u = yuv422p + y_size;
p_v = p_u + width * height / 2;
memcpy(p_y2, p_y1, y_size);
for (j = 0, i = 0; j < uv_size; j+=2, i++)
{
p_u[i] = p_uv[j];
p_v[i] = p_uv[j+1];
}
}
yuv422p_to_yuv422sp
void yuv422p_to_yuv422sp(unsigned char* yuv422p, unsigned char* yuv422sp, int width, int height)
{
int i, j;
int y_size;
int uv_size;
unsigned char* p_y1;
unsigned char* p_uv;
unsigned char* p_y2;
unsigned char* p_u;
unsigned char* p_v;
y_size = uv_size = width * height;
p_y1 = yuv422p;
p_y2 = yuv422sp;
p_u = p_y1 + y_size;
p_v = p_u + width * height / 2;
p_uv = p_y2 + y_size;
memcpy(p_y2, p_y1, y_size);
for (j = 0, i = 0; j < uv_size; j+=2, i++)
{
// 此处可调整U、V的位置,变成NV16或NV61
#if 01
p_uv[j] = p_u[i];
p_uv[j+1] = p_v[i];
#else
p_uv[j] = p_v[i];
p_uv[j+1] = p_u[i];
#endif
}
}
yuv420sp_to_jpg1
此函数可以直接把参数 jpeg_data 来写入文件,然后打开看是不是正常的jpg文件来验证正确性
static long yuv420sp_to_jpg1(int chn_id, int frm_num, int width, int height, unsigned char *yuv_data,unsigned char **jpeg_data, char *save_jpeg)
{
int j = 0;
long jpeg_size = 0;
if (yuv_data == NULL)
{
fprintf(stderr, "\033[0;31m[%s]-%d: the input yuv data is NULL\033[0;39m\n", __FUNCTION__, __LINE__);
return -1;
}
unsigned char *yuvbuf = NULL;
if ((yuvbuf = (unsigned char *)malloc(width * 3)) == NULL)
{
fprintf(stderr, "\033[0;31m[%s]-%d: yuv buf malloc failed\033[0;39m\n", __FUNCTION__, __LINE__);
return -1;
}
memset(yuvbuf, 0, width * 3);
JSAMPROW row_pointer[1];
unsigned char *ybase = NULL;
unsigned char *ubase = NULL;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
FILE * jfp = NULL;
char jpeg_name[128] = {0};
if (save_jpeg != NULL)
{
snprintf(jpeg_name, sizeof(jpeg_name) - 1, "/mnt/mtd/data/test_chn%d_%d.jpg", chn_id, frm_num);
if ((jfp = fopen(jpeg_name, "wb")) == NULL)
{
fprintf(stderr, "\033[0;31m[%s]-%d: can't open jpeg file %s\033[0;39m\n", __FUNCTION__, __LINE__, jpeg_name);
goto EXIT0;
}
jpeg_stdio_dest(&cinfo, jfp);
}
else
{
// write to mem
jpeg_mem_dest(&cinfo, jpeg_data, (size_t *)&jpeg_size);
}
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_YCbCr;
cinfo.dct_method = JDCT_FLOAT;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 40, 1);
jpeg_start_compress(&cinfo, 1);
ybase = yuv_data;
ubase = yuv_data + width * height;
while (cinfo.next_scanline < cinfo.image_height)
{
int idx = 0;
for (int i = 0; i < width; i++) /* 此处是yuv420sp转yuv444 */
{
yuvbuf[idx++] = ybase[i + j * width];
yuvbuf[idx++] = ubase[j / 2 * width + (i / 2) * 2 + 1];
yuvbuf[idx++] = ubase[j / 2 * width + (i / 2) * 2];
}
row_pointer[0] = yuvbuf;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
j++;
}
jpeg_finish_compress(&cinfo);
if (save_jpeg != NULL)
{
if (jfp)
{
fclose(jfp);
jfp = NULL;
remove(jpeg_name);
}
}
EXIT0:
jpeg_destroy_compress(&cinfo);
if (yuvbuf)
{
free(yuvbuf);
yuvbuf = NULL;
}
return jpeg_size;
}
/* 注意:
jpeg_data 是二级指针,
libjpeg库会自动分配内存,
我们用完后要记得手动释放
*/
- 还可以先把YUV数据转换为RGB数据,再设置 cinfo.in_color_space = JCS_RGB; 后进行压缩
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包
点赞
收藏
评论
打赏
- 分享
- 举报
评论
0个
手气红包
暂无数据
相关专栏
-
浏览量:995次2023-06-03 15:58:55
-
浏览量:1481次2023-11-07 15:38:56
-
浏览量:1803次2020-04-14 10:30:04
-
浏览量:1916次2018-01-17 10:44:34
-
浏览量:1218次2023-10-09 19:22:39
-
浏览量:11319次2020-12-22 09:51:03
-
浏览量:804次2023-12-22 10:53:00
-
浏览量:1982次2019-11-28 18:23:26
-
浏览量:2319次2020-08-05 20:49:45
-
浏览量:2035次2018-10-24 21:19:17
-
浏览量:1963次2018-02-04 17:00:44
-
浏览量:6478次2021-04-26 17:28:30
-
浏览量:1074次2023-01-12 12:51:39
-
浏览量:4984次2021-06-28 13:50:26
-
浏览量:7987次2020-12-07 21:44:22
-
浏览量:8963次2020-12-07 17:31:38
-
浏览量:2648次2017-11-22 11:51:03
-
浏览量:1848次2021-12-17 17:19:43
-
浏览量:6731次2020-12-22 09:37:22
置顶时间设置
结束时间
删除原因
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
打赏作者
一休摸鱼
您的支持将鼓励我继续创作!
打赏金额:
¥1
¥5
¥10
¥50
¥100
支付方式:
微信支付
打赏成功!
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报反馈
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
审核成功
发布时间设置
发布时间:
请选择发布时间设置
是否关联周任务-专栏模块
审核失败
失败原因
请选择失败原因
备注
请输入备注