惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
S
Secure Thoughts
S
Schneier on Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Privacy International News Feed
L
Lohrmann on Cybersecurity
Schneier on Security
Schneier on Security
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Latest news
Latest news
H
Hacker News: Front Page
月光博客
月光博客
Forbes - Security
Forbes - Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Security @ Cisco Blogs
WordPress大学
WordPress大学
Recent Commits to openclaw:main
Recent Commits to openclaw:main
aimingoo的专栏
aimingoo的专栏
宝玉的分享
宝玉的分享
D
Docker
U
Unit 42
Recorded Future
Recorded Future
Spread Privacy
Spread Privacy
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
Application and Cybersecurity Blog
Application and Cybersecurity Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Heimdal Security Blog
Microsoft Azure Blog
Microsoft Azure Blog
V
Vulnerabilities – Threatpost
Vercel News
Vercel News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
博客园 - 聂微东
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog
G
GRAHAM CLULEY
Apple Machine Learning Research
Apple Machine Learning Research
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Blog — PlanetScale
Blog — PlanetScale
博客园 - 三生石上(FineUI控件)
罗磊的独立博客
Help Net Security
Help Net Security
Google Online Security Blog
Google Online Security Blog
S
SegmentFault 最新的问题

音视频开发进阶

音视频教程-第三节 音视频教程-第二节 真的,AI 可能就是新时代的信息差 充值 Cursor 之后,工作有了哪些变化?🤔 个人'蒸馏'大模型能做哪些有意思的事情 DeepSeek 大模型在 Mac 上的部署和运行 音视频教程-第一节 【WebRTC 专栏】-- Android 开发集成 WebRTC 库的几种方式 【WebRTC 专栏】-- 在 Mac M1 等系列芯片编译和开发 WebRTC-Android 库 Meta Llama3 大模型在 Mac 上的部署和运行 iOS VideoToolBox 解码 HEVC Open-GOP 视频的问题排查 Flutter 状态管理之 InheritedWidget 使用和分析 用 ChatGPT 回答技术问题怎么样 ? 音视频开发系统入门大致路线 UE 4.27 添加自定义 ShadingModel 用 UE4 虚幻引擎做个捏脸小功能~~ UE4 材质练习 之 凹凸贴图偏移的使用 UE4 材质练习系列基础 OpenGL上下文创建以及共享机制 007 | 播放器系列专栏-解析 MP4 文件读取信息 006 | 播放器系列专栏-在 Mac 上查看 MP4 格式信息 干货 | 快速抽取缩略图是怎么练成的? 关于直播、WebRTC、FFmpeg 的那些事 005 | 播放器系列专栏-在 Windows 上查看 MP4 格式信息 将音视频中的花屏、绿屏、黑屏问题一网打尽 关于音视频里面的解码帧率和渲染帧率 004 | 播放器系列专栏-认识MP4视频(下) 003 | 播放器系列专栏-认识MP4视频(上) 入门或者转行音视频,应该要怎么做? H264视频文件如何缩放分辨率 002 | 播放器系列专栏-FFmpeg依赖库的配置 001 | 播放器系列专栏-关于播放器项目的一个小实践 Seek策略以及在有B帧情况下的处理 目前流媒体开发工程师工作内容主要是什么? 一个音视频领域专业问答的小圈子 干货收藏 || Vulkan Game Engine 视频教程 音视频春节假期内卷指南(实操) Vulkan 在 FFmpeg 中的支持 Windows 下 FFmpeg 和 LibX264 的编译和配置 Metal 开发 | 使用 C++ 进行接口调用 音视频开发工作经验分享 || 视频版 FFmpeg 调用 MediaCodec 硬解码到 Surface 上 代码吸猫 | 用 OpenGL 图像渲染的养猫计划 百倍变速--解码到底能不能丢 非参考帧 ?FFmpeg 有话说!!! 老生常谈-FFmpeg 的编译问题轻松搞定 FFmpeg 调用 Android MediaCodec 进行硬解码(附源码) 【WebRTC 专栏】--创建相机预览 Unity Shader 光照基础之 Half Lambert 光照模型 Unity Shader 光照基础之Lambert光照模型 Unity Shader 光照基础内容 Unity Shader 显示一张图片纹理 UnityShader 的基本概念 Unity 物体的基本操作 C++ 模板系列小结07-尾置返回类型 C++ 模板系列小结06-可变参数模板特性 C++ 中的多线程的使用和线程池建设 C++ 模板系列小结05-模板类型作为模板参数 C++ 模板系列小结04-类模板中的成员模板 C++ 模板系列小结03-在模板中指定变量类型 C++ 模板系列小结02-非类型模板参数 C++ 模板系列小结01-函数模板和类模板 从零打造渲染引擎系列01-什么是渲染引擎 iOS开发 - 在 Swift 中去调用 C/C++ 代码 2021 技术新番 - 从零打造渲染引擎系列 iOS 音视频开发的一些基础准备工作 音视频交流群又来啦~~~ 【WebRTC 专栏】WebRTC & Android 开发学习环境搭建~ 【喜大普奔】域名终于备案通过啦 Shader 优化 | OpenGL 绘制网格效果 【音视频连载-011】第二季 FFmpeg 一层一层获取文件信息 KodeLife | Shader 实时编辑预览的强大工具使用实践 推荐几个堪称教科书级别的 Android 音视频入门项目 【音视频连载-010】第二季 FFmpeg 日志打印 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下) 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上) 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示 【音视频连载-004】基础学习篇-SDL 加载图片并显示 【音视频连载-003】基础学习篇-SDL 消息循环和事件响应 【音视频连载-002】基础学习篇-SDL 创建窗口并显示颜色 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置 LearnOpenGL 源码在 MAC 上的编译与调试 2019 年终总结与回顾 Android NDK 开发的免费技术视频来啦~~ OpenGL 实现视频编辑中的转场效果 OpenGL 实践之贝塞尔曲线绘制 图像库 libjpeg-turbo 编译与实践 图像库 libpng 编译与实践 rust 开发编译 Android 动态库实践 Android NDK 开发 —— 从 Assets 文件夹加载图片并上传纹理 博客图床迁移记 进击的 Vulkan 移动开发之 SwapChain 进击的 Vulkan 移动开发之 Command Buffer 进击的 Vulkan 移动开发之 Instance & Device & Queue 进击的 Vulkan 移动开发(一)之今生前世 Java 显式锁 Lock 与条件队列 C++ 标准容器库小结 MediaCodec 硬编码之相机内容编码成 H264 文件 一文读懂 YUV 的采样与格式 《OpenGL ES 3.x 游戏开发》碰撞检测之 AABB 包围盒
简单易用的图像解码库介绍 —— stb_image
音视频开发进阶 · 2019-05-12 · via 音视频开发进阶

一个专注音视频领域的小圈子

避免图片丢失,建议阅读微信原文:

https://mp.weixin.qq.com/s/Mh_cLQeRy5J5AufeaGaOmA 说到图像解码库,最容易想起的就是 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;
}

这个例子很简单也很全面,主要就是加载了一张图片,并将它的宽高都缩小一倍,并保存缩小后图片。

首先是调用 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

原创文章,转载请注明来源:    简单易用的图像解码库介绍 —— stb_image