庫的移植和使用---例子:JPEG的解碼和編碼

来源:https://www.cnblogs.com/fly-home/p/18190179
-Advertisement-
Play Games

目錄開源庫移植步驟[1]:下載庫的源碼包[2]:解壓,且閱讀“README(自述文件)",瞭解對應庫的使用規則[3]:打開源碼中的install.txt的文本,學習庫的移植和安裝步驟[4]:把下載好的源碼包jpegsrc.v9f.tar.gz發送到linux系統的家目錄下進行解壓[5] :“配置” ...


目錄

開源庫移植步驟

註:以下操作均以 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;	//R
            data |= buffer[3*i+1]<<8;	//G
            data |= 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	 :  [email protected]
*	date	 :  2024/05/13
*	function :  該案例是掌握JPEG的解碼過程
* 	note	 :  None
*
*	CopyRight (c)  2023-2024   [email protected]   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	 :  [email protected]
*	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;	//R
            data |= buffer[3*i+1]<<8;	//G
            data |= 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進行記憶體映射  mmap
	int *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.顯示一張jpg
	read_JPEG_file (argv[1],start_x,start_y,lcd_mp);

	return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linux 可用 pid 上限是多少?如何提升上限?為何提升上限可以實時生效?Linux 底層如何實現 pid 快速分配與歸還?這種實現為何只需要極少的記憶體開銷?本文通過閱讀 Linux 內核源碼,一一為你揭秘 ...
  • 大家好,我是 Java陳序員。 俗話說,上班不摸魚,不如當頭驢。上班不摸魚是沒有靈魂的! 但是,上班摸魚需要有一定的技巧,需要與老闆鬥智鬥勇,需要時時刻刻註意查崗。 今天,給大家安利一個摸魚APP,幫助你更好的摸魚! 關註微信公眾號:【Java陳序員】,獲取開源項目分享、AI副業分享、超200本經典 ...
  • @目錄前言第一步:查看Docker Root目錄第二步:查到容器的長id(container id)第三步:停止容器第四步:編輯修改環境變數env第五步:重載服務的配置文件第六步:重啟docker總結 前言 請各大網友尊重本人原創知識分享,謹記本人博客:南國以南i、 提示:以下是本篇文章正文內容,下 ...
  • 目錄題目題目分析思路解析知識點涉及代碼展示優化思考問題一:觀察界面切換效果,可明顯觀察到界面切換時有明顯的刷新效果,有點影響使用效果問題二:圖片的按鍵位置不能相近或者重合,否則有誤觸導致執行了別的功能問題三:當快速來回點擊觸摸屏兩個位置時,會出現點擊位置坐標讀取與實際觸摸坐標不一致的情況 題目 設計 ...
  • /** * @date 2024/05/14 * CopyRight (c) 2023-2024 [email protected] All Right Reseverd */ #include <stdio.h> #include <stdlib.h> #include <sys/types. ...
  • KylinV10SP2實現ARM和x86架構系統PXE部署(S3) 本文介紹在esxi(虛擬化)中Centos7.9操作系統上部署PXE服務端,集成麒麟系統安裝源,TFTP服務,DHCP服務,HTTP服務,能夠向裸機發送PXE引導程式、Linux內核、啟動菜單等數據,以及提供安裝文件。 系統引導模式 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是恩智浦i.MX RT1170 uSDHC eMMC啟動時間。 本篇是 i.MXRT1170 啟動時間評測第五彈,前四篇分別給大家評測了 Raw NAND 啟動時間(基於 MIMXRT1170-EVK_Rev.B)、Serial NOR ...
  • 在Linux中可以不需要有腳本或者二進位程式的文件在文件系統上實際存在,只需要有對應的數據在記憶體中,就有辦法執行這些腳本和程式。 原理其實很簡單,Linux里有辦法把某塊記憶體映射成文件描述符,對於每一個文件描述符,Linux會在/proc/self/fd/<文件描述符>這個路徑上創建一個對應描述符的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...