《 IMX6平台交叉编译libjpeg-turbo》 来自https://blog.csdn.net/lang523493505/article/details/81104404
《嵌入式平台使用libjpeg-turbo将YUV420SP保存为jpg》 来自
《libjpeg-turbo使用实例(编解码jpeg、jpg转bmp、bmp转jpg代码)》 来自
1. 本ADAS项目采用usb虚拟串口将图像数据上传到PC实时显示;
2. 由于usb2.0接口带宽限制,不能实时传输1280*720*15帧的YUV420P原图数据,需要采用jpeg编码压缩数据才能传输。
3. 因此采用libjpeg-turbo库,将YUV420P原图编码成JPEG压缩图传输。
window7 + VMware12中安装(ubuntu16.04.3 + SDX2017.4)
硬件环境: zynq7020 + SD + usb2.0虚拟串口传输数据;
软件环境: linux-xlnx-xilinx-v2017.4 (kernel 4.9)
参考《 IMX6平台交叉编译libjpeg-turbo》 来自https://blog.csdn.net/lang523493505/article/details/81104404
解压进入libjpeg-turbo-1.5.3目录,执行如下配置编译命令:
export PWD=`pwd`
export CFLAGS="-march=armv7-a -mtune=cortex-a7 -mfpu=neon -mfloat-abi=hard -D__ARM_HAVE_NEON -D__ARM_NEON__"
./configure --host=arm-linux-gnueabihf --prefix=${PWD}/_install CC="arm-linux-gnueabihf-gcc -O3"
make V=1
make install
安装后在 _install/目录下生成如下目录
../lib/ 库文件目录,将静态库libturbojpeg.a、libjpeg.a添加到项目
../include/ 头文件目录, 将全部copy到项目。
只能采用cmake编译, 编译脚本如下: 注意: 只在window7 + msys2 + sdx2017.4的环境下编译成功(ubuntu16.04 + sdx2017.4编译不过)
#!/bin/bash
# Set these variables to suit your needs
BUILD_PLATFORM="linux-x86_64"
TOOLCHAIN_VERSION="4.9"
ROOT_PWD=$(pwd)
PREFIX=$(pwd)/../_install
cd ${ROOT_PWD}
# It should not be necessary to modify the rest
export HOST=arm-linux-gnueabihf
export CFLAGS="-march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a7"
export LDFLAGS=-pie
export TOOLCHAIN=/opt/Xilinx_SDx_2017.4-final/SDK/2017.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi
cat <toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER ${TOOLCHAIN}/bin/${HOST}-gcc)
set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN}/${HOST})
EOF
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_POSITION_INDEPENDENT_CODE=1 -DCMAKE_INSTALL_PREFIX=${PREFIX} ${ROOT_PWD}
make clean
make
make install
#include "turbojpeg.h"
/// 二进制数据写入文件
static size_t Write2File(char *filename, uint8_t *buf, int size)
{
size_t ret;
FILE *fJpg = fopen(filename, "wb");
if(fJpg == NULL){
printf("Cannot open file %s, %s\n", filename, strerror(errno));
return -1;
}
ret = fwrite(buf, 1, size, fJpg);
fclose(fJpg);
return ret;
}
/*************************************************************************************//**
*\n 函数名称: tyuv2jpeg1()
*\n 功能描述:
*\n YUV420P转jpeg格式
*\n 输入参数:
*\n unsigned char* yuv_buffer : yuv数据
*\n int width : 宽*高
*\n int height
*\n int subsample : TJSAMP_420, TJSAMP_422,TJSAMP_GRAY, etc
*\n 输出参数:
*\n unsigned char** jpeg_buffer : 函数内部malloc生成缓冲,外部需要free()
*\n unsigned long* jpeg_size : jpeg大小
*\n
*\n 返 回 值:
*\n =0: 成功
*\n <0: 失败(错误码)
*\n
*\n -----------------------------------------------------------------------------------
*\n 版本: 修改人: 修改日期: 描述:
*\n V0.1
*****************************************************************************************/
static int tyuv2jpeg(unsigned char* yuv_buffer, int width, int height, int subsample,
unsigned char** jpeg_buffer, unsigned long* jpeg_size)
{
tjhandle handle = NULL;
int flags = 0;
int padding = 1; // 1或4均可,但不能是0
int need_size = 0;
int ret = 0;
int yuv_size = width * height * 3/2;
handle = tjInitCompress();
need_size = tjBufSizeYUV2(width, padding, height, subsample);
if (need_size != yuv_size)
{
tjDestroy(handle);
printf("we detect yuv size: %d, but you give: %d, check again.\n", need_size, yuv_size);
return -1;
}
int quality = 80; // 压缩率(1 ~ 100)
ret = tjCompressFromYUV(handle, yuv_buffer, width, padding, height,
subsample, jpeg_buffer, jpeg_size, quality, flags);
if (ret < 0)
{
printf("compress to jpeg failed: %s\n", tjGetErrorStr());
}
tjDestroy(handle);
return ret;
}
// 测试代码, 将YUV420P图像编码为jpeg图像,并写入文件
int wirt2file(void *yuv_buffer, int width, int height)
{
unsigned char *jpeg_buf = NULL;
unsigned long jpeg_size = 0;
int ret = tyuv2jpeg(src, width, height, TJSAMP_420, &jpeg_buf, &jpeg_size); //函数分配jpeg内存
if (ret < 0)
{
printf("tyuv2jpeg() error");
goto exit1;
}
Write2File("/cam.jpg", jpeg_buf, jpeg_size);
printf("write /cam.jpg,");
exit1:
free(jpeg_buf); // 用户释放内存
return 0;
}
经过测试 JPEG编码时间为: 40毫秒
/*************************************************************************************//**
*\n 函数名称: tyuv2jpeg()
*\n 功能描述:
*\n YUV420P转jpeg格式
*\n 输入参数:
*\n JSAMPLE *pYUVBuffer : yuv数据
*\n int width : 宽 x 高
*\n int height
*\n size_t *dest_size : jpeg缓冲大小
*\n 输出参数:
*\n uint8_t *jpeg_buffer : jpeg图像
*\n size_t *dest_size : jpeg大小
*\n
*\n 返 回 值:
*\n =0: 成功
*\n <0: 失败(错误码)
*\n
*\n -----------------------------------------------------------------------------------
*\n 版本: 修改人: 修改日期: 描述:
*\n V0.1 罗先能 2018.12.12 创建
*****************************************************************************************/
static int tyuv2jpeg(/*OUT*/uint8_t *jpeg_buffer, /*INOUT*/ size_t *dest_size,
JSAMPLE *pYUVBuffer, int width, int height)
{
JSAMPROW row_pointer[1];
int row_stride;
int i = 0, j = 0;
unsigned char yuvbuf[width * 3];
unsigned char *pY, *pU, *pV;
int ulen;
if(pYUVBuffer == NULL){
printf("pBGRBuffer is NULL!\n");
return -1;
}
struct jpeg_error_mgr jerr;
struct jpeg_compress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_mem_dest(&cinfo, &jpeg_buffer, (unsigned long*)dest_size);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_YCbCr;
cinfo.dct_method = JDCT_ISLOW;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 80, TRUE);
jpeg_start_compress(&cinfo, TRUE);
row_stride = cinfo.image_width * 3;
ulen = width * height / 4;
pY = pYUVBuffer;
pU = pYUVBuffer + width*height;
pV = pYUVBuffer + width*height + ulen;
j = 1;
while (cinfo.next_scanline < cinfo.image_height) {
if(j % 2 == 1 && j > 1){
pU = pYUVBuffer + width*height + width / 2 * (j / 2);
pV = pYUVBuffer + width*height * 5 / 4 + width / 2 *(j / 2);
}
for(i = 0; i < width; i += 2){
yuvbuf[i*3] = *pY++;
yuvbuf[i*3 + 1] = *pU;
yuvbuf[i*3 + 2] = *pV;
yuvbuf[i*3 + 3] = *pY++;
yuvbuf[i*3 + 4] = *pU++;
yuvbuf[i*3 + 5] = *pV++;
}
row_pointer[0] = yuvbuf;
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
j++;
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
return 0;
}
经过测试 JPEG编码时间为: 99mS
方案一,效率高。