使用libpng直接在内存中对数据进行png编解码

由于工作需要,需要在内存中直接对数据进行png编码,然后再解码以测试其解码的速度,与现有的图像压缩方法进行比较。由于初次接触libpng,而网上这种直接在内存中操作的文章并不多,从头学习要花不少的时间。鉴于此,我借助第3方库:opencv库,来学习opencv是怎么在内存中对数据进行操作的(opencv的imread和imwrite PNG格式的数据,底层就是使用libpng操作的)。
opencv中的imread和imwrite方法就是对相应格式的数据的一种编解码,其源码在modules\imgcodecs\src中,找到grfmt_base.hpp
class BaseImageDecoder
{
public:
    BaseImageDecoder();
    virtual ~BaseImageDecoder() {}
 
    int width() const { return m_width; }
    int height() const { return m_height; }
    virtual int type() const { return m_type; }
 
     virtual bool setSource( const String& filename );\\设置解码文件路径
    virtual bool setSource( const Mat& buf );\\解码数据源,不设置的话,将使用的是解码指定文件路径的文件
    virtual int setScale( const int& scale_denom );
    virtual bool readHeader() = 0;
    virtual bool readData( Mat& img ) = 0;
 
    /// Called after readData to advance to the next page, if any.
    virtual bool nextPage() { return false; }
 
    virtual size_t signatureLength() const;
    virtual bool checkSignature( const String& signature ) const;
    virtual ImageDecoder newDecoder() const;
 
protected:
    int  m_width;  // width  of the image ( filled by readHeader )
    int  m_height; // height of the image ( filled by readHeader )
    int  m_type;
    int  m_scale_denom;
    String m_filename;
    String m_signature;
    Mat m_buf;
    bool m_buf_supported;
};
 
 
/ base class for encoders
class BaseImageEncoder
{
public:
    BaseImageEncoder();
    virtual ~BaseImageEncoder() {}
    virtual bool isFormatSupported( int depth ) const;
 
     virtual bool setDestination( const String& filename );\\设置输出接口为文件,将文件名告诉编码器
    virtual bool setDestination( std::vector<uchar>& buf );\\这个函数实际上是将buf交给m_buf,是编码后的数据存放的地方,如果不设置m_buf,那么默认将是IO输出为文件
    virtual bool write( const Mat& img, const std::vector<int>& params ) = 0;\\编码
    virtual bool writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params);
 
    virtual String getDescription() const;
    virtual ImageEncoder newEncoder() const;
 
    virtual void throwOnEror() const;
 
protected:
    String m_description;
 
    String m_filename;
    std::vector<uchar>* m_buf; \\保存编码后的数据,可变数组
    bool m_buf_supported;
 
    String m_last_error;
};
从父类的方法能看出Png子类应该有两种方法:一个是来源于文件,另一个就是buffer了,其编解码器中均使用 m_buf来表示这个buffer。
找到文件grfmt_png.hpp
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 structure
    void* m_info_ptr; // pointer to image information structure
    void* m_end_info; // pointer to one more image information structure
    FILE* 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);
};
 
而看到这些之后,基本就能看懂grfmt_png.cpp里是怎么分装libpng了,那么,就使用粘贴复制大法,完成自己的一个基于内存的libpng编解码的类吧。
 
一下是修改的内存操作png的hpp和cpp的代码如下:
 
#pragma once
#include <png.h>
#include <string>
#include <opencv2/core/core.hpp>

bool isBigEndian()
{
	unsigned short usData = 0x1122;
	unsigned char *pucData = (unsigned char*)&usData;
	return (*pucData == 0x22);
}

enum  	ImwritePNGFlags_zc {
	IMWRITE_PNG_STRATEGY_DEFAULT = 0,
	IMWRITE_PNG_STRATEGY_FILTERED = 1,
	IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY = 2,
	IMWRITE_PNG_STRATEGY_RLE = 3,
	IMWRITE_PNG_STRATEGY_FIXED = 4
};

enum  ImwriteFlags_zc {
	IMWRITE_PNG_COMPRESSION = 16,
	IMWRITE_PNG_STRATEGY = 17,
	IMWRITE_PNG_BILEVEL = 18
};

class PngImageDecoder
{
public:
	PngImageDecoder();
	~PngImageDecoder();

	int width() const { return m_width; }
	int height() const { return m_height; }

	bool setSource(const std::string& filename);
	bool setSource(const cv::Mat& buf);
	int setScale(const int& scale_denom);
	bool readHeader();
	bool readData(cv::Mat& img);
	void  close();

	//virtual size_t signatureLength() const;
	//virtual bool checkSignature(const String& signature) const;
	//PngImageDecoder newDecoder() const;

protected:
	int  m_width;  // width  of the image ( filled by readHeader )
	int  m_height; // height of the image ( filled by readHeader )
	int  m_type;
	int  m_scale_denom;
	std::string m_filename;
	//String m_signature;
	cv::Mat m_buf;

	static void readDataFromBuf(void* png_ptr, uchar* dst, size_t size);
	int   m_bit_depth;
	void* m_png_ptr;  // pointer to decompression structure
	void* m_info_ptr; // pointer to image information structure
	void* m_end_info; // pointer to one more image information structure
	FILE* m_f;
	int   m_color_type;
	size_t m_buf_pos;
};


/ base class for encoders 
class PngImageEncoder
{
public:
	PngImageEncoder();
	~PngImageEncoder() {}
	//bool isFormatSupported(int depth) const;

	bool setDestination(const std::string& filename);
	bool setDestination(std::vector<uchar>& buf);
	bool write(const cv::Mat& img, const std::vector<int>& params);
	//virtual bool writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params);

	//virtual String getDescription() const;
	//PngImageEncoder newEncoder() const;

	//virtual void throwOnEror() const;

protected:
	//String m_description;
	static void writeDataToBuf(void* png_ptr, uchar* src, size_t size);
	static void flushBuf(void* png_ptr);
	std::string m_filename;
	std::vector<uchar>* m_buf;
	//bool m_buf_supported;

	//String m_last_error;
};
#include "stdafx.h"
#include "pngCoder.h"
#define Z_NO_COMPRESSION         0
#define Z_BEST_SPEED             1
#define Z_BEST_COMPRESSION       9
#define Z_DEFAULT_COMPRESSION  (-1)

#define Z_FILTERED            1
#define Z_HUFFMAN_ONLY        2
#define Z_RLE                 3
#define Z_FIXED               4
#define Z_DEFAULT_STRATEGY    0
//#include <zlib.h>

PngImageEncoder::PngImageEncoder()
{
    m_buf = 0;
}

bool PngImageEncoder::setDestination(const std::string& filename)
{
    m_filename = filename;
    m_buf = 0;
    return true;
}

bool PngImageEncoder::setDestination(std::vector<uchar>& buf)
{
    m_buf = &buf;
    m_buf->clear();
    m_filename = std::string();
    return true;
}

void PngImageEncoder::writeDataToBuf(void* _png_ptr, uchar* src, size_t size)
{
    if (size == 0)
        return;
    png_structp png_ptr = reinterpret_cast<png_structp>(_png_ptr);
    PngImageEncoder* encoder = (PngImageEncoder*)(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 PngImageEncoder::flushBuf(void*)
{
}

bool PngImageEncoder::write(const cv::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;
    cv::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 valid
                int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
                bool 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 strategy
                        compression_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
                    {
                        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;

                    unsigned char** ptr = buffer.operator uchar **();
                    //png_write_image(png_ptr, buffer.data());
                    png_write_image(png_ptr, ptr);
                    png_write_end(png_ptr, info_ptr);

                    result = true;
                }
            }
        }
    }

    png_destroy_write_struct(&png_ptr, &info_ptr);
    if (f) fclose((FILE*)f);

    return result;
}

//------------------decoder---------------
PngImageDecoder::PngImageDecoder()
{
    //m_signature = "\x89\x50\x4e\x47\xd\xa\x1a\xa";
    m_color_type = 0;
    m_png_ptr = 0;
    m_info_ptr = m_end_info = 0;
    m_f = 0;
    //m_buf_supported = true;
    m_buf_pos = 0;
    m_bit_depth = 0;
}

PngImageDecoder::~PngImageDecoder() 
{
    close();
}

bool PngImageDecoder::setSource(const std::string& filename)
{
    m_filename = filename;
    m_buf.release();
    return true;
}

bool PngImageDecoder::setSource(const cv::Mat& buf)
{
    m_filename = std::string();
    m_buf = buf;
    return true;
}

int PngImageDecoder::setScale(const int& scale_denom)
{
    int temp = m_scale_denom;
    m_scale_denom = scale_denom;
    return temp;
}

void  PngImageDecoder::readDataFromBuf(void* _png_ptr, uchar* dst, size_t size)
{
    png_structp png_ptr = (png_structp)_png_ptr;
    PngImageDecoder* decoder = (PngImageDecoder*)(png_get_io_ptr(png_ptr));
    //CV_Assert(decoder);
    const cv::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;
}

void  PngImageDecoder::close()
{
    if (m_f)
    {
        fclose(m_f);
        m_f = 0;
    }

    if (m_png_ptr)
    {
        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;
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        m_png_ptr = m_info_ptr = m_end_info = 0;
    }
}

bool PngImageDecoder::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;
}


bool  PngImageDecoder::readData(cv::Mat& img)
{
    volatile bool result = false;
    cv::AutoBuffer<uchar*> _buffer(m_height);
    uchar** buffer = _buffer.operator uchar **();
    bool 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)
            {
         
                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);
    #endif

            if ((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;
}

 

 
 
 

转载于:https://www.cnblogs.com/XYZYY/p/10065783.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值