JPEG 解码示例
源码位置: samples/media/jpeg 查看源码
功能说明
演示如何使用 IJG JPEG 库从内存缓冲区读取 JPEG 数据并解码为 RGB565 格式。本示例展示了如何设置自定义错误处理、从内存读取 JPEG 数据、配置解压缩参数、逐行解码图像数据,以及将 RGB888 转换为 RGB565 格式。
IJG (Independent JPEG Group) JPEG 库是一个广泛使用的 JPEG 图像压缩和解压缩库,支持多种输入输出格式和颜色空间。
硬件连接
无需外部连接,JPEG 解码为纯软件图像处理库。
示例内容
定义自定义错误处理结构(使用
setjmp/longjmp进行错误恢复)实现自定义错误处理函数
my_error_exit实现 RGB888 到 RGB565 的转换函数
实现 JPEG 到 RGB565 的转换函数:
设置错误处理
创建解压缩对象
指定内存数据源
读取 JPEG 文件头
设置输出颜色空间为 RGB
启动解压缩器
获取图像尺寸
逐行读取并转换像素数据
完成解压缩并释放资源
使用内置的测试 JPEG 数据(1x1 像素)进行转换测试
打印转换结果和图像尺寸
释放 RGB565 数据内存
编译
重要提示:在编译前,请先确认您使用的开发板型号。SDK 目前支持以下开发板:
arcs_evb - ARCS EVB 评估板
arcs_mini - ARCS Mini 开发板
根据您的开发板型号,选择对应的编译命令:
在示例目录下执行编译:
# 使用 arcs_evb 开发板
./build.sh -C -DBOARD=arcs_evb
# 或使用 arcs_mini 开发板
./build.sh -C -DBOARD=arcs_mini
Note
如果在 SDK 根目录执行,需要指定示例路径:
# 使用 arcs_evb 开发板
./build.sh -C -S samples/<示例路径> -DBOARD=arcs_evb
# 或使用 arcs_mini 开发板
./build.sh -C -S samples/<示例路径> -DBOARD=arcs_mini
Note
确保已安装对应的工具链。
烧录
编译完成后,使用 SDK tools 目录下的 cskburn 工具烧录固件:
./tools/burn/cskburn -s /dev/ttyUSB0 -b 3000000 0x0 build/arcs.bin -C arcs
Note
烧录参数说明:
-s /dev/ttyUSB0:串口设备路径,需要根据实际情况修改 - Linux 系统:通常是/dev/ttyUSB0或/dev/ttyACM0- 可通过ls /dev/tty*命令查看可用串口设备 - 不同开发板或 USB 转串口芯片可能使用不同的设备名-b 3000000:烧录波特率(3Mbps)0x0:烧录起始地址build/arcs.bin:编译生成的固件路径-C arcs:芯片类型
注意事项:
确保开发板已正确连接到电脑
如果无法识别串口设备,请检查 USB 连接线是否正常,或尝试其他 USB 端口
预期输出
********Arcs SDK 0.1.0 @ v0.0.23.temp.docs-96-gf56c5084660d********
Running on hart-id: 1
I/elog [1034:42:44.159 1 elog_async] EasyLogger V2.2.99 is initialize success.
[I][sample] JPEG (from memory) to RGB565 converter sample
[I][sample] Conversion completed successfully
[I][sample] Image dimensions: 1 x 1
说明:
输出开头包含系统启动信息和日志系统初始化信息
示例使用内置的 1x1 像素 JPEG 数据进行测试
转换成功后显示图像尺寸
如果转换失败,会输出 “Conversion failed”
核心 API
API |
说明 |
|---|---|
|
创建 JPEG 解压缩对象 |
|
指定内存数据源 |
|
读取 JPEG 文件头并获取图像信息 |
|
启动解压缩过程 |
|
读取一行或多行扫描线数据 |
|
完成解压缩 |
|
销毁解压缩对象并释放资源 |
|
获取标准错误处理管理器 |
关键代码
/* 自定义错误处理结构 */
struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
/* 自定义错误处理函数 */
METHODDEF(void)
my_error_exit(j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
/* RGB888 到 RGB565 转换 */
static uint16_t rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b)
{
uint16_t rgb565 = ((r & 0xF8) << 8) | /* 5 bits for red */
((g & 0xFC) << 3) | /* 6 bits for green */
((b & 0xF8) >> 3); /* 5 bits for blue */
return rgb565;
}
/* JPEG 到 RGB565 转换函数 */
int jpeg_memory_to_rgb565(const unsigned char *jpeg_data, size_t jpeg_size,
uint16_t **rgb565_output, int *width, int *height)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
JSAMPARRAY buffer;
int row_stride;
uint16_t *rgb565_buffer = NULL;
/* 设置错误处理 */
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/* 建立错误恢复点 */
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
if (rgb565_buffer != NULL) {
free(rgb565_buffer);
}
return -1;
}
/* 初始化解压缩对象 */
jpeg_create_decompress(&cinfo);
/* 指定内存数据源 */
jpeg_mem_src(&cinfo, jpeg_data, jpeg_size);
/* 读取文件头 */
(void) jpeg_read_header(&cinfo, TRUE);
/* 设置输出颜色空间为 RGB */
cinfo.out_color_space = JCS_RGB;
/* 启动解压缩器 */
(void) jpeg_start_decompress(&cinfo);
/* 获取图像尺寸 */
*width = cinfo.output_width;
*height = cinfo.output_height;
/* 计算行步长 */
row_stride = cinfo.output_width * cinfo.output_components;
/* 分配行缓冲区 */
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
/* 分配 RGB565 输出缓冲区 */
rgb565_buffer = (uint16_t *)malloc(
cinfo.output_width * cinfo.output_height * sizeof(uint16_t));
/* 逐行处理 */
uint16_t *current_line = rgb565_buffer;
while (cinfo.output_scanline < cinfo.output_height) {
/* 读取一行像素 */
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
/* 转换为 RGB565 */
for (int i = 0; i < cinfo.output_width; i++) {
uint8_t r = buffer[0][i * 3]; /* Red */
uint8_t g = buffer[0][i * 3 + 1]; /* Green */
uint8_t b = buffer[0][i * 3 + 2]; /* Blue */
current_line[i] = rgb888_to_rgb565(r, g, b);
}
current_line += cinfo.output_width;
}
/* 完成解压缩 */
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
/* 设置输出指针 */
*rgb565_output = rgb565_buffer;
return 0;
}
颜色格式说明
RGB888 (24-bit)
每个像素 3 字节(红、绿、蓝各 1 字节)
红色:8 位(0-255)
绿色:8 位(0-255)
蓝色:8 位(0-255)
RGB565 (16-bit)
每个像素 2 字节(16 位)
红色:5 位(高 5 位)
绿色:6 位(中间 6 位)
蓝色:5 位(低 5 位)
转换公式:
RGB565 = (R & 0xF8) << 8 | (G & 0xFC) << 3 | (B & 0xF8) >> 3
配置说明
必需配置
CONFIG_SDK_MODULE_IJG=y: 启用 IJG JPEG 库模块
可选配置
CONFIG_MEM_CONFIG=y: 启用内存配置(如果需要)
注意事项
错误处理: IJG JPEG 库使用
setjmp/longjmp机制进行错误处理,必须设置自定义错误处理函数,否则错误会导致程序崩溃内存管理: 使用
jpeg_mem_src()时,JPEG 数据必须完整存在于内存中,不能是流式数据资源清理: 解压缩完成后必须调用
jpeg_finish_decompress()和jpeg_destroy_decompress()释放资源输出缓冲区: RGB565 输出缓冲区由调用者分配,使用完毕后必须由调用者释放(使用
free())颜色空间: 输出颜色空间设置为
JCS_RGB,输出为每像素 3 字节的 RGB888 格式,需要手动转换为 RGB565行缓冲区: 使用
alloc_sarray()分配的行缓冲区由 JPEG 库管理,无需手动释放图像尺寸: 图像尺寸在
jpeg_start_decompress()后通过cinfo.output_width和cinfo.output_height获取逐行处理: 使用
jpeg_read_scanlines()逐行读取数据,适合处理大图像,避免一次性加载整个图像到内存内存数据源: 使用 IJG 库提供的
jpeg_mem_src()函数从内存读取 JPEG 数据,该函数内部处理了内存源管理,无需手动实现