库的移植和使用---例子:JPEG的解码和编码

news/2024/9/23 9:34:47

目录
  • 开源库移植步骤
    • [1]:下载库的源码包
    • [2]:解压,且阅读“README(自述文件)",了解对应库的使用规则
    • [3]:打开源码中的install.txt的文本,学习库的移植和安装步骤
    • [4]:把下载好的源码包jpegsrc.v9f.tar.gz发送到linux系统的家目录下进行解压
    • [5] :“配置” (./configure)
    • [6]: “编译” (make)
    • [7]:“安装” (make install)
    • [8]:安装完成后,拷贝用户指定的安装路径中生成的libjpeg库的头文件和库文件,方便后续设计程序使用
    • [9]:把include文件夹和lib文件夹与自己的工程文件放在同一个路径,方便后期的工程维护!
  • 库的使用
    • 解码步骤
      • 习题:设计程序实现在LCD上的任意位置显示一张任意大小的jpg图片,注意不要越界。
      • [1]:创建解码对象,并且对解码对象进行初始化,另外需要创建错误处理对象,并和解码对象进行关联
      • [2]:打开待解码的jpg图片,使用fopen的时候需要添加选项”b”,以二进制方式打开文件!
      • [3]:读取待解码图片的文件头,并把图像信息和解码对象进行关联,通过解码对象对jpg图片进行解码
      • [4]:可以选择设置解码参数,如果打算以默认参数对jpg图片进行解码,则可以省略该步骤!
      • [5]:开始对jpg图片进行解码,调用函数之后开始解码,可以得到图像宽、图像高等信息!
      • [6]:开始设计一个循环,在循环中每次读取1行的图像数据,并写入到LCD中
      • [7]:在所有的图像数据都已经解码完成后,则调用函数完成解码即可,然后释放相关资源!(不要遗漏打开的图像文件-----fclose(infile) )
      • [8]: 将所有头文件和库文件与main.c 一同完成编译
      • 代码完整展示

开源库移植步骤

注:以下操作均以 JPEG图片操作 为例

[1]:下载库的源码包

  • 从对应库的官网下载库的源码包,可直接搜索”lib关键字“,如”libJPEG

  • 下载时,最好选择全英文网站进行下载,优先选择代码沟通网站【github、sourceforge、git】,一般可以选择以”.org“结尾的网址(xxx.sourceforge.org)

  • 根据不同的系统平台,选择不同的压缩形式的源码包

    例:Windows平台---> xxx.zip Linux平台---> xxx.tar.gz

image

网址为:JPEG库源码包下载

[2]:解压,且阅读“README(自述文件)",了解对应库的使用规则

  • 此时是为了阅读“README”,所以可以直接在 Windows下直接解压源码包

  • 阅读顺序一般为:

    1. install.txt: 学习库的移植和安装步骤
    2. libjpeg.txt: 学习库的使用方法和步骤
    3. example.c: 在了解库的基本使用方法后,可以依照给出的库使用例子,进行模仿使用该库

image

[3]:打开源码中的install.txt的文本,学习库的移植和安装步骤

  • 经过阅读文本可知:(重要且背诵)

    移植libjpeg的步骤分为三步:配置(./configure) + 编译(make) + 安装(make install)。
    image

[4]:把下载好的源码包jpegsrc.v9f.tar.gz发送到linux系统的家目录下进行解压

image

  • 注意不可以在共享文件夹进行解压,因为共享文件夹依然属于Windows平台环境

[5] :“配置” (./configure)

切换到解压后的jpeg-9f的文件夹内,进行“配置”操作,且依照实际需要修改configure的参数(数据存放路径 + 指定平台)

  • 若是直接使用 ./configure ,系统会按照默认参数进行配置,不一定符合我们实际所需,所以需要使用指令对configure的参数进行修改
  • 默认参数指的是:编辑器使用---gcc 、 数据存放路径---/usr/local 等
  • 可以使用 "./configure --help" 查看所有可改的选项
  • 我们一般需要修改 数据存放路径 和 指定目标平台 两个参数。代码如下:

修改路径: --prefix = [绝对路径]

指定目标平台:--host = arm-linux (不用加gcc,因为此时修改的是平台;且一般修改平台后,编辑器也会随之修改)

image

  • 配置成功后,会得到一个“Makefile”脚本文件,为下一步“编译”做准备

image

[6]: “编译” (make)

配置成功之后,会得到一个makefile脚本文件,此时可以完成移植的第二步:*编译*,在命令行输入指令:make ,该指令会自动执行makefile

image

  • “编译”过程中不可以有错误(出现error),如果出错,则需要重新进行“配置”

  • 如果出现下图情况,是因为没有安装“make”工具所导致的,只需安装一下解决

image

image

[7]:“安装” (make install)

**编译通过之后,则可以完成libjpeg库的*安装*,此时在命令行输入指令: make install **

image

  • “安装”过程中也不可以有错误(出现error),如果出错,则需要重新进行“配置”
  • "安装"生成的文件会被放置到"配置"过程中指定的路径下

即 头文件----> [指定路径]/include

库文件----> [指定路径]/lib

[8]:安装完成后,拷贝用户指定的安装路径中生成的libjpeg库的头文件和库文件,方便后续设计程序使用

image

[9]:把include文件夹和lib文件夹与自己的工程文件放在同一个路径,方便后期的工程维护!

image

库的使用

阅读libjpeg.txt相关信息,了解并学习JPEG图片的解码与编码过程

image

解码步骤

为了可以把一张jpg图片显示在LCD上,所以需要把jpg图片进行解压,解压之后就可以得到图片内部的像素点的颜色分量,就可以把像素点的颜色分量向LCD的像素点写入。就需要掌握jpg图片的解压流程(背下来)。借助习题来更好的理解解压流程:

习题:设计程序实现在LCD上的任意位置显示一张任意大小的jpg图片,注意不要越界。

[1]:创建解码对象,并且对解码对象进行初始化,另外需要创建错误处理对象,并和解码对象进行关联

  • 解码对象一般命名为 cinfo,且解码对象是一个结构体变量
  • 调用“jpeg_create_decompress(&cinfo)"完成对解码对象的初始化

image

代码展示:

/*[1]:创建解码对象,并且对解码对象进行初始化,另外需要创建错误处理对象,并和解码对象进行关联*///创建解码对象,其是一个结构体变量 struct jpeg_decompress_struct cinfo;//创建错误处理对象struct jpeg_error_mgr jerr;//将错误处理对象与解码对象相关联cinfo.err = jpeg_std_error(&jerr);

[2]:打开待解码的jpg图片,使用fopen的时候需要添加选项”b”,以二进制方式打开文件!

  • 需要把打开的文件的文件指针和解码对象进行绑定

image

代码展示:

/*[2]:打开待解码的jpg图片,使用fopen的时候需要添加选项”b”,以二进制方式打开文件!*/FILE * infile;			    //接收打开文件的文件指针unsigned char * buffer;		//输出行缓冲区int row_stride;			    //buffer一行的像素点数量,即图片的宽度// 以二进制方式打开图片,并进行错误处理if ((infile = fopen(filename, "rb")) == NULL) {fprintf(stderr, "can't open %s\n", filename); return 0;}//把打开的文件的文件指针和解码对象进行绑定jpeg_stdio_src(&cinfo, infile);

[3]:读取待解码图片的文件头,并把图像信息和解码对象进行关联,通过解码对象对jpg图片进行解码

image

代码展示:

/*[3]:读取待解码图片的文件头,并把图像信息和解码对象进行关联,通过解码对象对jpg图片进行解码*/(void) jpeg_read_header(&cinfo, TRUE);

[4]:可以选择设置解码参数,如果打算以默认参数对jpg图片进行解码,则可以省略该步骤!

  • 使用默认参数的情况,即对jpg图片的操作不涉及缩放图片大小等操作

image

代码展示:

/*[4]:可以选择设置解码参数,如果打算以默认参数对jpg图片进行解码,则可以省略该步骤!*//* 在该习题要求中,并不涉及图片缩放等问题,所以我们可以省略该步骤* jpeg_read_header(), */

[5]:开始对jpg图片进行解码,调用函数之后开始解码,可以得到图像宽、图像高等信息!

  • 图像宽: output_width
  • 图像宽: output_height
  • 图像色深:output_components

​ 注意:此处的色深数据是以“字节”为单位存储

image

代码展示:

/*[5]:开始对jpg图片进行解码,调用函数之后开始解码,可以得到图像宽、图像高等信息!*///我们只需要调用该函数,将图像信息放入解码对象中,无需注意其的返回值(void) jpeg_start_decompress(&cinfo);

[6]:开始设计一个循环,在循环中每次读取1行的图像数据,并写入到LCD中

  • 注意:转换算法需要用户自己依照要求设计

  • 该题目需要任意位置显示,所以在“写入”时需要考虑图片的初始位置

  • 由于该题目的图片大小也是未知的,所以在程序中必须调用解码对象中的图片信息,而不能将条件”写死“

image

  • (void) jpeg_read_scanlines(&cinfo, &buffer, 1) 函数返回的是实际读取的行的数量

  • cinfo.output_scanline 该变量记录的是扫描行的数量,初始值为0,调用一次"jpeg_read_scanlines()"函数,数值加1

  • JPEG存储的方式与BMP不同,JPEG的存储方式是大端存储。即 像素点的颜色顺序位 RGB; 存储的图像行数据 是从上到下

  • 申请缓冲区大小时,不应该申请超过图像一行数据大小的空间

image

image

代码展示:

*[6]:开始设计一个循环,在循环中每次读取1行的图像数据,并写入到LCD中*///计算图像一行的大小row_stride = cinfo.output_width * cinfo.output_components;  //为自定义缓冲区申请堆内存,注意申请的内存空间大小应为图像一行的大小buffer = calloc(1,row_stride);//定义一个int类型变量,用于存放颜色分量数据int data = 0;/*定义一个循环,用于循环写入一行的图像数据;使用解码对象当前扫描行数与图像的高比较结果作为循环条件,当两者相等,即图像数据写入完后退出循环*/while (cinfo.output_scanline < cinfo.output_height)  {/*调用jpeg_read_scanlines函数,读取解码对象中的图像一行数据,并存放进自定义缓冲区中且cinfo.output_scanline会随着调用该函数而增加1,保证while循环能够正常退出*/(void) jpeg_read_scanlines(&cinfo, &buffer, 1); //从上到下,从左到右  RGB RGB RGB RGB //将缓冲区中存储的数据逐一写入LCD的内存映射空间中for (int i = 0; i < cinfo.output_width; ++i)  //012 345{/*由于图片没有透明度,所以一个像素点大小为3byte,而data为int类型变量,所以需要借助"|=" 使得颜色分量顺序存储正确;又因为JEPG存储颜色分量顺序为RGB,所以进行下面算法*/data |= buffer[3*i]<<16;	//Rdata |= buffer[3*i+1]<<8;	//Gdata |= buffer[3*i+2];  	//B /*把像素点写入到LCD的指定位置。其中800*start_y + start_x控制的是用户自定义的图片显示初始位置;800*(cinfo.output_scanline-1)控制的是写入图像数据的行数切换;+ i控制的是写入图像数据的列数切换*/lcd_mp[800*start_y + start_x + 800*(cinfo.output_scanline-1) + i] = data;//最后需将data内部清零,避免对下一次循环的颜色分量写入造成影响data = 0;}}

[7]:在所有的图像数据都已经解码完成后,则调用函数完成解码即可,然后释放相关资源!(不要遗漏打开的图像文件-----fclose(infile) )

image

image

代码展示:

/*[7]:在所有的图像数据都已经解码完成后,则调用函数完成解码即可,然后释放相关资源!(不要遗漏打开的图像文件)*///解码完成(void) jpeg_finish_decompress(&cinfo);//释放解码对象jpeg_destroy_decompress(&cinfo);//关闭打开的图像文件fclose(infile);return 1;

[8]: 将所有头文件和库文件与main.c 一同完成编译

image

错误情况分析:

  1. image

  2. image

代码完整展示

/*******************************************************************
*
*	file name:	main.c
*	author	 :  790557054@qq.com
*	date	 :  2024/05/13
*	function :  该案例是掌握JPEG的解码过程
* 	note	 :  None
*
*	CopyRight (c)  2023-2024   790557054@qq.com   All Right Reseverd 
*
* *****************************************************************/#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>#include "jpeglib.h"/********************************************************************
*
*	name	 :	read_JPEG_file
*	function :  实现
*	argument :  完成libjpeg库的移植,实现在LCD上的任意位置显示一张任意大小的jpg图片,并且对可能越界的情况做错误处理。
*				@filename  :需要解码的jpg图片@start_x   :图片显示初始位置的横坐标@start_y   :图片显示初始位置的纵坐标@lcd_mp    :LCD屏内存映射空间的地址
*				
*	retval	 :  调用成功返回1,调用失败返回0
*	author	 :  790557054@qq.com
*	date	 :  2024/05/13
* 	note	 :  学习JPEG的解码过程,以及JPEG存储颜色分量的方式
* 	
* *****************************************************************/
int read_JPEG_file (char * filename,int start_x,int start_y,int * lcd_mp)
{
/*[1]:创建解码对象,并且对解码对象进行初始化,另外需要创建错误处理对象,并和解码对象进行关联*///创建解码对象,其是一个结构体变量 struct jpeg_decompress_struct cinfo;//创建错误处理对象struct jpeg_error_mgr jerr;//将错误处理对象与解码对象相关联cinfo.err = jpeg_std_error(&jerr);//对解码对象进行初始化jpeg_create_decompress(&cinfo);/*[2]:打开待解码的jpg图片,使用fopen的时候需要添加选项”b”,以二进制方式打开文件!*/FILE * infile;			    //接收打开文件的文件指针unsigned char * buffer;		//输出行缓冲区int row_stride;			    //buffer一行的像素点数量,即图片的宽度// 以二进制方式打开图片,并进行错误处理if ((infile = fopen(filename, "rb")) == NULL) {fprintf(stderr, "can't open %s\n", filename); return 0;}//把打开的文件的文件指针和解码对象进行绑定jpeg_stdio_src(&cinfo, infile);/*[3]:读取待解码图片的文件头,并把图像信息和解码对象进行关联,通过解码对象对jpg图片进行解码*/(void) jpeg_read_header(&cinfo, TRUE);/*[4]:可以选择设置解码参数,如果打算以默认参数对jpg图片进行解码,则可以省略该步骤!*//* 在该习题要求中,并不涉及图片缩放等问题,所以我们可以省略该步骤* jpeg_read_header(), *//*[5]:开始对jpg图片进行解码,调用函数之后开始解码,可以得到图像宽、图像高等信息!*///我们只需要调用该函数,将图像信息放入解码对象中,无需注意其的返回值(void) jpeg_start_decompress(&cinfo);/*[6]:开始设计一个循环,在循环中每次读取1行的图像数据,并写入到LCD中*///计算图像一行的大小row_stride = cinfo.output_width * cinfo.output_components;  //为自定义缓冲区申请堆内存,注意申请的内存空间大小应为图像一行的大小buffer = calloc(1,row_stride);//定义一个int类型变量,用于存放颜色分量数据int data = 0;/*定义一个循环,用于循环写入一行的图像数据;使用解码对象当前扫描行数与图像的高比较结果作为循环条件,当两者相等,即图像数据写入完后退出循环*/while (cinfo.output_scanline < cinfo.output_height)  {/*调用jpeg_read_scanlines函数,读取解码对象中的图像一行数据,并存放进自定义缓冲区中且cinfo.output_scanline会随着调用该函数而增加1,保证while循环能够正常退出*/(void) jpeg_read_scanlines(&cinfo, &buffer, 1); //从上到下,从左到右  RGB RGB RGB RGB //将缓冲区中存储的数据逐一写入LCD的内存映射空间中for (int i = 0; i < cinfo.output_width; ++i)  //012 345{/*由于图片没有透明度,所以一个像素点大小为3byte,而data为int类型变量,所以需要借助"|=" 使得颜色分量顺序存储正确;又因为JEPG存储颜色分量顺序为RGB,所以进行下面算法*/data |= buffer[3*i]<<16;	//Rdata |= buffer[3*i+1]<<8;	//Gdata |= buffer[3*i+2];  	//B /*把像素点写入到LCD的指定位置。其中800*start_y + start_x控制的是用户自定义的图片显示初始位置;800*(cinfo.output_scanline-1)控制的是写入图像数据的行数切换;+ i控制的是写入图像数据的列数切换*/lcd_mp[800*start_y + start_x + 800*(cinfo.output_scanline-1) + i] = data;//最后需将data内部清零,避免对下一次循环的颜色分量写入造成影响data = 0;}}/*[7]:在所有的图像数据都已经解码完成后,则调用函数完成解码即可,然后释放相关资源!(不要遗漏打开的图像文件)*///解码完成(void) jpeg_finish_decompress(&cinfo);//释放解码对象jpeg_destroy_decompress(&cinfo);//关闭打开的图像文件fclose(infile);return 1;
}int main(int argc, char const *argv[])
{//1.打开LCD   open  int lcd_fd = open("/dev/fb0",O_RDWR);//2.对LCD进行内存映射  mmapint *lcd_mp = (int *)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);//3.读取图片初始显示位置int start_x = 0, start_y = 0;printf("Please input start_x :\n");scanf("%d", &start_x);printf("Please input start_y :\n");scanf("%d", &start_y);//3.显示一张jpgread_JPEG_file (argv[1],start_x,start_y,lcd_mp);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/31260.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

C#.Net筑基-模式匹配汇总

从C#7开始支持的 模式匹配 语法(糖,挺甜),可非常灵活的对数据进行条件匹配和提取,经过多个版本的完善,已经非常强大了。01、模式匹配概述 从C#7开始支持的 模式匹配 语法(糖,挺甜),可非常灵活的对数据进行条件匹配和提取,经过多个版本的完善,已经非常强大了。 C# 支…

全面系统的AI学习路径,帮助普通人也能玩转AI

前言 现如今AI技术和应用的发展可谓是如火如荼,它们在各个领域都展现出了巨大的潜力和影响力。AI的出现对于我们这些普通人而言也是影响匪浅,比如说使用AI工具GPT来写文档查问题、使用AI辅助编程工具帮助我们写代码、并且可是使用AI来实现人工客服等。那么普通人如何学习AI呢…

python3脚本调整歌词文件时间戳

场景:截取部分音乐文件,对应歌词的时间要做相应调整。 例如:下方歌词文件,只需要 [00:28] 及以后的内容,而且将第一句 ( [00:28]月光 把天空照亮 )的时间改成 [00:01],那么第二句的时间应该是 [00:06],第三句的时间应该是 [00:12],以此类推 歌词文件中,时间的格式…

GPU算子计算与调度技术

GPU算子计算与调度技术 9.4.1 GPU内核驱动分析概述 不同CPU相比,GPU中包含了大量的并行计算单元,适合处理像素,矩阵,坐标等大量同类型的数据,因此,很多LINUX上的应用程序为了能够利用GPU的加速功能,都试图和GPU直接打交道,因此,系统中可能有多个组件或者程序同时使用G…

读人工智能时代与人类未来笔记02_技术变革

读人工智能时代与人类未来笔记02_技术变革1. 目标 1.1. AlphaZero的目标是在遵守规则的前提下赢得国际象棋比赛 1.2. 发现Halicin的人工智能的目标是灭杀尽可能多的致病菌:它在不伤害宿主的情况下灭杀的致病菌越多,就越成功 1.2.1. 人工智能…

Golang初学:高并发时写入数据到不同的map

go version go1.22.1 windows/amd64 Windows 11 + amd64 x86_64 x86_64 GNU/Linux ---序章 多个 给 map 写入数据。 1、基本的map:make(map[any]any) 失败。 2、sync.Map 成功。测试场景: 1K 个 goroutines 给同一个 map 各自写入 1W 数据,总1KW数据。测试#1:普通map 代码:…

Cocos Creator 3.8.x 透明带滚动功能的容器

ScrollView 是一种带滚动功能的容器 1、删除ScrollView下Sprite组件的SpriteFrame 2、ScrollView下scrollBar的Sprite组件的Color设为:FFFFFF00 3、ScrollView下view的Graphics组件的FillColor设为:FFFFFF00

TACO编译器张量与科学计算SpMV

TACO编译器张量与科学计算SpMV 定义张量 声明张量 taco::Tensor对象,对应于数学张量,构成了taco C++API的核心。可以通过指定新张量的名称、包含张量每个维度大小的向量,以及将用于存储张量的存储格式来声明新张量: // Declare a new tensor "A" of double-preci…