天天看點

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

作者:星隕

來源:

音視訊開發進階 說到圖像解碼庫,最容易想起的就是

libpng

libjpeg

這兩個老牌圖像解碼庫了。

libpng

libjpeg

分别各自對應

png

jpeg

兩種圖像格式。這兩種格式的差別如下:

png

支援清晰度,無損壓縮的圖檔格式,能在保證不失真的情況下重新壓縮壓縮圖像檔案的大小,是以圖像品質高,在一些貼紙應用中也大部分用的是png圖檔。

jpg

不支援垂直,有損壓縮的圖檔格式,有損壓縮會和原始圖檔資料品質下載下傳,也是以它占用的記憶體小,在網頁應用中加速速度快。

要想在工程中同時解碼

png

jpeg

格式圖檔,就必須同時引用這兩個庫,而且還得通過分段編譯步驟才行。

在這裡,介紹一個簡單易用的圖像庫:

stb_image

。Github位址為:https://github.com/nothings/stb,目前已經有了9600+ Star。它的使用非常簡單,看看README可能你就會了。

看看它的源碼,會你發現全的英文

.h

頭檔案。這就是它的強大之處了,僅需在工程中加入頭檔案就可以解析圖像了(實際上是函數實作等内容都放在頭檔案了而已)。

重點關注如下三個頭檔案:

  • stb_image.h
    • 用于圖像加載
  • stb_image_write.h
    • 用于寫入圖像檔案
  • stb_image_resize.h
    • 用于改變圖像尺寸

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

1#include <iostream>
 2
 3#define STB_IMAGE_IMPLEMENTATION
 4#include "stb_image.h"
 5#define STB_IMAGE_WRITE_IMPLEMENTATION
 6#include "stb_image_write.h"
 7#define STB_IMAGE_RESIZE_IMPLEMENTATION
 8#include "stb_image_resize.h"
 9#include <string>
10#include <stdio.h>
11#include <stdlib.h>
12#include <vector>
13
14using namespace std;
15
16int main() {
17    std::cout << "Hello, STB_Image" << std::endl;
18
19    string inputPath = "/Users/glumes/Pictures/input.png";
20    int iw, ih, n;
21
22    // 加載圖檔擷取寬、高、顔色通道資訊
23    unsigned char *idata = stbi_load(inputPath.c_str(), &iw, &ih, &n, 0);
24
25    int ow = iw / 2;
26    int oh = ih / 2;
27    auto *odata = (unsigned char *) malloc(ow * oh * n);
28
29    // 改變圖檔尺寸
30    stbir_resize(idata, iw, ih, 0, odata, ow, oh, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0,
31                 STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP,
32                 STBIR_FILTER_BOX, STBIR_FILTER_BOX,
33                 STBIR_COLORSPACE_SRGB, nullptr
34    );
35
36    string outputPath = "/Users/glumes/Pictures/output.png";
37    // 寫入圖檔
38    stbi_write_png(outputPath.c_str(), ow, oh, n, odata, 0);
39
40    stbi_image_free(idata);
41    stbi_image_free(odata);
42    return 0;
43}      

這個例子很簡單也很全面,主要就是加載了一張圖檔,變成它的寬高都縮小一倍,并儲存縮小後圖檔。

stb_image

首先是調用

stbi_load

方法去加載圖像資料,并擷取相關資訊。初始的參數除了圖檔檔案位址,還有寬,高,顔色通道資訊的引用。

變量

n

就代表圖檔的顔色通道值,通常有如下的情況:

  • 1:灰色圖
  • 2:灰色圖加雙邊  
  • 3:紅綠藍RGB三色圖
  • 4:紅綠藍加同軸RGBA圖

傳回的結果就是圖檔預期資料的指針了。

stbi_load

不僅僅支援

png

格式,把上面的例子中的圖檔改成

jpg

格式字尾的依舊可行。

它支援的所有格式如下:

  • png
  • jpg
  • ga
  • bmp
  • psd
  • gif
  • 人類發展報告
  • 圖檔

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

除了從檔案加載圖檔,stb_image還支援從記憶體中加載圖檔,通過該方法

stbi_load_from_memory

,在後續文章中會用到它的。

加載完圖檔之後,stb_image還提供了相應的釋放方法

stbi_image_free

,實際上就是把

free

封裝了一下而已。

sbt_image_resize

加載完圖檔估計資料之後,就可以通過

stbir_resize

方法更改圖檔的尺寸。

stbir_resize

方法參數有很多:

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

stbir_edge

stbir_filter

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

1typedef enum
 2{
 3    STBIR_EDGE_CLAMP   = 1,
 4    STBIR_EDGE_REFLECT = 2,
 5    STBIR_EDGE_WRAP    = 3,
 6    STBIR_EDGE_ZERO    = 4,
 7} stbir_edge;
 8
 9typedef enum
10{
11    STBIR_FILTER_DEFAULT      = 0,  // use same filter type that easy-to-use API chooses
12    STBIR_FILTER_BOX          = 1,  // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
13    STBIR_FILTER_TRIANGLE     = 2,  // On upsampling, produces same results as bilinear texture filtering
14    STBIR_FILTER_CUBICBSPLINE = 3,  // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
15    STBIR_FILTER_CATMULLROM   = 4,  // An interpolating cubic spline
16    STBIR_FILTER_MITCHELL     = 5,  // Mitchell-Netrevalli filter with B=1/3, C=1/3
17} stbir_filter;      

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

真正有用的可能還是前面那幾個參數,指定了預期的圖檔縮放後的寬高資料。

stb_image_write

最後就是調用

stbi_write_png

方法将資料寫入檔案中,另外,stb_image_write還提供了

stbi_write_jpg

方法來儲存jpg格式圖檔。

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

1int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
2
3int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
4      

總結

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

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

但是在一些大型的項目中,還是要深思熟慮一些,從性能方面考慮,它肯定不如老牌的圖像解碼庫了,像

libjpeg-turbo

解碼用到了

NEON

這樣SIMD(單指令流多資料流)的操作,才是大型項目的首選了。

參考

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

https://github.com/glumes/InstantGLSL
安卓以及音視訊雜談
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。  
簡單易用的圖像解碼庫介紹-stb_image

繼續閱讀