簡單易用的圖像解碼庫介紹 —— stb_image


原文鏈接:簡單易用的圖像解碼庫介紹 —— stb_image

說到圖像解碼庫,最容易想起的就是 libpnglibjpeg 這兩個老牌圖像解碼庫了。

libpnglibjpeg 分別各自對應 pngjpeg 兩種圖像格式。這兩種格式的區別如下:

png 支持透明度,無損壓縮的圖片格式,能在保證不失真的情況下盡可能壓縮圖像文件的大小,因此圖像質量高,在一些貼紙應用中也大部分用的是 png 圖片。

jpg 不支持透明度,有損壓縮的圖片格式,有損壓縮會使得原始圖片數據質量下載,也因此它占用的內存小,在網頁應用中加速速度快。

要想在工程中同時解碼 pngjpeg 格式圖片,就必須同時引用這兩種庫,而且還得經過一系列編譯步驟才行。

在這里,介紹一個簡單易用的圖像庫:stb_image 。Github 地址為:https://github.com/nothings/stb ,目前已經有了 9600+ Star 。它的使用非常簡單,看看 README 可能你就會了。

看看它的源碼,你會發現全是 .h 頭文件。這就是它的強大之處了,僅需在工程中加入頭文件就可以解析圖像了(實際上是函數實現等內容都放在頭文件了而已)。

重點關注如下三個頭文件:

  • stb_image.h
    • 用於圖像加載
  • stb_image_write.h
    • 用於寫入圖像文件
  • stb_image_resize.h
    • 用於改變圖像尺寸

下面就開始實踐吧,先給出一個完整的例子:

#include <iostream>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>

using namespace std;

int main() {
    std::cout << "Hello, STB_Image" << std::endl;

    string inputPath = "/Users/glumes/Pictures/input.png";
    int iw, ih, n;
    
    // 加載圖片獲取寬、高、顏色通道信息
    unsigned char *idata = stbi_load(inputPath.c_str(), &iw, &ih, &n, 0);

    int ow = iw / 2;
    int oh = ih / 2;
    auto *odata = (unsigned char *) malloc(ow * oh * n);
    
    // 改變圖片尺寸
    stbir_resize(idata, iw, ih, 0, odata, ow, oh, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0,
                 STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP,
                 STBIR_FILTER_BOX, STBIR_FILTER_BOX,
                 STBIR_COLORSPACE_SRGB, nullptr
    );

    string outputPath = "/Users/glumes/Pictures/output.png";
    // 寫入圖片
    stbi_write_png(outputPath.c_str(), ow, oh, n, odata, 0);

    stbi_image_free(idata);
    stbi_image_free(odata);
    return 0;
}

這個例子很簡單也很全面,主要就是加載了一張圖片,並將它的寬高都縮小一倍,並保存縮小后圖片。

stb_image

首先是調用 stbi_load 方法去加載圖像數據,並獲取相關信息。傳入的參數除了圖片文件地址,還有寬、高、顏色通道信息的引用。

變量 n 就代表圖片的顏色通道值,通常有如下的情況:

  • 1 : 灰度圖
  • 2 : 灰度圖加透明度
  • 3 : 紅綠藍 RGB 三色圖
  • 4 : 紅綠藍加透明度 RGBA 圖

返回的結果就是圖片像素數據的指針了。

stbi_load 不僅僅支持 png 格式,把上面例子中的圖片改成 jpg 格式后綴的依舊可行。

它支持的所有格式如下:

  • png
  • jpg
  • tga
  • bmp
  • psd
  • gif
  • hdr
  • pic

格式雖多,不過一般用到 png 和 jpg 就好了。

除了從文件加載圖片,stb_image 還支持從內存中加載圖片,通過該方法 stbi_load_from_memory ,在后續文章中會用到它的。

加載完圖片之后,stb_image 還提供了相應的釋放方法 stbi_image_free,實際上就是把 free 封裝了一下而已。

sbt_image_resize

加載完圖片像素數據之后,就可以通過 stbir_resize 方法改變圖片的尺寸。

stbir_resize 方法參數有很多:

STBIRDEF int stbir_resize(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
                             void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
                             stbir_datatype datatype,
                             int num_channels, int alpha_channel, int flags,
                             // stb 中提供了多種模式,但是修改后並沒有見很明顯的效果
                             stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, 
                             stbir_filter filter_horizontal,  stbir_filter filter_vertical,
                             stbir_colorspace space, void *alloc_context)

stbir_edgestbir_filter 類型的參數,stb_image_resize 提供了多種類型:

typedef enum
{
    STBIR_EDGE_CLAMP   = 1,
    STBIR_EDGE_REFLECT = 2,
    STBIR_EDGE_WRAP    = 3,
    STBIR_EDGE_ZERO    = 4,
} stbir_edge;

typedef enum
{
    STBIR_FILTER_DEFAULT      = 0,  // use same filter type that easy-to-use API chooses
    STBIR_FILTER_BOX          = 1,  // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
    STBIR_FILTER_TRIANGLE     = 2,  // On upsampling, produces same results as bilinear texture filtering
    STBIR_FILTER_CUBICBSPLINE = 3,  // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
    STBIR_FILTER_CATMULLROM   = 4,  // An interpolating cubic spline
    STBIR_FILTER_MITCHELL     = 5,  // Mitchell-Netrevalli filter with B=1/3, C=1/3
} stbir_filter;

但實際上調整不同類型組合得到的圖片並沒有太多的變化 ┑( ̄Д  ̄)┍。

真正有用的可能還是前面那幾個參數,指定了要將圖片縮放后的寬高數據。

stb_image_write

最后就是調用 stbi_write_png 方法將像素數據寫入文件中,除此之外,stb_image_write 還提供了 stbi_write_jpg 方法來保存 jpg 格式圖片。

根據兩者格式的不同,方法調用的參數也是不一樣的。

int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)

int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)

總結

以上就是關於 stb_image 圖像解碼庫的小介紹。

總的來說,它還是挺簡單易用的,在平常做一些 Demo 以及需要快速實現、驗證功能的情況下都可以考慮考慮。

但是在一些大型的項目中,還是要深思熟慮一些,從性能方面考慮,它肯定不如老牌的圖像解碼庫了,像 libjpeg-turbo 解碼用到了 NEON 這樣 SIMD (單指令流多數據流)的操作,才是大型項目的首選了。

參考

關於 stb_image 在 Android 中的使用實踐,可以參考我的項目:

https://github.com/glumes/InstantGLSL

歡迎關注微信公眾號:【紙上淺談】,獲得最新文章推送~~~

掃碼關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM