作者:星陨
来源:音视频开发进阶
说到图像解码库,最容易想起的就是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中的使用实践,可以参考我的项目:
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。