解析png格式的图像

前言

鉴于有人收藏我07年写的博客,是关于解析png格式图像的那一篇。所以我打算再写一篇防止坑你们……

1.图像的内存表示

反复说到计算机中只有二进制表示,那图像如何以二进制表示呢?现在常用的规范是用4个字节表示颜色,分为4个通道ARGB,分别为透明度、红色、绿色、蓝色,这是比较常用的颜色表示方式。还有其他的色彩模型,例如HSV色彩模型,分别为色度、饱和度、纯度。还有HLS色彩模型,为色度、亮度、饱和度。

在绘图库和显示器等层面,能够直接处理的应该是RGB颜色模型。也就是通过3个变量控制最终的颜色(加上透明度是4个),而在显示器上则是1个像素包含3种颜色的灯,控制强度从而呈现不同的颜色(或许实现还有更多细节,也可能是不同的方法)。小时候如果你用眼睛贴到电视上,应该能够发现许多颜色不同的小条。以前低分辨率的手机屏幕也能发现类型的情形,现在之所以看不出来是因为像素点越来越小,不太容易看出来(人通过3种不同的视锥细胞感觉颜色)。真实世界的颜色是原子那么小的,渐变也更加平滑,而显示器的颜色实际是很大一颗一颗的,所以看显示器和看真实世界,背后的区别很大。

一般行业内都用十六进制来表示颜色,前面说了4个字节,1个字节表示1个通道。那么1个字节可表示256个值,用十六进制表示就需要2位。而十六进制用0123456789abcdef十六个符号表示,所以2位十六进制表示范围为[00, ff],那么一个颜色就需要8位十六进制表示,在代码中一般加前缀0x代表此数字是十六进制。所以颜色可以表示如下:

A R G B 颜色
0xff000000 0xff 0x00 0x00 0x00 不透明黑色
0xff00ff00 0xff 0x00 0xff 0x00 不透明绿色
0x66ff00ff 0x66 0xff 0x00 0xff 半透明紫色(红配蓝为紫)
0x00ffffff 0x00 0xff 0xff 0xff 全透明白色

在各种软件中,通过拾色器也能看到颜色代码。如果用十进制表示,则每个分量的范围为[0-255]。用整个十六进制数字表示即可以是6位,如果包含透明通道就是8位,如下图所示:

2.PNG格式

此时我们可以计算一下120*120像素的图像需要多大的空间保存,首先一个像素占4个字节,那么14400个像素就是占57600字节,也就是56.25kb。而一个png格式的图像,120*120的大小实际上,大概占几kb到二十几kb。

PNG格式全称Portable Network Graphics,中文翻译为便携式网络图形,是一种无损压缩位图格式。在不损失图像颜色信息的情况下,压缩保存图像数据。由于能够减小图像保存的大小,所以它才有存在的价值。相比JPEG格式,它是无损的,且带有透明通道,并且是可免费商用的,所以适用于游戏制作,但体积比jpeg略大。

关于PNG格式更详细的数据格式,此处就不展开了,只说怎么用libpng库来读取和保存png格式图像。

3.libpng库

libpng库依赖zlib库,所以需要二者都配置,具体怎么配置,可以参考我的其他文章(不过我现在还没写),也可以参考网上的教程。

4.读取过程

首先包含头文件:

#include <png>
#include <stdio.h>//这里使用C语言的读写文件

先以标准的读文件方式,读取图像文件:

FILE* fp = NULL;
_wfopen_s(&fp, "test.png", L"rb");

接着读取8个字节确认是不是png格式:

        //判断是否为 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);//必须是png文件return NULL;}

创建必要的结构:

        //创建read struct png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);if (!png_ptr){fclose(fp);return NULL;}//创建图像信息 infopng_infop info_ptr = png_create_info_struct(png_ptr);if (!info_ptr){fclose(fp);return NULL;}//错误处理if (setjmp(png_jmpbuf(png_ptr))){fclose(fp);//失败,则释放前面创建的结构,防止内存泄漏png_destroy_read_struct(&png_ptr, &info_ptr, NULL);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结构,可以获取图像的一些信息,其中color_type可以知道是否包含透明通道数据,之后处理需要用到它:

     //从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);        //获得图片颜色类型

接下来就是读取过程:

         //分配内存保存从png解析出的数据img->_buffer = new DWORD[w*h];//从info 复制到 imagepng_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;//没有透明通道数据,则填充不透明0xffpos += 4;}//释放png内存png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

5.保存到文件

        FILE* fp;png_infop info_ptr;//png_colorpfopen_s(&fp, L"test.png", "wb");if (fp == NULL){//创建文件失败!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){return;}info_ptr = png_create_info_struct(png_ptr);if (info_ptr == NULL){fclose(fp);return;}//错误处理if (setjmp(png_jmpbuf(png_ptr))){fclose(fp);png_destroy_write_struct(&png_ptr, (png_infopp)NULL);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);

结语

代码如上,短小精悍。其中错误处理,以及读取的数据处理,就需要自己看情况修改了。

【pnglib】解析png格式的图像相关推荐

  1. MIPI RAW和YUV常见图像格式的解析、格式转换和看图软件

    设计初衷 在ISP的图像算法开发中,经常会涉及到YUV.RAW等格式的图像.例如,在YUV域,经常会涉及到I420.NV12和P010等数据格式之间的转换.在RAW域,又会经常涉及到MIPI RAW等 ...

  2. PCB javascript解析Gerber274X格式实现方法

    解析钻Gerber274X格式前首先得了解此格式,这样才能更好的解析呀. 一个Gerber274X里面包含的基本信息如下: 1.单位:公式mm,英制inch 2.省零方式:前省零,后省零 3.坐标方式 ...

  3. urlparse模块(专门用来解析URL格式)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #urlparse模块(专门用来解析URL格式)#URL格式: #protocol ://hostname[: ...

  4. python使用openCV加载图像、并将BGR格式转换成HSV格式、定义HSV格式中需要分离颜色的掩码(掩模)区间(mask)、并使用mask信息进行颜色分离、BGR格式的图像转化为RGB、并可视化

    python使用openCV加载图像.并将BGR格式转换成HSV格式.定义HSV格式中需要分离颜色的掩码(掩模)区间(mask).并使用mask信息进行颜色分离.将BGR格式的图像转化为RGB.可视化 ...

  5. php获得帮助类数据_PHP解析xml格式数据工具类示例

    本文实例讲述了PHP解析xml格式数据工具类.分享给大家供大家参考,具体如下: class ome_xml { /** * xml资源 * * @var resource * @see xml_par ...

  6. python中json模块_Python使用内置json模块解析json格式数据的方法

    本文实例讲述了Python使用内置json模块解析json格式数据的方法.分享给大家供大家参考,具体如下: Python中解析json字符串非常简单,直接用内置的json模块就可以,不需要安装额外的模 ...

  7. XML系列之--解析电文格式的XML(二)

    上一节介绍了XML的结构以及如何创建.讲到了XML可作为一种简单文本存储数据,把数据存储起来,以XML的方式进行传递.当接收到XML时,必不可少的就是对其进行解析,捞取有效数据,或者将第三方数据以节点 ...

  8. java jdom格式_Java全面解析XML格式串(JDOM解析)

    搜索热词 Java全面解析XML格式串(JDOM解析) import java.io.IOException; import java.io.StringReader; import java.uti ...

  9. QT解析 JSON 格式的数据

    QT解析 JSON 格式的数据 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.JSON 采用完全独立于语言的文本格式,这些特性使 JSON 成为理想的数 ...

最新文章

  1. java中json重复数据结构_JS实现去除数组中重复json的方法示例
  2. 2004-10-26+ 用户输入的安全问题
  3. 如何在CentOS 7上安装Apache
  4. 从“美屋”到“打扮家”:线下VR家居馆中的科技新体验
  5. jquery validate常用方法及注意问题
  6. 彻底删除 XP 自带的 Windows Messenger方法
  7. linux c之用fwrite和fread实现文件的复制
  8. 坚实原则:接口隔离原则
  9. 两个向量之间的夹角公式_向量的内积
  10. FFmpeg--命令详解
  11. 【spine】原理介绍和程序实现
  12. 搭建Mock Server实践(一)理论篇
  13. STL之vector去重三步曲(利用unique函数)
  14. 【WIN问题】微软Microsoft onenote/store 无法连接网络无法同步解决
  15. 最经典的10部爱情小说
  16. 体验与对比新版EBS gp3 vs gp2
  17. Springer-Verlag免费下载图书400本
  18. FPGA CDC跨时钟域设计学习(一)亚稳态
  19. 2015年6月9日晨_学习
  20. Mac电脑 zsh: command not found: vue

热门文章

  1. mint java_Linux Mint19安装jdk1.8.0.191过程
  2. l2的最优回归_【机器学习】逻辑回归(非常详细)
  3. 【小白学习C++ 教程】二、C++基础语法、注释和变量
  4. 六十五、SpringBoot配置拦截器拦截静态资源和区域解析器实现登陆功能
  5. java的死锁是什么意思_Java面试题:什么是死锁?如何手写一个死锁(Dead Lock)...
  6. 北京内推 | 微软亚洲研究院MSRA STCA招聘多模态算法实习生
  7. AAAI 2022 | 北航提出基于特征纯化的视线估计算法,让机器更好地“看见”
  8. 语音合成:模拟最像人类声音的系统
  9. 知识图谱实体链接:一份“由浅入深”的综述
  10. 线下沙龙 × 报名 | “大规模数据存储与挖掘”博士生研讨会