在MCU上移植使用libjpeg

原创 嵌入式Lee 2024-11-26 17:46

一. 前言

在一些嵌入式项目中可能需要用到jpeg编解码,比如UVCMJPEG显示, 加载JPEG图片显示到TFT屏幕等,而往往很多通用MCU平台不带硬件jpeg编解码,此时就需要软件实现,这时就希望能有一个好用的jpeg编解码库,恰好官方就提供了这么一个c库即libjpeg。网址为https://libjpeg.sourceforge.net/可以方便将该库应用到自己的项目中,当然其实现也是学习jpeg编解码实现的不二之选。我们就来移植使用该项目源码,源码默认输入输出是针对文件的,我这里对其进行了一些可移植性改造,可以用户配置输入输出接口,这样可以针对缓存或者文件都可以,源码分享在了https://github.com/qinyunti/libjpeg_port.git.

二. 准备

这里先进入网站下载源码和相关的资料

进入网址https://libjpeg.sourceforge.net/

点击上述链接进入https://www.ijg.org/files/

最新版是jpegsr9f,上面还有一些pdf资料可以参考。

解压下载的压缩包jpegsr9f.zip

里面提供了VS相关的工程可以按照说明直接构建,但是我们这里是要移植到MCU上,所以只使用其源码,其他很多的图像格式处理相关的文件也暂时不添加以后有需要可以使用。

需要使用的源码如下,即只需要j开头的ch文件。

src|-- jaricom.c|-- jcapimin.c|-- jcapistd.c|-- jcarith.c|-- jccoefct.c|-- jccolor.c|-- jcdctmgr.c|-- jchuff.c|-- jcinit.c|-- jcmainct.c|-- jcmarker.c|-- jcmaster.c|-- jcomapi.c|-- jcparam.c|-- jcprepct.c|-- jcsample.c|-- jctrans.c|-- jdapimin.c|-- jdapistd.c|-- jdarith.c|-- jdatadst.c|-- jdatasrc.c|-- jdcoefct.c|-- jdcolor.c|-- jddctmgr.c|-- jdhuff.c|-- jdinput.c|-- jdmainct.c|-- jdmarker.c|-- jdmaster.c|-- jdmerge.c|-- jdpostct.c|-- jdsample.c|-- jdtrans.c|-- jerror.c|-- jfdctflt.c|-- jfdctfst.c|-- jfdctint.c|-- jidctflt.c|-- jidctfst.c|-- jidctint.c|-- jmemmgr.c|-- jquant1.c|-- jquant2.c`-- jutils.c
0 directories, 45 files
inc/|-- jdct.h|-- jerror.h|-- jinclude.h|-- jioport.h|-- jmemsys.h|-- jmorecfg.h|-- jpegint.h|-- jpeglib.h`-- jversion.h
0 directories, 9 files
port/|-- jioport.c|-- jmemansi.c|-- jmemdos.c|-- jmemmac.c|-- jmemname.c|-- jmemnobs.c`-- jmemport.c
0 directories, 7 files

我这里的源码如下

三. 配置

使用jconfig.h进行编译时静态配置

该文件由源码目录下的jconfig.txt生成,我们这里复制该文件手动修改为jconfig.h添加到自己的工程中去。

会有很多warning: unused parameter所以我们直接

添加编译参数-Wno-unused-variable,-Wno-unused-parameter忽略该告警。

3.1适配mem接口

添加jmemport.c

我这里不使用stdlib.hmallocfree所以

jpegsr9f/inc/jconfig.h

删除#define HAVE_STDLIB_H

参考jmemnobs.c新建我们自己的jmemport.c

实现以下接口

jpeg_get_small

jpeg_free_small

jpeg_get_large

jpeg_free_large

jpeg_mem_available

jpeg_open_backing_store

jpeg_mem_init

jpeg_mem_term

#include "FreeRTOS.h"//#ifndef HAVE_STDLIB_H   /*  should declare malloc(),free() *///extern void * malloc JPP((size_t size));//extern void free JPP((void *ptr));//#endif

替换mallocfree为我们自己的实现,我这里使用的是freertos

pvPortMallocvPortFree,如果支持标准cmallocfree则可以使用jmemansi.c

实现如下

/* * jmemport.c * * Copyright (C) 1992-1996, Thomas G. Lane. * Modified 2019 by Guido Vollbeding. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file provides a really simple implementation of the system- * dependent portion of the JPEG memory manager.  This implementation * assumes that no backing-store files are needed: all required space * can be obtained from malloc(). * This is very portable in the sense that it'll compile on almost anything, * but you'd better have lots of main memory (or virtual memory) if you want * to process big images. * Note that the max_memory_to_use option is respected by this implementation. */
#define JPEG_INTERNALS#include "jinclude.h"#include "jpeglib.h"#include "jmemsys.h" /* import the system-dependent declarations */#include "FreeRTOS.h"//#ifndef HAVE_STDLIB_H /* should declare malloc(),free() *///extern void * malloc JPP((size_t size));//extern void free JPP((void *ptr));//#endif

/* * Memory allocation and freeing are controlled by the regular library * routines malloc() and free(). */
GLOBAL(void *)jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject){ void* ret; ret = (void *) pvPortMalloc(sizeofobject); #if JPEG_LOG jprintf("jpeg_get_small %p size:%ld r\n",ret, sizeofobject); #endif return ret;}
GLOBAL(void)jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject){ #if JPEG_LOG jprintf("jpeg_free_small %p \r\n",object); #endif vPortFree(object);}

/* * "Large" objects are treated the same as "small" ones. * NB: although we include FAR keywords in the routine declarations, * this file won't actually work in 80x86 small/medium model; at least, * you probably won't be able to process useful-size images in only 64KB. */
GLOBAL(void FAR *)jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject){ void* ret; ret = (void FAR *) pvPortMalloc(sizeofobject); #if JPEG_LOG jprintf("jpeg_get_small %p size:%ld r\n",ret, sizeofobject); #endif return ret;}
GLOBAL(void)jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject){ #if JPEG_LOG jprintf("jpeg_free_large %p \r\n",object); #endif vPortFree(object);}

/* * This routine computes the total memory space available for allocation. */
GLOBAL(long)jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, long max_bytes_needed, long already_allocated){ if (cinfo->mem->max_memory_to_use) return cinfo->mem->max_memory_to_use - already_allocated;
/* Here we say, "we got all you want bud!" */ return max_bytes_needed;}

/* * Backing store (temporary file) management. * Since jpeg_mem_available always promised the moon, * this should never be called and we can just error out. */
GLOBAL(void)jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, long total_bytes_needed){ ERREXIT(cinfo, JERR_NO_BACKING_STORE);}

/* * These routines take care of any system-dependent initialization and * cleanup required. Here, there isn't any. */
GLOBAL(long)jpeg_mem_init (j_common_ptr cinfo){ return 0; /* just set max_memory_to_use to 0 */}
GLOBAL(void)jpeg_mem_term (j_common_ptr cinfo){ /* no work */}

jdatadst.cmalloc/free替换

jpegsr9f/src/jdatadst.c

#ifndef HAVE_STDLIB_H   /*  should declare malloc(),free() */extern void * malloc JPP((size_t size));extern void free JPP((void *ptr));#endif

不使用stdlib则这里依赖外部实现的mallocfree,这里可移植性不好,这里直接依赖mem接口即。

注释掉

//#ifndef HAVE_STDLIB_H   /*  should declare malloc(),free() *///extern void * malloc JPP((size_t size));//extern void free JPP((void *ptr));//#endif

后面的malloc改为jpeg_get_small

free改为jpeg_free_small

nextbuffer = (JOCTET *) malloc(nextsize);

改为

nextbuffer = (JOCTET *) jpeg_get_small(0,nextsize);

dest->newbuffer = *outbuffer = (unsigned char *) malloc(OUTPUT_BUF_SIZE);

改为

dest->newbuffer = *outbuffer = (unsigned char *) jpeg_get_small(0,OUTPUT_BUF_SIZE);

free(dest->newbuffer);

改为

jpeg_free_small(0,dest->newbuffer,0);

jpegsr9f/src/jdatadst.c中需要

#include "jmemsys.h"

3.2不使用getenv

jpegsr9f/src/jmemmgr.c

#ifndef NO_GETENV#ifndef HAVE_STDLIB_H   /*  should declare getenv() */extern char * getenv JPP((const char * name));#endif#endif

我们不使用getenv则需要定义NO_GETENV

jpegsr9f/inc/jconfig.h中添加

#define NO_GETENV

3.3适配IO操作

我们在jpegsr9f/inc/jconfig.h

定义#define JPEG_USE_FILE_IO_CUSTOM

不定义JPEG_HAVE_FILE_IO_CUSTOM

走如下路线

即用户需要实现

jfread
jfwrite
jfflush
jferror

jpegsr9f/inc/jinclude.h中,FILE*改为void*,这样可移植

extern size_t jfread(void * __ptr, size_t __size, size_t __n, FILE * __stream);extern size_t jfwrite(const void * __ptr, size_t __size, size_t __n, FILE * __stream);extern int    jfflush(FILE * __stream);extern int    jferror(FILE * __fp);
改为extern size_t jfread(void * __ptr, size_t __size, size_t __n, void * __stream);extern size_t jfwrite(const void * __ptr, size_t __size, size_t __n, void * __stream);extern int    jfflush(void * __stream);extern int    jferror(void * __fp);

为了可移植性

jdatadst.c

jdatasrc.c,

jmemsys.h

jpeglib.h

FILE改为void

为了方便适配不同的输入输出方式,我这里添加了jioport.c/jioport.h可以用户设置对应的接口

h文件

#ifndef JPEG_IOPORT_H#define JPEG_IOPORT_H
#ifdef __cplusplus extern "C" {#endif
#include #include
typedef struct{  size_t (*read)(void * __ptr, size_t __size, size_t __n, void * __stream);  size_t (*write)(const void * __ptr, size_t __size, size_t __n, void * __stream);  int    (*flush)(void * __stream);  int    (*error)(void * __fp);  size_t infile_len;} jioport_st;
size_t jfread(void * __ptr, size_t __size, size_t __n, void * __stream);size_t jfwrite(const void * __ptr, size_t __size, size_t __n, void * __stream);int    jfflush(void * __stream);int    jferror(void * __fp);
void jioport_set(jioport_st* port);
#ifdef __cplusplus}#endif
#endif

c文件

#include "jconfig.h"#include "jioport.h"
jioport_st* s_jioport = (jioport_st*)0;
size_t jfread(void * __ptr, size_t __size, size_t __n, void * __stream){  #if JPEG_LOG  jprintf("jfread ptr:%p size:%ld n:%ld stream:%p\r\n",__ptr,__size,__n,__stream);  #endif  if((s_jioport != (jioport_st*)0) && (s_jioport->read != 0))  {    return s_jioport->read(__ptr, __size, __n, __stream);  }  return 0;}
size_t jfwrite(const void * __ptr, size_t __size, size_t __n, void * __stream){  #if JPEG_LOG  jprintf("jfwrite ptr:%p size:%ld n:%ld stream:%p\r\n",__ptr,__size,__n,__stream);  #endif  if((s_jioport != (jioport_st*)0) && (s_jioport->write != 0))  {    return s_jioport->write(__ptr, __size, __n, __stream);  }  return 0;}
int    jfflush(void * __stream){  #if JPEG_LOG  jprintf("jfflush stream:%p\r\n",__stream);  #endif  if((s_jioport != (jioport_st*)0) && (s_jioport->flush != 0))  {    return s_jioport->flush(__stream);  }  return 0;}
int    jferror(void * __fp){  #if JPEG_LOG  jprintf("jferror fp:%p\r\n",__fp);  #endif  if((s_jioport != (jioport_st*)0) && (s_jioport->error != 0))  {    return s_jioport->flush(__fp);  }  return 0;}
void jioport_set(jioport_st* port){  s_jioport = port;}

3.4适配打印

jpegsr9f/src/jerror.c

注释掉 //exit(EXIT_FAILURE);

因为一般MCU平台没有exit

fprintf(stderr, "%s\n", buffer);

改为

jprintf("%s\n", buffer);

jpegsr9f/inc/jconfig.h中实现jprintf

#include 
#define jprintf(fmt, arg...) printf(fmt, ##arg)

四. 测试

可以直接在pc上测试,参加git源码

编译

./build.sh

4.1 编码

源码见

jpeg_encode_buffer.c/h

encode_test.c

注意输入bmp图片必须是24位格式,且一行数据量要是4的倍数,widthx34的倍数.

./encode_test testimg.bmp testimg.jpg 320 240 50

生成testimg.jpg如下

4.2解码

源码见

jpeg_decode_buffer.c/h

decode_test.c

./decode_test testimg.jpg testimg.rgb 320

使用YUView软件查看输出文件testimg.rgb

五. 总结

ligjpegTom LaneIJG开发的对jpeg的实现,广泛使用。本文分享了将其进行简单的移植改造适合MCU等任意嵌入式平台使用。

以上测试是在pc上进行,在mcu上比如freertos上则只需要使用jmemport.c,如果不是使用freertos则替换自己的动态分配接口,使用jconfig.h修改#define jprintf(fmt, arg...) printf(fmt, ##arg)宏为自己的打印宏即可。









评论
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 105浏览
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 147浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 163浏览
  • 临近春节,各方社交及应酬也变得多起来了,甚至一月份就排满了各式约见。有的是关系好的专业朋友的周末“恳谈会”,基本是关于2025年经济预判的话题,以及如何稳定工作等话题;但更多的预约是来自几个客户老板及副总裁们的见面,他们为今年的经济预判与企业发展焦虑而来。在聊天过程中,我发现今年的聊天有个很有意思的“点”,挺多人尤其关心我到底是怎么成长成现在的多领域风格的,还能掌握一些经济趋势的分析能力,到底学过哪些专业、在企业管过哪些具体事情?单单就这个一个月内,我就重复了数次“为什么”,再辅以我上次写的:《
    牛言喵语 2025-01-22 17:10 18浏览
  •  光伏及击穿,都可视之为 复合的逆过程,但是,复合、光伏与击穿,不单是进程的方向相反,偏置状态也不一样,复合的工况,是正偏,光伏是零偏,击穿与漂移则是反偏,光伏的能源是外来的,而击穿消耗的是结区自身和电源的能量,漂移的载流子是 客席载流子,须借外延层才能引入,客席载流子 不受反偏PN结的空乏区阻碍,能漂不能漂,只取决于反偏PN结是否处于外延层的「射程」范围,而穿通的成因,则是因耗尽层的过度扩张,致使跟 端子、外延层或其他空乏区 碰触,当耗尽层融通,耐压 (反向阻断能力) 即告彻底丧失,
    MrCU204 2025-01-17 11:30 179浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 46浏览
  • 本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。源码修改修改源码根目录下文件device/rockchip/rk3562/package_performance.xml并添加以下内容,注意"+"号为添加内容,"com.tencent.mm"为AP
    Industio_触觉智能 2025-01-17 14:09 161浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 95浏览
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 73浏览
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 121浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 318浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦