图像加载

目录

图像加载

一、前言

二、对比

三、stb_image

四、FreeImage

五、libpng


一、前言

libpng、FreeImage、stb_image都是图像解析的开源库,由于三者我都简单使用过,于是做个总结对比。

二、对比

开源协议 编译依赖 win编译难度 使用难度 格式支持
libpng zlib zlib 自带vs工程 只支持png
FreeImage 混合 自带了7、8个库 有dll发行版 简单 很多
stb_image MIT 只有头文件 简单 常用的几个

它们的官网如下:

libpng Home Page

The FreeImage Project

GitHub - nothings/stb: stb single-file public domain libraries for C/C++

总的来说,它们的开源协议都问题不大。源码编译stb_image最简单,因为它只有一个头文件。而libpng是操作png文件的库,代码比较复杂,但是它的优点是灵活,速度更快。FreeImage集成了各种加载库,支持的格式比较多。

接下来,我就按使用难度给出它们的基本代码。注意均是读取到uint32_t缓冲区,代表RGBA32位颜色,如下:

class Image
{
//otheruint32_t* _data;array<unsigned, 2> _size;
}

注意我并没有处理字节序问题,是写死的,在不同大小端的系统运行,应该改进一下代码(我懒得很,要等到遇到问题再解决,就是交换一下单个像素RGBA通道的读取顺序)。

觉得有用,请点赞、收藏、关注。我写这篇文件就是因为有人get了我以前写的libpng的文章。

三、stb_image

读取如下,确实很简单,直接返回的就是RGBA32位颜色:

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>dnd::Image* Image::Create(string_view path_name)
{int texWidth, texHeight, texChannels;stbi_uc* pixels = stbi_load(string{ path_name }.c_str(),&texWidth, &texHeight, &texChannels, STBI_rgb_alpha);if (!pixels) {debug_err(format("加载图像失败:{}", path_name));return nullptr;}Image* ret = new Image;ret->_data = (uint32_t*)pixels;ret->_size = { (unsigned)texWidth, (unsigned)texHeight };return ret;
}

导出图像数据更简单;

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>bool Image::SaveToFile(string_view path_name)
{return stbi_write_png(string{ path_name }.c_str(), _size[0], _size[1], 4, _data, 0);
}

注意,我们应该使用它的释放函数来释放资源,转回stbi_uc*类型(实际上也就是调用了free函数,不过大家都懂不要自己free):

stbi_image_free((stbi_uc*)_data);

四、FreeImage

代码中用到的一些函数(语法糖)如下:

/**
* @brief RAII清理操作
*/
template<typename F>
class finally
{
public:finally(F&& func) : _func(func) {}~finally() { _func(); }
private:F _func;
};//! 对容器任意元素的判断
namespace Any
{/*** @brief 判断容器任意元素等于某值* @param[in] container 操作的容器* @param[in] v 要比较的值
*/
template<typename C, typename V>
bool Equal(const C& container, const V& v)
{for (auto& iter : container){if (iter == v)return true;}return false;
}}

读取代码如下:

#include <FreeImage.h>string path_name_mb = String::cvt_u8_mb(path_name);
const char* filename = path_name_mb.c_str();//读文件头判断格式
FREE_IMAGE_FORMAT file_format = FreeImage_GetFileType(filename, 0);
if (file_format == FIF_UNKNOWN)
{//通过文件名读取file_format = FreeImage_GetFIFFromFilename(filename);
}if (file_format == FIF_UNKNOWN)
{debug_err("图像文件格式不支持:" + string{ path_name });return nullptr;
}
//格式支持读取则读取
FIBITMAP* bitmap = nullptr;
if (FreeImage_FIFSupportsReading(file_format))
{bitmap = FreeImage_Load(file_format, filename);
}if (!bitmap)
{return nullptr;
}//资源释放
finally f0([&]() {FreeImage_Unload(bitmap);
});
//
unsigned bpp = FreeImage_GetBPP(bitmap);           //取像素深度
FREE_IMAGE_TYPE file_type = FreeImage_GetImageType(bitmap);    //取数据类型
BYTE* bits = FreeImage_GetBits(bitmap);                //取像素数组
unsigned w = FreeImage_GetWidth(bitmap);           //宽
unsigned h = FreeImage_GetHeight(bitmap);          //高
unsigned pitch = FreeImage_GetPitch(bitmap);       //每行像素(freeimage自动做了32位对齐, gl默认也是32位对齐)
if (bits == 0 || w == 0 || h == 0)
{debug_err("图像文件基本数据错误:" + string{ path_name });return nullptr;
}
//
if (file_type != FIT_BITMAP)
{debug_err("图像文件类型不是位图:" + string{ path_name });return nullptr;
}
//
vector<unsigned> mul_bpp{ 32, 24, 8 };
if (!Any::Equal(mul_bpp, bpp))
{debug_err(format("图像文件不支持的色深({}):", bpp, path_name));return nullptr;
}//
Image* ret = new Image;
ret->_size = { w, h };
size_t length = size_t(w * h);
ret->_buffer = new uint32[length];//BGRA => ABGR(RGBA) 单个访问时有字节序问题,需要反过来
if (bpp == 32)
{   for (unsigned x = 0; x < w; ++x){for (unsigned y = 0; y < h; ++y){unsigned p0 = y * w + x;unsigned p1 = (h - 1 - y) * pitch + x * 4;char* p = (char*)&(ret->_buffer[p0]);p[0] = bits[p1 + 2];p[1] = bits[p1 + 1];p[2] = bits[p1 + 0];p[3] = bits[p1 + 3];}}
}
else if (bpp == 24)
{for (unsigned x = 0; x < w; x++){for (unsigned y = 0; y < h; y++){unsigned p0 = y * w + x;unsigned p1 = (h - 1 - y) * pitch + x * 3;char* p = (char*)&(ret->_buffer[p0]);p[0] = bits[p1 + 2];p[1] = bits[p1 + 1];p[2] = bits[p1 + 0];p[3] = (char)255;}}
}
else if (bpp == 8)
{for (unsigned x = 0; x < w; x++){for (unsigned y = 0; y < h; y++){unsigned p0 = y * w + x;unsigned p1 = (h - 1 - y) * pitch + x * 1;char* p = (char*)&(ret->_buffer[p0]);p[0] = bits[p1];p[1] = bits[p1];p[2] = bits[p1];p[3] = (char)255;}}
}return ret;

导出图像数据到文件:

string path_name_mb = String::cvt_u8_mb(path_name);
const char* filename = path_name_mb.c_str();FREE_IMAGE_FORMAT file_format = FreeImage_GetFIFFromFilename(filename);
if (file_format == FIF_UNKNOWN)
{debug_err("不支持的图像格式:" + string{ path_name });return false;
}
unsigned w = _size[0];
unsigned h = _size[1];uint32_t* bits = new uint32_t[w * h];//ABGR(RGBA)=> BGRA  单个访问时有字节序问题,需要反过来
for (unsigned x = 0; x < w; ++x)
{for (unsigned y = 0; y < h; ++y){unsigned index = y * w + x;char* p_dst = (char*)&(bits[index]);char* p = (char*)&(_buffer[index]);p_dst[0] = p[2];p_dst[1] = p[1];p_dst[2] = p[0];p_dst[3] = p[3];}
}
//
FIBITMAP* bitmap = FreeImage_ConvertFromRawBits((BYTE*)bits,w, h, w * 4, 32, 0, 0, 0, true);delete[] bits;FreeImage_Save(file_format, bitmap, filename);//释放
FreeImage_Unload(bitmap);return true;

五、libpng

这里的代码比较陈旧,用了win32的类型,自行修改一下即可。加载代码如下:

#include <png.h>
Image* img = new Image;///从文件加载/
FILE* fp = NULL;
if (sys->GetFile(path, fp) == -1)
{debug_err(String(L"DND: Image::Create: 图像文件打开失败: ") + path);return NULL;
}//判断是否问 png 文件
size_t number = 8;
png_bytep header = new png_byte[number];
fread(header, 1, number, fp);
bool is_png = !png_sig_cmp(header, 0, number);
if (!is_png)
{fclose(fp);debug_err(String(L"DND: Image::Create: 必须是png文件: ") + path);return NULL;
}
//初始化pnglib
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);
if (!png_ptr)
{fclose(fp);debug_err(L"DND: Image::Create: 初始化pnglib失败!");return NULL;
}
//创建图像信息 info
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{fclose(fp);debug_err(L"DND: Image::Create: 创建png_info失败!");return NULL;
}
//错误处理
if (setjmp(png_jmpbuf(png_ptr)))
{fclose(fp);png_destroy_read_struct(&png_ptr, &info_ptr, NULL);debug_err(L"DND: Image::Create: pnglib出现错误!");return NULL;
}//设置数据源
png_init_io(png_ptr, fp);//表明文件头已处理
png_set_sig_bytes(png_ptr, number);//读png 这一步会实际分配内存
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);fclose(fp);//从info查询数据
unsigned w = png_get_image_width(png_ptr, info_ptr);        //获得图片宽度
unsigned h = png_get_image_height(png_ptr, info_ptr);        //获得图片高度
int color_type = png_get_color_type(png_ptr, info_ptr);        //获得图片颜色类型//赋值image
img->_size.w = w;
img->_size.h = h;img->_buffer = new DWORD[w*h];//从info 复制到 image
png_bytep *row_point = NULL;
row_point = png_get_rows(png_ptr, info_ptr);int block_size = (color_type == 6 ? 4 : 3);//(A)RGBunsigned pos = 0;
for (unsigned x = 0; x < h; ++x)for (unsigned y = 0; y < w*block_size; y += block_size){((unsigned char*)img->_buffer)[pos + 0] = row_point[x][y + 2];//b;((unsigned char*)img->_buffer)[pos + 1] = row_point[x][y + 1];//g((unsigned char*)img->_buffer)[pos + 2] = row_point[x][y + 0];//rif (color_type == 6)((unsigned char*)img->_buffer)[pos + 3] = row_point[x][y + 3];//aelse((unsigned char*)img->_buffer)[pos + 3] = 0xff;pos += 4;}//释放png内存
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);return img;

导出图像数据到文件:

FILE* fp;png_infop info_ptr;char cpath[MAX_PATH] = { NULL };
path.GetMultiByteStr(cpath, MAX_PATH);fopen_s(&fp, cpath, "wb");
if (fp == NULL)
{debug_err(L"DND: Image::SaveToPNG: 创建文件失败!");return;
}//初始化pnglib
static png_structp png_ptr = NULL;
if (!png_ptr)png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);
if (!png_ptr)
{debug_err(L"DND: Image::SaveToPNG: 创建文件时初始化pnglib失败!");return;
}info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{fclose(fp);debug_err(L"DND: Image::SaveToPNG: png_create_info_struct失败!");return;
}//错误处理
if (setjmp(png_jmpbuf(png_ptr)))
{fclose(fp);png_destroy_write_struct(&png_ptr, (png_infopp)NULL);debug_err(L"DND: Image::SaveToPNG: pnglib 出现错误!");return;
}unsigned bit_depth = 8;
unsigned pixel_byte = 4;
unsigned row_byte = _size.w * pixel_byte;//设置输出控制
png_init_io(png_ptr, fp);//设置图像属性
png_set_IHDR(png_ptr, info_ptr, _size.w, _size.h, bit_depth,PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, //交错无PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);//写头部
png_write_info(png_ptr, info_ptr);//获取行指针
png_bytepp row_pointers = (png_bytep*)malloc(_size.h*sizeof(png_bytep));for (unsigned x = 0; x < _size.h; ++x)
{//分配一行row_pointers[x] = (png_bytep)malloc(row_byte);for (unsigned y = 0; y < row_byte; y += pixel_byte){row_pointers[x][y + 2] = ((unsigned char*)_buffer)[x * row_byte + y + 0];row_pointers[x][y + 1] = ((unsigned char*)_buffer)[x * row_byte + y + 1];row_pointers[x][y + 0] = ((unsigned char*)_buffer)[x * row_byte + y + 2];row_pointers[x][y + 3] = ((unsigned char*)_buffer)[x * row_byte + y + 3];/*row_pointers[x][y + 2] =row_pointers[x][y + 1] =row_pointers[x][y + 0] =row_pointers[x][y + 3] = 0xff;*/}
}
//写入全部
png_write_image(png_ptr, row_pointers);
//写尾部
png_write_end(png_ptr, info_ptr);//释放png内存
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);/*    delete[] row_pointers;
delete[] image;*/
for (unsigned x = 0; x < _size.h; ++x)
{//释放每行free(row_pointers[x]);
}
free(row_pointers);
fclose(fp);

【C++】图像加载(libpng、FreeImage、stb_image)相关推荐

  1. 好轮子收藏:一个支持几乎所有流行格式的图像加载库stb_image.h

    是在opengl教程网站上看到的,挺好用. stb_image.h一个非常流行的单头文件图像加载库,它能够加载大部分流行的文件格式,并且能够很简单得整合到你的工程之中. 下载地址:stb/stb_im ...

  2. OpenCV 图像加载和显示

    OpenCV 图像加载和显示 OpenCV 图像加载和显示 加载图像(使用cv :: imread) 创建一个名为OpenCV的窗口(使用cv :: namedWindow) 在OpenCV窗口中显示 ...

  3. python使用openCV图像加载(转化为灰度图像)、Canny边缘检测器检测图像的边缘(Detect Edges)

    python使用openCV图像加载(转化为灰度图像).Canny边缘检测器检测图像的边缘(Detect Edges) 目录

  4. python使用openCV图像加载(转化为灰度图像)、使用filter2D函数对图像进行锐化(Sharpen Images)

    python使用openCV图像加载(转化为灰度图像).使用filter2D函数对图像进行锐化(Sharpen Images) 目录

  5. 要求jQuery在执行某些操作之前等待所有图像加载的官方方式

    在jQuery中,当您执行以下操作时: $(function() {alert("DOM is loaded, but images not necessarily all loaded&q ...

  6. 【OpenCV学习笔记之一】图像加载,修改及保存

    加载图像(用cv::imread) imread功能是加载图像文件成为一个Mat对象 其中第一个参数表示图像文件名称 第二个参数 表示加载的图像是什么类型 支持常见的三个参数值 IMREAD_UNCH ...

  7. html5实例异步图片加载,javascript – 你如何处理html5的画布图像加载异步?

    我一直在学习html5的画布.因为图像可能需要一段时间才能加载,所以适当的技术似乎是在尝试绘制图像之前使用onload来等待图像加载.所以: var fig = document.getElement ...

  8. img 加载 svg占位符_如何使用SVG作为占位符以及其他图像加载技术

    img 加载 svg占位符 by José M. Pérez 由JoséM.Pérez 如何使用SVG作为占位符以及其他图像加载技术 (How to use SVG as a Placeholder, ...

  9. [Swift通天遁地]五、高级扩展-(11)图像加载Loading动画效果的自定义和缓存

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

  10. 【前端】【cornerstone】cornerstone.js如何编辑图像/加载已有图像数据(以画直线为例)

    [前端][cornerstone]cornerstone.js如何编辑图像/加载已有图像数据(以画直线为例) 首次加载图像 加载已有图像 部分参考博客:<cornerstone.js 使用总结& ...

最新文章

  1. 给虚拟机中的linux系统增加一个硬盘
  2. Java黑皮书课后题第1章:1.10(以英里计的平均速度)假设一个跑步者45分30秒跑了14千米。编写一个程序显示以每小时多少英里为单位的平均速度值
  3. IE请求json数据时出现下载文件的现象。
  4. jboss mysql cluster_jboss配置mysql数据库连接池
  5. python怎样装pandas_小白学python:坑一:如何安装pandas和numpy
  6. win7中竟然没有telnet.exe??
  7. Session一次错误记录
  8. NVT NT98510 SDK介绍
  9. 芯邦主控芯片CBM2199E量产工具设置
  10. Ubuntu系统搜狗拼音无法输出
  11. ubuntu浏览器突然使用不了搜狗拼音法
  12. python中render是什么意思_Django中render_to_response和render的区别(转载)
  13. 经纬财富:巴中美联储措辞乐观 白银难突出重围
  14. 广东侨乡台山人“小年夜”喜用传统沙琪玛祭灶
  15. 永磁直流电机 matlab仿真,永磁直流电动机的Simulink建模仿真教学.doc
  16. erp故障处理流程图_完整ERP流程图
  17. 电脑pro,iPad Pro 2018体验:这个电脑,不是一般的电脑
  18. 基于Python的五子棋人机对战
  19. 今日头条小程序内测!BAT早已收割,张一鸣还能分到羹吗?
  20. RocketMQ高可用机制----同步刷盘、异步刷盘和同步复制、异步复制

热门文章

  1. 计算机进入屏保快捷键,电脑如何快速进入屏保
  2. mysql jdbc8.0驱动包下载_JDBC驱动jar包|JDBC驱动(mysql connector java)下载v8.0.11安装包 - 欧普软件下载...
  3. Windows开启网络对时方法
  4. 什么是Overlay网络?Underlay 网络 vs. Overlay网络
  5. WIN7 Activation
  6. 浅谈CDN技术的发展历程
  7. 关于下载的NetMeeting无法共享的解决办法
  8. 笔记本电脑桌面计算机图标不见了怎么办,桌面图标不见了怎么办,教您电脑桌面图标不见了怎么办...
  9. 鼠标单击变双击问题排查
  10. 基于javaweb的在线学习系统