在OpenCV源码剖析之ImageDecoder 中,ImageCodecInitializer注册了众多图像类型,这一节我们将讲解PNG的编解码。由于这部分比较简单,只将简单看下代码。PNG 解码是从BaseImageDecoder继承而来,PNG 编码则是从BaseImageEncoder继承而来。在grfmt_png.hpp中可以查看他们的定义:

namespace cv
{class PngDecoder CV_FINAL : public BaseImageDecoder
{
public:PngDecoder();virtual ~PngDecoder();bool  readData( Mat& img ) CV_OVERRIDE;bool  readHeader() CV_OVERRIDE;void  close();ImageDecoder newDecoder() const CV_OVERRIDE;protected:static void readDataFromBuf(void* png_ptr, uchar* dst, size_t size);int   m_bit_depth;void* m_png_ptr;  // pointer to decompression structurevoid* m_info_ptr; // pointer to image information structurevoid* m_end_info; // pointer to one more image information structureFILE* m_f;int   m_color_type;size_t m_buf_pos;
};class PngEncoder CV_FINAL : public BaseImageEncoder
{
public:PngEncoder();virtual ~PngEncoder();bool  isFormatSupported( int depth ) const CV_OVERRIDE;bool  write( const Mat& img, const std::vector<int>& params ) CV_OVERRIDE;ImageEncoder newEncoder() const CV_OVERRIDE;protected:static void writeDataToBuf(void* png_ptr, uchar* src, size_t size);static void flushBuf(void* png_ptr);
};}

对于解码主要就是readHeader和readData这两个函数了,其中readHeader实现如下:

bool PngDecoder::readHeader()
{volatile bool result = false;close();png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);if (png_ptr){png_infop info_ptr = png_create_info_struct(png_ptr);png_infop end_info = png_create_info_struct(png_ptr);m_png_ptr  = png_ptr;m_info_ptr = info_ptr;m_end_info = end_info;m_buf_pos  = 0;if (info_ptr && end_info){if (setjmp(png_jmpbuf(png_ptr)) == 0){if (!m_buf.empty()){png_set_read_fn(png_ptr, this, (png_rw_ptr)readDataFromBuf);}else{m_f = fopen(m_filename.c_str(), "rb");if (m_f){png_init_io(png_ptr, m_f);}}if (!m_buf.empty() || m_f){png_uint_32   wdth, hght;int           bit_depth, color_type, num_trans = 0;png_bytep     trans;png_color_16p trans_values;png_read_info(png_ptr, info_ptr);png_get_IHDR(png_ptr, info_ptr, &wdth, &hght,&bit_depth, &color_type, 0, 0, 0);m_width      = (int)wdth;m_height     = (int)hght;m_color_type = color_type;m_bit_depth  = bit_depth;if ((bit_depth <= 8) || (bit_depth == 16)){switch (color_type){case PNG_COLOR_TYPE_RGB:case PNG_COLOR_TYPE_PALETTE:png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);if (num_trans > 0){m_type = CV_8UC4;}else{m_type = CV_8UC3;}break;case PNG_COLOR_TYPE_GRAY_ALPHA:case PNG_COLOR_TYPE_RGB_ALPHA:m_type = CV_8UC4;break;default:m_type = CV_8UC1;}if (bit_depth == 16){m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));}result = true;}}}}}if (!result){close();}return result;
}

从代码中可以看出,图片数据有2种来源DataFromBuf和文件,DataFromBuf实现通过png_set_read_fn指定,其处理函数实现如下所示:

void PngDecoder::readDataFromBuf(void *_png_ptr, uchar *dst, size_t size)
{png_structp png_ptr  = (png_structp)_png_ptr;PngDecoder  *decoder = (PngDecoder *)(png_get_io_ptr(png_ptr));CV_Assert(decoder);const Mat& buf = decoder->m_buf;if (decoder->m_buf_pos + size > buf.cols * buf.rows * buf.elemSize()){png_error(png_ptr, "PNG input buffer is incomplete");return;}memcpy(dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size);decoder->m_buf_pos += size;
}

可以看出这个函数最主要的实现就是memcpy(dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size);它是将数据从decoder->m_buf拷贝到dst buf中去,decoder->m_buf就是基类中setSource 2种实现之一的来自内存了,其类型为Mat。

在指定数据源后,通过png_read_info接口读取png chunk信息,再调用png_get_IHDR获取png文件头数据块信息,从未获取图片的宽高、颜色类型、位深信息。再根据位深和颜色类型得到对应的Mat 数据类型。

获取到图片的基本信息后,接下来就是读取数据,其实现为

bool PngDecoder::readData(Mat& img)
{volatile bool result = false;AutoBuffer<uchar *> _buffer(m_height);uchar               **buffer = _buffer;int                 color    = img.channels() > 1;png_structp png_ptr  = (png_structp)m_png_ptr;png_infop   info_ptr = (png_infop)m_info_ptr;png_infop   end_info = (png_infop)m_end_info;if (m_png_ptr && m_info_ptr && m_end_info && m_width && m_height){if (setjmp(png_jmpbuf(png_ptr)) == 0){int y;if ((img.depth() == CV_8U) && (m_bit_depth == 16)){png_set_strip_16(png_ptr);}else if (!isBigEndian()){png_set_swap(png_ptr);}if (img.channels() < 4){/* observation: png_read_image() writes 400 bytes beyond* end of data when reading a 400x118 color png* "mpplus_sand.png".  OpenCV crashes even with demo* programs.  Looking at the loaded image I'd say we get 4* bytes per pixel instead of 3 bytes per pixel.  Test* indicate that it is a good idea to always ask for* stripping alpha..  18.11.2004 Axel Walthelm*/png_set_strip_alpha(png_ptr);}else{png_set_tRNS_to_alpha(png_ptr);}if (m_color_type == PNG_COLOR_TYPE_PALETTE){png_set_palette_to_rgb(png_ptr);}if (((m_color_type & PNG_COLOR_MASK_COLOR) == 0) && (m_bit_depth < 8))
#if (PNG_LIBPNG_VER_MAJOR * 10000 + PNG_LIBPNG_VER_MINOR * 100 + PNG_LIBPNG_VER_RELEASE >= 10209) || \(PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR == 0 && PNG_LIBPNG_VER_RELEASE >= 18){png_set_expand_gray_1_2_4_to_8(png_ptr);}
#else{png_set_gray_1_2_4_to_8(png_ptr);}
#endifif ((m_color_type & PNG_COLOR_MASK_COLOR) && color){png_set_bgr(png_ptr);   // convert RGB to BGR}else if (color){png_set_gray_to_rgb(png_ptr);   // Gray->RGB}else{png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);   // RGB->Gray}png_set_interlace_handling(png_ptr);png_read_update_info(png_ptr, info_ptr);for (y = 0; y < m_height; y++){buffer[y] = img.data + y * img.step;}png_read_image(png_ptr, buffer);png_read_end(png_ptr, end_info);result = true;}}close();return result;
}

这里有点需要注意的是PNG图片存储是按大端顺序存储数据的,因此在处理前需要判断系统测存储模式。
         PNG编码和解码处理差不多,这里将编码代码附上:

/// PngEncoder ///
PngEncoder::PngEncoder()
{m_description   = "Portable Network Graphics files (*.png)";m_buf_supported = true;
}PngEncoder::~PngEncoder()
{
}bool PngEncoder::isFormatSupported(int depth) const
{return depth == CV_8U || depth == CV_16U;
}ImageEncoder PngEncoder::newEncoder() const
{return makePtr<PngEncoder>();
}void PngEncoder::writeDataToBuf(void *_png_ptr, uchar *src, size_t size)
{if (size == 0){return;}png_structp png_ptr  = (png_structp)_png_ptr;PngEncoder  *encoder = (PngEncoder *)(png_get_io_ptr(png_ptr));CV_Assert(encoder && encoder->m_buf);size_t cursz = encoder->m_buf->size();encoder->m_buf->resize(cursz + size);memcpy(&(*encoder->m_buf)[cursz], src, size);
}void PngEncoder::flushBuf(void *)
{
}bool PngEncoder::write(const Mat& img, const std::vector<int>& params)
{png_structp    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);png_infop      info_ptr = 0;FILE *volatile f = 0;int            y, width = img.cols, height = img.rows;int            depth = img.depth(), channels = img.channels();volatile bool  result = false;AutoBuffer<uchar *> buffer;if ((depth != CV_8U) && (depth != CV_16U)){return false;}if (png_ptr){info_ptr = png_create_info_struct(png_ptr);if (info_ptr){if (setjmp(png_jmpbuf(png_ptr)) == 0){if (m_buf){png_set_write_fn(png_ptr, this,(png_rw_ptr)writeDataToBuf, (png_flush_ptr)flushBuf);}else{f = fopen(m_filename.c_str(), "wb");if (f){png_init_io(png_ptr, (png_FILE_p)f);}}int  compression_level    = -1;                       // Invalid value to allow setting 0-9 as validint  compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategybool isBilevel            = false;for (size_t i = 0; i < params.size(); i += 2){if (params[i] == IMWRITE_PNG_COMPRESSION){compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategycompression_level    = params[i + 1];compression_level    = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);}if (params[i] == IMWRITE_PNG_STRATEGY){compression_strategy = params[i + 1];compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);}if (params[i] == IMWRITE_PNG_BILEVEL){isBilevel = params[i + 1] != 0;}}if (m_buf || f){if (compression_level >= 0){png_set_compression_level(png_ptr, compression_level);}else{// tune parameters for speed// (see http://wiki.linuxquestions.org/wiki/Libpng)png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);png_set_compression_level(png_ptr, Z_BEST_SPEED);}png_set_compression_strategy(png_ptr, compression_strategy);png_set_IHDR(png_ptr, info_ptr, width, height,depth == CV_8U ? isBilevel ? 1 : 8 : 16,channels == 1 ? PNG_COLOR_TYPE_GRAY :channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);png_write_info(png_ptr, info_ptr);if (isBilevel){png_set_packing(png_ptr);}png_set_bgr(png_ptr);if (!isBigEndian()){png_set_swap(png_ptr);}buffer.allocate(height);for (y = 0; y < height; y++){buffer[y] = img.data + y * img.step;}png_write_image(png_ptr, buffer);png_write_end(png_ptr, info_ptr);result = true;}}}}png_destroy_write_struct(&png_ptr, &info_ptr);if (f){fclose((FILE *)f);}return result;

从上部分代码实现可以看出OpenCV的PNG支持实现还是挺简单的,主要还是调用了libpng接口实现。

OpenCV源码剖析之imread PNG相关推荐

  1. OpenCV源码剖析之imread JPEG

    在经历第一份工作的2年半后,有幸能够从新进入到图像处理这个领域来,与以前工作时只能空闲时间看看OpenCV源码.博客和了解OpenCV最新动态这种三天打鱼两天晒网的不同,这次自己可以专心扎进这里面来了 ...

  2. 【opencv源码剖析】背景建模mog2

    前言 opencv实现的背景建模方法有很多,早期的opencv版本modules/video/src下有acmmm2003(2.3.1版本).codebook(2.3.1版本).gmg(2.4.8版本 ...

  3. windows+vscode+opencv源码安装配置

    一.参考资料 VScode搭建OpenCV环境 OpenCV使用CMake和MinGW-w64的编译安装 win10下VSCode配置opencv4.4.0(超详细教程,亲测有效) VSCODE中配置 ...

  4. 修改并编译OpenCV源码提升霍夫变换线检测效果

    在做图像处理的时候,经常需要用到MATLAB验证与OpenCV实现共同进行,本文动手动机就是:OpenCV提供的Hough线检测不能满足我的要求,故需要对OpenCV源码进行修改.本人菜鸟,才学C++ ...

  5. 双目相机标定OpenCV源码讲解

    双目相机标定OpenCV源码讲解 背景介绍 所述内容 参考资料 摄像机标定部分代码 代码思路 代码中的其他函数 找角点&求内参 求外参 求矫正映射矩阵 后记 背景介绍 暑假接近两个月的时间做了 ...

  6. 老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 1...

    老李推荐:第14章4节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-端口转发 在初始化HierarchyViewer的实例过程中, ...

  7. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

  8. 一次搞定OpenCV源码及扩展模块的编译与环境配置

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|计算机视觉工坊 版本:VS2017.CMake3.12. ...

  9. Kafka源码剖析 —— 网络I/O篇 —— 浅析KafkaSelector

    为什么80%的码农都做不了架构师?>>>    ##NioSelector和KafkaSelector有什么区别? 先说结论,KafkaSelector(org.apache.kaf ...

  10. Mongoose源码剖析:Introduction and Installation

    引言 要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性.并去使用它.本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件.web服务器使用什么 ...

最新文章

  1. python英语单词大全-使用Python进行英文单词分割
  2. 机器视觉资料汇总(2)
  3. 【Spring注解系列07】Spring注入Bean有哪些方式总结
  4. Career Essentials Postgraduate Study: exploring your options 读完本科后干啥?
  5. db2查询字段备注_通过逐浪数据精灵管理sqlserver数据表备注以及字段说明
  6. ANSYS——查看某一截面的云图分布(也叫做切片图)
  7. 数据库连接池_DataSource_数据源(简单介绍C3P0和Druid)
  8. 比亚迪汽车发布品牌全新标识 取消了椭圆型边界
  9. 如何自学python知乎-你们都是怎么学 Python 的?
  10. PLC编程过程中需要注意的事项
  11. CAT1 4G+以太网开发板腾讯云手机微信小程序显示温度和下发控制
  12. 192.168.0.1路由器设置进入
  13. 共享文件夹----详细教程
  14. 假期读书|《老人与海》读后感
  15. C++中std::endl的作用
  16. RabbitMQ入门学习笔记
  17. 春节入境澳门旅客料增1成 景点拥挤须提高警惕
  18. KaLi Linux 安装sougou输入法
  19. 1213_SCons初步了解以及安装试用
  20. 去外包公司的伙伴们小心了!——亲身经历,数数外包公司的坑

热门文章

  1. CSS3(新增样式)
  2. mipi的dsi全称_MIPI-DSI/CSI协议介绍
  3. 埃默里大学又一华人科学家被要求搬离实验室,当事人称“这是报复”
  4. java.lang.IllegalStateException: Could not find method onClick(View) in a parent or ancestor Context
  5. 电脑怎么连接上苹果手机的热点
  6. oracle 18c,Oracle 18c
  7. linux怎么共享打印机驱动程序,Linux下使用局域网中windows 共享打印机
  8. python+selenium 定位元素踩过的坑
  9. 高端大气上档次!10个精美的国外HTML5网站欣赏
  10. Acer 4750 安装黑苹果_傻瓜式黑苹果安装神器