【正点原子I.MX6U-MINI应用篇】5、嵌入式Linux在LCD上显示BMP、JPG、PNG图片

news2025/9/16 23:09:24

BMPJPEGPNG

一、BMP图像介绍与显示

我们常用的图片格式有很多,一般最常用的有三种:JPEG(或 JPG)、PNG、BMP和GIF。其中 JPEG(或JPG)、PNG以及 BMP 都是静态图片,而 GIF 则可以实现动态图片。

BMP(全称 Bitmap)是Window操作系统中的标准图像文件格式,文件后缀名为.bmp,使用非常
广。它采用位映射存储格式,除了图像深度可选以外,图像数据没有进行任何压缩,因此,BMP 图像文件所占用的空间很大,但是没有失真、并且解析BMP图像简单

BMP文件的图像深度可选1bit、4bit、8bit、16bit、24bit 以及32bit,典型的BMP图像文件由四部分组成:

  • ①、BMP文件头(BMP file header),它包含BMP文件的格式、大小、位图数据的偏移量等信息;
  • ②、位图信息头(bitmap information),它包含位图信息头大小、图像的尺寸、图像大小、位平面数、压缩方式以及颜色索引等信息;
  • ③、调色板(color palette),这部分是可选的,如果使用索引来表示图像,调色板就是索引与其对应颜色的映射表;
  • ④、位图数据(bitmap data),也就是图像数据。

BMP图像各数据段说明

一般常见的图像都是以 16 位(R、G、B 三种颜色分别使用 5bit、6bit、5bit 来表示)、24 位(R、G、B 三种颜色都使用 8bit 来表示)色图像为主,我们称这样的图像为真彩色图像,真彩色图像是不需要调色板的,即位图信息头后面紧跟的就是位图数据了

对某些BMP位图文件说并非如此,譬如16色位图、256色位图,它们需要使用到调色板,具体调色板如何使用,我们不关心,本节我们将会以16 位色(RGB565)BMP 图像为例。

图片链接

可以看到该图片的分辨率为800*480,位深度为16bit,每个像素点使用16位表示,也就是RGB565。为了向大家介绍BMP文件结构,接下来使用十六进制查看工具将image.bmp文件打开,文件头部分的内容如下所示:

1.1 bmp文件头

Windows下为bmp文件头定义了如下结构体:

typedef struct tagBITMAPFILEHEADER
{
	UINT16 bfType;
	DWORD bfSize;
	UINT16 bfReserved1;
	UINT16 bfReserved2;
	DWORD bfOffBits;
} BITMAPFILEHEADER;

结构体中每一个成员说明如下:

bmp 文件头成员说明
从上面的描述信息,再来对照文件数据:

  • 00~01H :0x42、0x4D 对应的 ASCII 字符分别为为B、M,表示这是 Windows 所支持的位图格式,该字段必须是BM才是Windows位图文件。
  • 02~05H :对应于文件大小,0x000BB848=768072字节=750KB,与image.bmp文件大小是相符的。
  • 06~09H :保留字段。
  • 0A~0D :0x00000046=70,即从文件头部开始到位图数据需要偏移70个字节。

bmp文件头的大小固定为 14 个字节。

1.2 位图信息头

同样,Windows 下为位图信息头定义了如下结构体:

typedef struct tagBITMAPINFOHEADER 
{
	DWORD biSize;
	LONG biWidth;
	LONG biHeight;
	WORD biPlanes;
	WORD biBitCount;
	DWORD biCompression;
	DWORD biSizeImage;
	LONG biXPelsPerMeter;
	LONG biYPelsPerMeter;
	DWORD biClrUsed;
	DWORD biClrImportant;
} BITMAPINFOHEADER;

结构体中每一个成员说明如下:

 位图信息头成员说明从上面的描述信息,再来对照文件数据:

  • 0E~11H :0x00000038=56,这说明这个位图信息头的大小为56个字节。
  • 12~15H :0x00000320=800,图像宽度为 800 个像素,与文件属性一致。
  • 16~19H :0x000001E0=480,图像高度为 480 个像素,与文件属性一致;
  • 1A~1BH :0x0001=1,这个值总为 1。
  • 1C~1DH :0x0010=16,表示每个像素占16个bit。
  • 1E~21H :0x00000003=0,bit-fileds 方式。
  • 22~25H :0x000BB802=768002,图像的大小,注意图像的大小并不是BMP文件的大小,而是图像数据的大小。
  • 26~29H :0x00001274=4724,水平分辨率为4724像素/米。
  • 2A~2DH :0x00001274=4724,垂直分辨率为4724像素/米。
  • 2E~31H :0x00000000=0,本位图未使用调色板。
  • 32~35H :0x00000000=0。

只有压缩方式选项被设置为bit-fileds(0x3)时,位图信息头的大小才会等于56字节,否则,为40字节。

1.3 调色板

调色板是单色、16 色、256 色位图图像文件所持有的,如果是 16 位、24 位以及 32 位位图文件,则 BMP文件组成部分中不包含调色板,关于调色板这里不过多介绍,有兴趣可以自己去了解。

1.4 位图数据

位图数据其实就是图像的数据,对于24位位图,使用3个字节数据来表示一个像素点的颜色,对于16位位图,使用2个字节数据来表示一个像素点的颜色,同理,32 位位图则使用4个字节来描述。
BMP位图分为正向的位图和倒向的位图,主要区别在于图像数据存储的排列方式,前面已经给大家解释的比较清楚了,如下如所示(左边对应的是正向位图,右边对应的则是倒向位图):

 所以正向位图先存储图像的第一行数据,从左到右依次存放,接着存放第二行,依次这样;而倒向位图,则先存储图像的最后一行(倒数第一行)数据,也是从左到右依次存放,接着倒数二行,依次这样。

1.5 源码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>

/**** BMP文件头数据结构 ****/
typedef struct {
    unsigned char type[2];      //文件类型
    unsigned int size;          //文件大小
    unsigned short reserved1;   //保留字段1
    unsigned short reserved2;   //保留字段2
    unsigned int offset;        //到位图数据的偏移量
} __attribute__ ((packed)) bmp_file_header;

/**** 位图信息头数据结构 ****/
typedef struct {
    unsigned int size;          //位图信息头大小
    int width;                  //图像宽度
    int height;                 //图像高度
    unsigned short planes;      //位面数
    unsigned short bpp;         //像素深度 
    unsigned int compression;   //压缩方式
    unsigned int image_size;    //图像大小
    int x_pels_per_meter;       //像素/米
    int y_pels_per_meter;       //像素/米 
    unsigned int clr_used;
    unsigned int clr_omportant;
} __attribute__ ((packed)) bmp_info_header;

/**** 静态全局变量 ****/
static int width;                       //LCD X分辨率
static int height;                      //LCD Y分辨率
static unsigned short *screen_base = NULL; //映射后的显存基地址
static unsigned long line_length;       //LCD一行的长度(字节为单位)

/********************************************************************
 * 函数名称: show_bmp_image
 * 功能描述: 在LCD上显示指定的BMP图片
 * 输入参数: 文件路径
 * 返 回 值: 成功返回0, 失败返回-1
 ********************************************************************/
static int show_bmp_image(const char *path)
{
    bmp_file_header file_h;
    bmp_info_header info_h;
    unsigned short *line_buf = NULL;    //行缓冲区
    unsigned long line_bytes;   //BMP图像一行的字节的大小
    unsigned int min_h, min_bytes;
    int fd = -1;
    int j;

    /* 打开文件 */
    if (0 > (fd = open(path, O_RDONLY))) {
        perror("open error");
        return -1;
    }

    /* 读取BMP文件头 */
    if (sizeof(bmp_file_header) !=
        read(fd, &file_h, sizeof(bmp_file_header))) {
        perror("read error");
        close(fd);
        return -1;
    }

    if (0 != memcmp(file_h.type, "BM", 2)) {
        fprintf(stderr, "it's not a BMP file\n");
        close(fd);
        return -1;
    }

    /* 读取位图信息头 */
    if (sizeof(bmp_info_header) !=
        read(fd, &info_h, sizeof(bmp_info_header))) {
        perror("read error");
        close(fd);
        return -1;
    }

    /* 打印信息 */
    printf("文件大小: %d\n"
         "位图数据的偏移量: %d\n"
         "位图信息头大小: %d\n"
         "图像分辨率: %d*%d\n"
         "像素深度: %d\n", file_h.size, file_h.offset,
         info_h.size, info_h.width, info_h.height,
         info_h.bpp);

    /* 将文件读写位置移动到图像数据开始处 */
    if (-1 == lseek(fd, file_h.offset, SEEK_SET)) {
        perror("lseek error");
        close(fd);
        return -1;
    }

    /* 申请一个buf、暂存bmp图像的一行数据 */
    line_bytes = info_h.width * info_h.bpp / 8;
    line_buf = malloc(line_bytes);
    if (NULL == line_buf) {
        fprintf(stderr, "malloc error\n");
        close(fd);
        return -1;
    }

    if (line_length > line_bytes)
        min_bytes = line_bytes;
    else
        min_bytes = line_length;

    /**** 读取图像数据显示到LCD ****/
    /*******************************************
     * 为了软件处理上方便,这个示例代码便不去做兼容性设计了
     * 如果你想做兼容, 可能需要判断传入的BMP图像是565还是888
     * 如何判断呢?文档里边说的很清楚了
     * 我们默认传入的bmp图像是RGB565格式
     *******************************************/
    if (0 < info_h.height) {//倒向位图
        if (info_h.height > height) {
            min_h = height;
            lseek(fd, (info_h.height - height) * line_bytes, SEEK_CUR);
            screen_base += width * (height - 1);    //定位到屏幕左下角位置
        }
        else {
            min_h = info_h.height;
            screen_base += width * (info_h.height - 1); //定位到....不知怎么描述 懂的人自然懂!
        }

        for (j = min_h; j > 0; screen_base -= width, j--) {
            read(fd, line_buf, line_bytes); //读取出图像数据
            memcpy(screen_base, line_buf, min_bytes);//刷入LCD显存
        }
    }
    else {  //正向位图
        int temp = 0 - info_h.height;   //负数转成正数
        if (temp > height)
            min_h = height;
        else
            min_h = temp;

        for (j = 0; j < min_h; j++, screen_base += width) {
            read(fd, line_buf, line_bytes);
            memcpy(screen_base, line_buf, min_bytes);
        }
    }

    /* 关闭文件、函数返回 */
    close(fd);
    free(line_buf);
    return 0;
}

int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;

    /* 传参校验 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <bmp_file>\n", argv[0]);
        exit(-1);
    }

    /* 打开framebuffer设备 */
    if (0 > (fd = open("/dev/fb0", O_RDWR))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    /* 获取参数信息 */
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

    screen_size = fb_fix.line_length * fb_var.yres;
    line_length = fb_fix.line_length;
    width = fb_var.xres;
    height = fb_var.yres;

    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }

    /* 显示BMP图片 */
    memset(screen_base, 0xFF, screen_size);
    show_bmp_image(argv[1]);

    /* 退出 */
    munmap(screen_base, screen_size);  //取消映射
    close(fd);  //关闭文件
    exit(EXIT_SUCCESS);    //退出进程
}

1.6 编译程序

我们要想给ARM板编译出程序,需要使用交叉编译工具链,交叉编译的工具链我们已经安装过了,详细请看【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.x.pdf 的第4.3小节。我是用的是arm-linux-gnueabihf交叉编译工具链。使用arm-linux-gnueabihf-gcc -v可以查看交叉编译工具链的版本号。

然后就可以使用下面命令编译出可以在ARM板子上运行的可执行文件了。

arm-linux-gnueabihf-gcc -o bmp_show bmp_show.c
  • 1、arm表示这是编译arm架构代码的编译器。
  • 2、linux表示运行在linux环境下。
  • 3、gnueabihf表示嵌入式二进制接口。
  • 4、gcc表示是gcc工具。

这样编译出来的 led程序才可以在ARM板子上运行。执行file bmp_show命令就可以看出hello是32位LSB的ELF格式文件,目标机架构为ARM,说明这个交叉编译正常,可执行文件可以在ARM板上执行。

1.7 上传程序到开发板执行

开发板启动后通过nfs挂载Ubuntu目录的方式,将相应的文件拷贝到开发板上。简单来说,就是通过NFS在开发板上通过网络直接访问ubuntu虚拟机上的文件,并且就相当于自己本地的文件一样。

开发板想访问/home/zhiguoxin/myproject/alientek_app_development_source这个目录中的文件,就要把/home/zhiguoxin/myproject/alientek_app_development_source挂载到开发板的mnt目录,这样就可以通过nfs来访问/home/zhiguoxin/myproject/alientek_app_development_source了。

因为我的代码都放在/home/zhiguoxin/myproject/alientek_app_development_source这个目录下,所以我们将这个目录作为NFS共享文件夹。设置方法参考移植SQLite3、OpenCV到RV1126开发板上开发人脸识别项目第一章。

Ubuntu IP为192.168.10.100,然后一般都是挂载在开发板的mnt目录下,这个目录是专门用来给我们作为临时挂载的目录。

文件系统目录简介

然后使用MobaXterm软件通过SSH访问开发板。

ubuntu ip:192.168.10.100
windows ip:192.168.10.200
开发板ip:192.168.10.50

在开发板上执行以下命令:

mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_app_development_source /mnt

就将开饭的mnt目录挂载在ubuntu的/home/zhiguoxin/myproject/alientek_app_development_source目录下了。这样我们就可以在Ubuntu下修改文件,然后可以直接在开发板上执行可执行文件了。当然我这里的/home/zhiguoxin/myproject/windows之间是一个共享目录,我也可以直接在windows上面修改文件,然后ubuntu和开发板直接进行文件同步了。

执行应用程序

./bmp_show xiaoya.bmp

 在开发板上的显示效果

1.8 如何得到16位色RGB565格式 BMP图像?

在Windows下我们转换得到的BMP位图通常是24位色的RGB888格式图像,那如何得到RGB565格式 BMP位图呢?当然这个方法很多,这里笔者向大家介绍一种方法就是通过 Photoshop软件来得到 RGB565格式的 BMP 位图。

首先,找一张图片,图片格式无所谓,只要Photoshop软件能打开即可;确定图片之后,我们启动Photoshop软件,并且使用Photoshop软件打开这张图片,打开之后点击菜单栏中的文件—>存储为,接着出现如下界面:


二、jpeg图像介绍与显示

我们常用的图片格式有很多,一般最常用的有三种:JPEG(或 JPG)、PNG、BMP。BMP 图像虽然没有失真、并且解析简单,但是由于图像数据没有进行任何压缩,因此,BMP 图像文件所占用的存储空间很大,不适合存储在磁盘设备中。

而 JPEG(或 JPG)、PNG 则是经过压缩处理的图像格式,将图像数据进行压缩编码,大大降低了图像文件的大小,适合存储在磁盘设备中,所以很常用。

2.1 JPEG 简介

JPEG(Joint Photographic Experts Group)是由国际标准组织为静态图像所建立的第一个国际数字图像压缩标准,也是至今一直在使用的、应用最广的图像压缩标准

JPEG由于可以提供有损压缩,因此压缩比可以达到其他传统压缩算法无法比拟的程度;JPEG虽然是有损压缩,但这个损失的部分是人的视觉不容易察觉到的部分,它充分利用了人眼对计算机色彩中的高频信息部分不敏感的特点,来大大节省了需要处理的数据信息。JPEG 压缩文件通常以.jpg 或.jpeg 作为文件后缀名。

2.2 libjpeg简介

JPEG 压缩标准使用了一套压缩算法对原始图像数据进行了压缩得到.jpg 或.jpeg 图像文件,如果想要在LCD 上显示.jpg 或.jpeg 图像文件,则需要对其进行解压缩、以得到图像的原始数据,譬如RGB 数据。

既然压缩过程使用了算法,那对.jpg 或.jpeg 图像文件进行解压同样也需要算法来处理,这里并不会教大家如何编写解压算法,这些算法的实现也是很复杂的,我不会,自然教不了大家!但是,我们可以使用别人写好的库、调用别人写好的库函数来解压.jpg 或.jpeg 图像文件—libjpeg 库。

libjpeg是一个完全用C语言编写的函数库,包含了JPEG解码(解压缩)、JPEG 编码(创建压缩)和
其他的 JPEG 功能的实现。可以使用 libjpeg 库对.jpg 或.jpeg 压缩文件进行解压或者生成.jpg 或.jpeg 压缩文件。

2.3 libjpeg移植

libjpeg是一个开源 C 语言库,我们获取到它的源代码。

2.3.1 下载源码包

首先,打开http://www.ijg.org/files/链接地址,如下所示

libjpeg下载链接页面
目前最新的一个版本是v9e,对应的年份为2022年,这里我们选择一个适中的版本,笔者以v9b 为例,对应的文件名为jpegsrc.v9b.tar.gz,点击该文件即可下载。我把源码下在完成后放在共享文件夹下:

其实开发板出厂系统中已经移植了libjpeg库,但是版本太旧了!所以这里我们选择重新移植

2.3.2 编译源码

将 jpegsrc.v9b.tar.gz 压缩包文件放到共享文件夹下,如下所示:
 执行命令解压

tar -vxzf jpegsrc.v9b.tar.gz

解压成功之后会生成 jpeg-9b 文件夹,也就是 libjpeg 源码文件夹。
 编译之前,在家目录下的tools文件夹中创建一个名为jpeg的文件夹,该目录作为libjpeg库的安装目录:

mkdir jpeg

回到共享文件夹目录下,进入到libjpeg源码目录jpeg-9b中,该目录下包含的内容如下所示:

接下来对 libjpeg 源码进行交叉编译,跟编译 tslib 时步骤一样,包含三个步骤:

  • 配置工程;
  • 编译工程;
  • 安装;

一套流程下来非常地快!没有任何难点。在此之前,先对交叉编译工具的环境进行初始化,使用 source执行交叉编译工具安装目录下的environment-setup-cortexa7hf-neon-poky-linux-gnueabi脚本文件(如果已经
初始化过了,那就不用再进行初始化了):

source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

执行下面这条命令对 libjpeg 工程进行配置:

./configure --host=arm-poky-linux-gnueabi --prefix=/home/zhiguoxin/linux/tool/jpeg

大家可以执行./configure --help查看它的配置选项以及含义,--host选项用于指定交叉编译得到的库文件是运行在哪个平台,通常将--host设置为交叉编译器名称的前缀,譬如arm-poky-linux-gnueabi-gcc前缀就是arm-poky-linux-gnueabi--prefix选项则用于指定库文件的安装路径,将/home/zhiguoxin/linux/tool目录作为libjpeg的安装目录。

配置工程
接着执行 make 命令编译工程:

make

编译工程
编译完成之后,执行命令安装 libjpeg:

make install

安装工程

命令执行完成之后,我们的 libjpeg 也就安装成功了!

2.3.3 安装目录下的文件夹介绍

进入到 libjpeg 安装目录:

2.3.4 移植到开发板

开发板出厂系统已经移植了libjpeg库,前面给大家提到过,只是移植的版本太低了,所以这里不打算使用出厂系统移植的libjpeg库,而使用交叉编译好的libjpeg库。

进入到libjpeg安装目录下,将bin目录下的所有测试工具拷贝到开发板Linux系统/usr/bin目录;将 lib目录下的所有库文件拷贝到开发板 Linux 系统/usr/lib目录。

拷贝lib目录下的库文件时,需要注意符号链接的问题,不能破坏原有的符号链接;可以将lib目录下的所有文件打包成压缩包的形式,譬如进入到lib目录,执行命令:

tar -czf lib.tar.gz ./*

将 lib 目录下的所有文件打包再将lib.tar.gz压缩文件拷贝到开发板Linux的用户家目录下,在解压之前,将开发板出厂系统中已经移植的libjpeg库删除,执行命令:

rm -rf /usr/lib/libjpeg.*

删除出厂系统原有的 libjpeg 库

Tips:注意!当出厂系统原有的libjpeg库被删除后,将会导致开发板下次启动后,出厂系统的 Qt GUI应用程序会出现一些问题,原本显示图片的位置变成了空白,显示不出来了!原因在于 Qt 程序处理图片(对jpeg 图片解码)时,它的底层使用到了 ibjpeg 库,而现在我们将出厂系统原有的 libjpeg 给删除了,自然就会导致Qt GUI应用程序中图片显示不出来(无法对 jpeg 图片进行解码)!

接着我们将lib.tar.gz压缩文件解压到开发板Linux系统/usr/lib目录下。我们可以使用scp命令将/home/zhiguoxin/linux/jpeg/lib目录下的lib.tar.gz发送到发开板中。

scp lib.tar.gz root@192.168.10.50:/home/root


注意:有的时候发下面这种错误,拷贝失败,可以关闭命令窗口,在重新打开一次就好了。


然后lib.tar.gz就拷贝到开发板了,然后使用下面命令将其解压至/usr/lib 目录下

tar -xzf lib.tar.gz -C /usr/lib
tar -xmzf lib.tar.gz -C /usr/lib

解压成功之后,接着执行libjpeg提供的测试工具,看看我们移植成功没:

djpeg --help

2.4 libjpeg使用说明

libjpeg 提供的库函数对.jpg/.jpeg 进行解码(解压),得到 RGB 数据。首先,使用 libjpeg 库需要在我们的应用程序中包含它的头文件 jpeglib.h,该头文件包含了一些结构体数据结构以及 API 接口的申明。先来看看解码操作的过程:

⑴、创建 jpeg 解码对象;
⑵、指定解码数据源;
⑶、读取图像信息;
⑷、设置解码参数;
⑸、开始解码;
⑹、读取解码后的数据;
⑺、解码完毕;
⑻、释放/销毁解码对象。

2.5 libjpeg应用编程

对一个指定的 jpeg 图像进行解码,显示在 LCD 屏上,示例代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <jpeglib.h>

typedef struct bgr888_color {
    unsigned char red;
    unsigned char green;
    unsigned char blue;
} __attribute__ ((packed)) bgr888_t;

static int width;                       //LCD X分辨率
static int height;                      //LCD Y分辨率
static unsigned short *screen_base = NULL;        //映射后的显存基地址
static unsigned long line_length;       //LCD一行的长度(字节为单位)
static unsigned int bpp;    //像素深度bpp

static int show_jpeg_image(const char *path)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *jpeg_file = NULL;
    bgr888_t *jpeg_line_buf = NULL;     //行缓冲区:用于存储从jpeg文件中解压出来的一行图像数据
    unsigned short *fb_line_buf = NULL; //行缓冲区:用于存储写入到LCD显存的一行数据
    unsigned int min_h, min_w;
    unsigned int valid_bytes;
    int i;

    //绑定默认错误处理函数
    cinfo.err = jpeg_std_error(&jerr);

    //打开.jpeg/.jpg图像文件
    jpeg_file = fopen(path, "r");   //只读方式打开
    if (NULL == jpeg_file) {
        perror("fopen error");
        return -1;
    }

    //创建JPEG解码对象
    jpeg_create_decompress(&cinfo);

    //指定图像文件
    jpeg_stdio_src(&cinfo, jpeg_file);

    //读取图像信息
    jpeg_read_header(&cinfo, TRUE);
    printf("jpeg图像大小: %d*%d\n", cinfo.image_width, cinfo.image_height);

    //设置解码参数
    cinfo.out_color_space = JCS_RGB;//默认就是JCS_RGB
    //cinfo.scale_num = 1;
    //cinfo.scale_denom = 2;

    //开始解码图像
    jpeg_start_decompress(&cinfo);

    //为缓冲区分配内存空间
    jpeg_line_buf = malloc(cinfo.output_components * cinfo.output_width);
    fb_line_buf = malloc(line_length);

    //判断图像和LCD屏那个的分辨率更低
    if (cinfo.output_width > width)
        min_w = width;
    else
        min_w = cinfo.output_width;

    if (cinfo.output_height > height)
        min_h = height;
    else
        min_h = cinfo.output_height;

    //读取数据
    valid_bytes = min_w * bpp / 8;//一行的有效字节数 表示真正写入到LCD显存的一行数据的大小
    while (cinfo.output_scanline < min_h) {

        jpeg_read_scanlines(&cinfo, (unsigned char **)&jpeg_line_buf, 1);//每次读取一行数据

        //将读取到的BGR888数据转为RGB565
        for (i = 0; i < min_w; i++)
            fb_line_buf[i] = ((jpeg_line_buf[i].red & 0xF8) << 8) |
                    ((jpeg_line_buf[i].green & 0xFC) << 3) |
                    ((jpeg_line_buf[i].blue & 0xF8) >> 3);

        memcpy(screen_base, fb_line_buf, valid_bytes);
        screen_base += width;//+width  定位到LCD下一行显存地址的起点
    }

    //解码完成
    jpeg_finish_decompress(&cinfo); //完成解码
    jpeg_destroy_decompress(&cinfo);//销毁JPEG解码对象、释放资源

    //关闭文件、释放内存
    fclose(jpeg_file);
    free(fb_line_buf);
    free(jpeg_line_buf);
    return 0;
}

int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;

    /* 传参校验 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <jpeg_file>\n", argv[0]);
        exit(-1);
    }

    /* 打开framebuffer设备 */
    if (0 > (fd = open("/dev/fb0", O_RDWR))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    /* 获取参数信息 */
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

    line_length = fb_fix.line_length;
    bpp = fb_var.bits_per_pixel;
    screen_size = line_length * fb_var.yres;
    width = fb_var.xres;
    height = fb_var.yres;

    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }

    /* 显示BMP图片 */
    memset(screen_base, 0xFF, screen_size);
    show_jpeg_image(argv[1]);

    /* 退出 */
    munmap(screen_base, screen_size);  //取消映射
    close(fd);  //关闭文件
    exit(EXIT_SUCCESS);    //退出进程
}

代码就不再讲解了,前面的内容看懂了,代码自然就能看懂!
在 while 循环中,通过 jpeg_read_scanlines()每次读取一行数据,注意,jpeg_read_scanlines()函数的第二个参数是一个 unsigned char **类型指针。读取到数据之后,需要将其转为 RGB565 格式,因为我们这个开发板出厂系统,LCD 是 RGB565 格式的显示设备,这个转化非常简单,没什么可说的,懂的人自然懂!

编译上述代码:

${CC} -o show_jpeg_image show_jpeg_image.c -I /home/zhiguoxin/linux/tool/jpeg/include -L /home/zhiguoxin/linux/tool/jpeg/lib -ljpeg

编译的时候需要指定头文件的路径、库文件的路径以及需要链接的库文件。将编译得到的可执行文件和一个.jpg/.jpeg图像文件挂载到开发板目录下目录下,执行:

./show_jpeg_image image.jpg


此时LCD屏上便会显示这张图片,如下所示:


三、png图像介绍与显示

3.1 PNG 简介

PNG(便携式网络图形格式 PortableNetwork Graphic Format,简称 PNG)是一种采用无损压缩算法的位图格式,其设计目的是试图替代GIF和TIFF 文件,同时增加一些GIF文件所不具备的特性。PNG 使用从LZ77 派生的无损数据压缩算法,它压缩比高,生成文件体积小,并且支持透明效果,所以被广泛使用。

特点

  • 无损压缩:PNG文件采用LZ77算法的派生算法进行压缩,其结果是获得高的压缩比,不损失数据。它利用特殊的编码方法标记重复出现的数据,因而对图像的颜色没有影响,也不可能产生颜色的损失,这样就可以重复保存而不降低图像质量。
  • 体积小:在保证图片清晰、逼真、不失真的前提下,PNG使用从LZ77派生的无损数据压缩算法,它压缩比高,生成文件体积小;
  • 索引彩色模式:PNG-8 格式与 GIF 图像类似,同样采用8位调色板将RGB彩色图像转换为索引彩色图像。图像中保存的不再是各个像素的彩色信息,而是从图像中挑选出来的具有代表性的颜色编号,每一编号对应一种颜色,图像的数据量也因此减少,这对彩色图像的传播非常有利。
  • 更优化的网络传输显示:PNG图像在浏览器上采用流式浏览,即使经过交错处理的图像会在完全下载之前提供浏览者一个基本的图像内容,然后再逐渐清晰起来。它允许连续读出和写入图像数据,这个特性很适合于在通信过程中显示和生成图像。
  • 支持透明效果:PNG可以为原图像定义256个透明层次,使得彩色图像的边缘能与任何背景平滑地融合,从而彻底地消除锯齿边缘。这种功能是GIF和JPEG没有的。

3.2 libpng简介与zlib移植

对于 png 图像,我们可以使用 libpng 库对其进行解码,跟libjpeg 一样,它也是一套免费、开源的 C 语言函数库,支持对png图像文件解码、编码等功能。

zlib其实是一套包含了数据压缩算法的函式库,此函数库为自由软件,是一套免费、开源的C语言函数库,所以我们可以获取到它源代码。

libpng 依赖于zlib库,所以要想移植libpng先得移植zlib库才可以,zlib也好、libpng也好,其实移植过程非常简单,无非就是下载源码、编译源码这样的一些工作。

在移植之前,开发板出厂系统都是已经移植好了这些库,其实是可以直接使用的,但是作为学习,必须要自己亲自把这些库给移植到开发板,这是非常重要的

3.2.1 下载源码包

我们可以进入到https://www.zlib.net/fossils/这个链接地址下载 zlib 源码包:

zlib 源码下载链接
往下翻,找到一个合适的版本,这里我们就选择1.2.10版本的 zlib,点击文件名就可以下载了,下载成功之后就会得到.tar.gz 格式的压缩文件:

3.2.2 编译源码

我这里还是将将下载的 zlib-1.2.10.tar.gz 压缩文件拷贝到 Ubuntu 的共享文件夹下,然后将其解压开:

tar -xzf zlib-1.2.10.tar.gz

 解压之后就会得到zlib-1.2.10文件夹,这就是zlib的源代码目录。

在编译zlib之前,我们先在 tool目录下创建一个名为zlib的文件夹,作为zlib库的安装目录:


接着我们进入到 zlib 的源码目录zlib-1.2.10,如下所示:


同样也是执行三部曲:配置、编译、安装,一套流程下来就 OK 了!
在此之前,先对交叉编译工具的环境进行初始化,使用 source 执行交叉编译工具安装目录下的
environment-setup-cortexa7hf-neon-poky-linux-gnueabi 脚本文件(如果已经初始化过了,那就不用再进行初始
化了):

source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

执行下面这条命令对 zlib 工程进行配置:

./configure --prefix=/home/zhiguoxin/linux/tool/zlib/

–prefix 选项指定 zlib 库的安装目录,将家目录下的 tools/zlib 作为 zlib 库的安装目录。


配置完成之后,直接 make 编译:

make

make 编译 zlib编译完成之后,接着执行 make install 安装即可!

make install


这样就解开那个zlib安装到/home/zhiguoxin/linux/tool/zlib/下面了。

3.2.3 安装目录下的文件夹介绍

进入到zlib库的安装目录:
安装目录下的文件夹

头文件目录 include以及库文件目录 lib。至此,zlib库就已经编译好了,接下来我们需要把编译得到的库文件拷贝到开发板。

3.2.4 移植到开发板

进入到zlib安装目录下,将lib目录下的所有动态链接库文件拷贝到开发板 Linux系统/usr/lib 目录;注意在拷贝之前,需要先将出厂系统中原有的zlib 库文件删除,在开发板 Linux 系统下执行命令:

rm -rf /usr/lib/libz.* /lib/libz.*

删除之后,再将我们编译得到的zlib库文件通过scp命令拷贝到开发板/usr/lib目录,拷贝lib目录下的库文件时,需要注意符号链接的问题,不能破坏原有的符号链接;可以将lib目录下的所有文件打包成压缩包的形式,譬如进入到lib目录,执行命令:

tar -czf libz.tar.gz ./libz.*

拷贝过去之后,开发板/usr/lib目录下就应该存在这些库文件,如下所示:

scp libz.tar.gz root@192.168.10.50:/home/root/

这里如果使用scp命令不成功,可以用其他的方法拷贝。

然后lib.tar.gz就拷贝到开发板了,然后使用下面命令将其解压至/usr/lib 目录下

tar -xmzf libz.tar.gz -C /usr/lib

然后使用下面命令查看是是否成功拷贝至/usr/lib目录。

ls -l /usr/lib/libz.*

开发板/usr/lib 目录下的 zlib 库文件

3.3 libpng移植

移植好zlib库之后,接着我们开始移植libpng。

3.3.1 下载源码包

首先下载libpng源码包,进入https://github.com/glennrp/libpng/releases链接地址,如下:

3.3.2 编译源码

我这里还是将将下载的 zlib-1.2.10.tar.gz 压缩文件拷贝到 Ubuntu 的共享文件夹下,然后将其解压开:

tar -zvxf  libpng-1.6.35.tar.gz


解压之后得到 libpng-1.6.35文件夹,这便是 libpng 的源码目录。在编译 libpng 之前,先在tool目录下创建一个名为png的文件夹,作为libpng库的安装目录:

mkdir png 

 接着我们进入到 libpng 源码目录下,同样也是执行三部曲:配置、编译、安装,一套流程下来就 OK 了!在此之前,先对交叉编译工具的环境进行初始化,使用 source 执行交叉编译工具安装目录下的
environment-setup-cortexa7hf-neon-poky-linux-gnueabi 脚本文件(如果已经初始化过了,那就不用再进行初始化了):

source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

libpng依赖于zlib库,前面我们已经将zlib库编译成功了,但是我们得告知编译器zlib库的安装目录,这样编译器才能找到zlib的库文件以及头文件,编译 libpng 的时才不会报错。

执行下面这三条命令,将zlib库安装目录下的include和lib路径导出到环境变量:

export LDFLAGS="${LDFLAGS} -L/home/zhiguoxin/linux/tool/zlib/lib"
export CFLAGS="${CFLAGS} -I/home/zhiguoxin/linux/tool/zlib/include"
export CPPFLAGS="${CPPFLAGS} -I/home/zhiguoxin/linux/tool/zlib/include"

导出环境变量接着执行下面这条命令对libpng源码工程进行配置:

./configure --prefix=/home/zhiguxoin/linux/tool/png --host=arm-poky-linux-gnueabi

–prefix选项指定libpng的安装目录,目录/home/zhiguxoin/linux/tool/png作为libpng的安装目录。

配置工程
接着执行make进行编译:

make

编译工程
最后执行make install安装即可!

make install


这时候我发现了一个问题,安装没有错误,但是我的安装目录/home/zhiguoxin/linux/tool/png下就是没有任何东西,正常情况下安装目录下应该有bin include lib share这四个文件夹,但是我的没有,我找了好半天问题也值不值为啥没有生成这四个文件夹,于是我就在我就在源码目录下新建了一个文件件,将安装路径换了一下,重新配置、编译、安装发现可以生成那四个文件夹。不知道这是啥原因,所以如果大家也遇到这个问题,可以在源码目录下新建一个文件夹,用来存放安装文件。下面是我的操作命令。

cd /home/zhiguoxin/shared/
tar -zvxf  libpng-1.6.35.tar.gz
cd libpng-1.6.35
mkdir arm_png_install
source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
export LDFLAGS="${LDFLAGS} -L/home/zhiguoxin/linux/tool/zlib/lib"
export CFLAGS="${CFLAGS} -I/home/zhiguoxin/linux/tool/zlib/include"
export CPPFLAGS="${CPPFLAGS} -I/home/zhiguoxin/linux/tool/zlib/include"
./configure --prefix=/home/zhiguoxin/shared/libpng-1.6.35/arm_png_install --host=arm-poky-linux-gnueabi
make
make install

之后我再将这几个文件夹拷贝到/home/zhiguoxin/linux/tool/png目录下。

安装目录下的文件夹介绍

进入到 libpng 安装目录:

同样包含了 bin、include、lib 这些目录。

移植到开发板

进入到 libpng 安装目录,将 bin 目录下的所有测试工具拷贝到开发板 Linux 系统/usr/bin 目录;将 lib 目录下的所有库文件拷贝到 Linux 系统/usr/lib 目录,注意在拷贝之前,先将开发板出厂系统中已经移植好的libpng 库文件删除,执行下面这条命令:

rm -rf /lib/libpng* /usr/lib/libpng*

删除之后,再将编译得到的 libpng 库文件拷贝到开发板/usr/lib 目录,拷贝lib目录下的库文件时,需要注意符号链接的问题,不能破坏原有的符号链接;可以将lib目录下的所有文件打包成压缩包的形式,譬如进入到lib目录,执行命令:

tar -czf lib.tar.gz ./libpng*

拷贝过去之后,开发板/usr/lib目录下就应该存在这些库文件,如下所示:

scp lib.tar.gz root@192.168.10.50:/home/root/

这里如果使用scp命令不成功,可以用其他的方法拷贝。

然后lib.tar.gz就拷贝到开发板了,然后使用下面命令将其解压至/usr/lib 目录下

tar -xmzf lib.tar.gz -C /usr/lib

然后使用下面命令查看是是否成功拷贝至/usr/lib目录。

ls -l /usr/lib/libpng.*

开发板/usr/lib 目录下的 libpng 相关库文件

3.4 libpng 使用说明

libpng 除了解码功能之外,还包含编码功能,也就是创建 png 压缩文件,当然,这个笔者就不再介绍了。libpng 官方提供一份非常详细地使用文档,
笔者也是参考了这份文档给大家进行介绍的,这份文档的链接地址如下:

http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf
http://www.libpng.org/pub/png/libpng-manual.txt

3.5 libpng应用编程

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <png.h>

static int width;                       //LCD X分辨率
static int height;                      //LCD Y分辨率
static unsigned short *screen_base = NULL;  //映射后的显存基地址
static unsigned long line_length;       //LCD一行的长度(字节为单位)
static unsigned int bpp;    //像素深度bpp

static int show_png_image(const char *path)
{
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    FILE *png_file = NULL;
    unsigned short *fb_line_buf = NULL; //行缓冲区:用于存储写入到LCD显存的一行数据
    unsigned int min_h, min_w;
    unsigned int valid_bytes;
    unsigned int image_h, image_w;
    png_bytepp row_pointers = NULL;
    int i, j, k;

    /* 打开png文件 */
    png_file = fopen(path, "r");    //以只读方式打开
    if (NULL == png_file) {
        perror("fopen error");
        return -1;
    }

    /* 分配和初始化png_ptr、info_ptr */
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        fclose(png_file);
        return -1;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        fclose(png_file);
        return -1;
    }

    /* 设置错误返回点 */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        fclose(png_file);
        return -1;
    }

    /* 指定数据源 */
    png_init_io(png_ptr, png_file);

    /* 读取png文件 */
    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_ALPHA, NULL);
    image_h = png_get_image_height(png_ptr, info_ptr);
    image_w = png_get_image_width(png_ptr, info_ptr);
    printf("分辨率: %d*%d\n", image_w, image_h);

    /* 判断是不是RGB888 */
    if ((8 != png_get_bit_depth(png_ptr, info_ptr)) &&
        (PNG_COLOR_TYPE_RGB != png_get_color_type(png_ptr, info_ptr))) {
        printf("Error: Not 8bit depth or not RGB color");
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        fclose(png_file);
        return -1;
    }

    /* 判断图像和LCD屏那个的分辨率更低 */
    if (image_w > width)
        min_w = width;
    else
        min_w = image_w;

    if (image_h > height)
        min_h = height;
    else
        min_h = image_h;

    valid_bytes = min_w * bpp / 8;

    /* 读取解码后的数据 */
    fb_line_buf = malloc(valid_bytes);
    row_pointers = png_get_rows(png_ptr, info_ptr);//获取数据

    unsigned int temp = min_w * 3;  //RGB888 一个像素3个bit位
    for(i = 0; i < min_h; i++) {

        // RGB888转为RGB565
        for(j = k = 0; j < temp; j += 3, k++)
            fb_line_buf[k] = ((row_pointers[i][j] & 0xF8) << 8) |
                ((row_pointers[i][j+1] & 0xFC) << 3) |
                ((row_pointers[i][j+2] & 0xF8) >> 3);

        memcpy(screen_base, fb_line_buf, valid_bytes);//将一行数据刷入显存
        screen_base += width;   //定位到显存下一行
    }

    /* 结束、销毁/释放内存 */
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    free(fb_line_buf);
    fclose(png_file);
    return 0;
}

int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;

    /* 传参校验 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <png_file>\n", argv[0]);
        exit(-1);
    }

    /* 打开framebuffer设备 */
    if (0 > (fd = open("/dev/fb0", O_RDWR))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    /* 获取参数信息 */
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

    line_length = fb_fix.line_length;
    bpp = fb_var.bits_per_pixel;
    screen_size = line_length * fb_var.yres;
    width = fb_var.xres;
    height = fb_var.yres;

    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }

    /* 显示BMP图片 */
    memset(screen_base, 0xFF, screen_size);//屏幕刷白
    show_png_image(argv[1]);

    /* 退出 */
    munmap(screen_base, screen_size);  //取消映射
    close(fd);  //关闭文件
    exit(EXIT_SUCCESS);    //退出进程
}

代码不再进行讲解,上述示例代码使用的是high-level接口处理方式,直接调用了png_read_png,一次性把整个png文件的数据解码出来,由于得到的数据是RGB888 格式,所以我们需要将其转为RGB565,转换完成之后将其刷入到显存中。

接下来我们编译示例代码,这里要注意下,使用交叉编译器编译代码时,需要指定libpng 库和zlib 库,如下所示:

arm-linux-gnueabihf-gcc -o show_png_image show_png_image.c -I /home/zhiguoxin/linux/tool/png/include -L /home/zhiguoxin/linux/tool/png/lib -L /home/zhiguoxin/linux/tool/zlib/lib -lpng -lz

使用-I选项指定libpng的头文件(也就是安装目录下的 include 目录),不需要指定zlib的头文件;使用了两次-L选项,分别指定了libpng和zlib的库目录(也就是安装目录下的 lib 目录);再使用-l 选项指定需要链接的库(z 表示 libz.so、png 表示 libpng.so)。

将编译得到的可执行文件拷贝到开发板Linux 系统的用户家目录下,并准备一个png文件,接着执行测试程序:

./show_png_image image.png

原图

实际效果图

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

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

相关文章

计算机学院第五次ACM周赛题解

目录 HF的智能小车车 Do you like Van game&#xff1f; 好姐姐的三角形 帮帮小陈 卷点 签个到就下班 现在是摸鱼时间 现在是摸鱼时间 PLUS HF的智能小车车 签到题目&#xff0c; #include<iostream>using namespace std;int main() {string arr;cin>>ar…

学生会信息管理系统

1、项目介绍 学生会信息管理系统拥有两种角色&#xff1a;学生和管理员 学生&#xff1a;查看活动和新闻信息、发布活动、登录注册 管理员&#xff1a;公共新闻管理、用户管理、活动审核等 2、项目技术 后端框架&#xff1a; Servlet、mvc模式 前端技术&#xff1a;jsp、c…

Linux(centos)安装Jenkins

Jenkins 是一个开源的, 基于Java的自动服务器&#xff0c;提供了一种建立持续集成和持续交付(CI/CD)流水线的简单方法 。 持续集成 (CI)是DevOps实践中&#xff0c;团队成员定期将他们的代码更改提交到版本仓库, 然后运行自动化的构建和测试. 持续交付(CD)是自动构建、测试和部…

【附源码】计算机毕业设计SSM物业管理系统设计

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

第九章 Ambari二次开发之自定义Flink服务 -- 自定义quiklinks链接

1、自定义quiklink链接功能概述 主要功能&#xff1a;用来配置第三方服务界面链接&#xff0c;如Yarn的管理界面。 服务可以通过在metainfo中加入一串quicklink的引用&#xff08;一串预定义的JSON字符串&#xff09;即可。ambari server能解析json文件并将其提供给UI&#xff…

CSS 2 CSS 选择器 - 2 2.5 属性选择器

CSS 文章目录CSS2 CSS 选择器 - 22.5 属性选择器2.5.1 CSS [attribute] 选择器2.5.2 CSS [attribute"value"] 选择器2.5.3 CSS [attribute~"value"] 选择器2.5.4 CSS [attribute|"value"] 选择器2.5.5 CSS [attribute^"value"] 选择器…

渠道数字化有什么好处?如何实现渠道数字化营销?

数字化转型是企业如今谋求商业升维的一种主要方式&#xff0c;即利用数字化技术和能力来赋能企业经营思维数字化、商业模式创新化&#xff0c;达到业务重构升级、实现业务的转型、创新&#xff0c;增长的目的。对于企业而言&#xff0c;数字化其实正是升维企业业务、企业流程及…

【电子通识】为什么高速信号传输要用差分

早期很多设备要接到计算机上,需要通过一些线径粗连接器笨重的总线。如GPIB接口(通用接口总线)是曾经大多台式仪器与计算机相连接的方式。 在文章:【蓝牙专题】蓝牙测试工具及性能标准 中,电脑就是通过USB转GPIB接口与MT8852进行连接,对低功耗蓝牙模块进行测试。 …

稳压二极管有什么特性?稳压二极管的特点

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

Java泛型——帮助你更好的读懂源码<dog>

泛型 泛型就相当于集合的标签&#xff0c;我们在使用集合<>的时候&#xff0c;就是在使用泛型 要知道的是在jdk1.5以前我们只能把集合中的元素设计为Object&#xff0c;之后引入了泛型的概念来解决&#xff0c;我们把元素的类型设计成一个参数&#xff0c;这个类型参数…

【PyTorch深度学习项目实战100例】—— Pytorch时间序列电力数据多输出预测 | 第70例

前言 大家好,我是阿光。 本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPy…

1024 你学算法了吗?

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

JUC - 多线程之 单例模式(八)

单例模式&#xff08;Singleton Pattern&#xff09;是一种非常简单的设计模式之一&#xff0c;当我们使用的对象要在全局唯一时就需要用到该模式&#xff0c;以保证对象的唯一性。除此之外&#xff0c;还能避免反复的实例化对象&#xff0c;减少内存开销 单例类提供了一种访问…

踩坑记录——USB键盘睡眠唤醒

踩坑记录——USB键盘睡眠唤醒 目录踩坑记录——USB键盘睡眠唤醒前言1 USB远程睡眠唤醒要注意的几个点2 MCU唤醒之后引起USB异常的几个点结束语前言 前段时间我用一个国产MCU实现了雷蛇键盘的效果&#xff0c;按键支持十键无冲&#xff0c;RGB灯支持单控任意一个灯任意一种颜色…

SpringAMQP (RabbitMQ五种模式 消息转换器)

一、简化Hello World模型实现步骤演示 代码步骤演示如下所示&#xff1a; 消息提供者publisher代码简化&#xff1a; 消息消费者consumer代码简化&#xff1a; 二、Work Queue 工作队列模型 消息提供者代码如下所示&#xff1a; 消息消费者代码如下所示&#xff1a; 消息…

SpringCloudAlibaba【六】微服务架构下的秒杀案例

背景 分布式微服务中的秒杀是怎么实现的呢&#xff1f;接着看下去吧 我们实现一个秒杀微服务&#xff0c;流程逻辑如下 项目搭建 MySQL create database if not exists demo;use demo;drop table if exists skill_goods;create table if not exists skill_goods (id bigint…

1024程序员节|【MySQL从入门到精通】【高级篇】(二十七)外连接和内连接如何进行查询优化呢?join的原理了解一波

您好&#xff0c;我是码农飞哥&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 ❤️ 2.网上优质的Python题库很少…

[面试宝典] Linux常见命令及面试题

前言&#xff1a; &#x1f604;作者简介&#xff1a;小曾同学.com,小伙伴们也可以叫我小曾&#xff0c;一个致力于测试开发的博主⛽️ 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;…

C语言 - 你一定能看懂的扫雷万字详解(加入了递归展开和手动标雷的功能)

C语言之扫雷详解&#xff08;包含递归展开和手动标雷功能&#xff0c;非常强大&#xff01;&#xff09; 文章目录一.写在前面二.效果展示三.整体逻辑四.详解1.进入主函数&#xff0c;打印菜单&#xff0c;玩家作出选择2.定义棋盘的数组并进行赋值3.棋盘的展示4.随机布雷5.开始…

前端开发入门--html--Flask

快速开发网站 pip install flaskfrom flask import Flaskapp Flask(__name__)# 创建了网址 /show/info 和 函数index 的对应关系 # 以后用户在浏览器上访问 /show/info&#xff0c;网站自动执行 index app.route("/show/info") def index():return "中国联通&…