OpenCV源码剖析之ImageDecoder

       在上一篇 OpenCV源码剖析之imread JPEG  中讲解了怎么使用OpenCV imread接口读取JPEG图片,其中有一点当时只是说遍历注册了的解码器,找到与传入图片相对应的解码器进行解码,而对于解码器注册等相关内容没有去深入探讨,本次将讲解下这部分的内容---ImageDecoder。

       上章中我们提到在findDecoder函数里通过for循环遍历找到对应的解码器:

    for (i = 0; i < codecs.decoders.size(); i++)
    {
        if (codecs.decoders[i]->checkSignature(signature))
        {
            return codecs.decoders[i]->newDecoder();
        }
    }

    可以看到这里主要就是根据codecs这个全局变量展开处理的了,其定义就在findDecoder上面:static ImageCodecInitializer codecs;ImageCodecInitializer是一个结构体,其定义为

struct ImageCodecInitializer
{
    /**
     * Default Constructor for the ImageCodeInitializer
     */
    ImageCodecInitializer()
    {
        /// BMP Support
        decoders.push_back(makePtr<BmpDecoder>());
        encoders.push_back(makePtr<BmpEncoder>());

#ifdef HAVE_IMGCODEC_HDR
        decoders.push_back(makePtr<HdrDecoder>());
        encoders.push_back(makePtr<HdrEncoder>());
#endif
#ifdef HAVE_JPEG
        decoders.push_back(makePtr<JpegDecoder>());
        encoders.push_back(makePtr<JpegEncoder>());
#endif
#ifdef HAVE_WEBP
        decoders.push_back(makePtr<WebPDecoder>());
        encoders.push_back(makePtr<WebPEncoder>());
#endif
#ifdef HAVE_IMGCODEC_SUNRASTER
        decoders.push_back(makePtr<SunRasterDecoder>());
        encoders.push_back(makePtr<SunRasterEncoder>());
#endif
#ifdef HAVE_IMGCODEC_PXM
        decoders.push_back(makePtr<PxMDecoder>());
        encoders.push_back(makePtr<PxMEncoder>(PXM_TYPE_AUTO));
        encoders.push_back(makePtr<PxMEncoder>(PXM_TYPE_PBM));
        encoders.push_back(makePtr<PxMEncoder>(PXM_TYPE_PGM));
        encoders.push_back(makePtr<PxMEncoder>(PXM_TYPE_PPM));
        decoders.push_back(makePtr<PAMDecoder>());
        encoders.push_back(makePtr<PAMEncoder>());
#endif
#ifdef HAVE_TIFF
        decoders.push_back(makePtr<TiffDecoder>());
        encoders.push_back(makePtr<TiffEncoder>());
#endif
#ifdef HAVE_PNG
        decoders.push_back(makePtr<PngDecoder>());
        encoders.push_back(makePtr<PngEncoder>());
#endif
#ifdef HAVE_GDCM
        decoders.push_back(makePtr<DICOMDecoder>());
#endif
#ifdef HAVE_JASPER
        decoders.push_back(makePtr<Jpeg2KDecoder>());
        encoders.push_back(makePtr<Jpeg2KEncoder>());
#endif
#ifdef HAVE_OPENEXR
        decoders.push_back(makePtr<ExrDecoder>());
        encoders.push_back(makePtr<ExrEncoder>());
#endif

#ifdef HAVE_GDAL
        /// Attach the GDAL Decoder
        decoders.push_back(makePtr<GdalDecoder>());
#endif      /*HAVE_GDAL*/
    }

    std::vector<ImageDecoder> decoders;
    std::vector<ImageEncoder> encoders;
};

       该结构体定义了2个vector容器变量decoders和encoders,类型分别为ImageDecoder和ImageEncoder,并且在结构体里面实现了构造函数的初始化,主要就是根据宏定义将支持的编码、解码类型加入到decoders和encoders中,可以认为这就实现了对支持的编解码器的注册了。

      接下来就该看下 ImageDecoder和ImageEncoder这两个数据类型了。在grfmt_base.hpp中可以找到他们的定义:

namespace cv
{
class BaseImageDecoder;
class BaseImageEncoder;
typedef Ptr<BaseImageEncoder> ImageEncoder;
typedef Ptr<BaseImageDecoder> ImageDecoder;

///////////////////////////////// base class for decoders ////////////////////////
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 );
    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;
};

}

        可以看出在这里是定义了2个图像处理基类,其实现在grfmt_base.cpp中:

namespace cv
{

BaseImageDecoder::BaseImageDecoder()//默认构造函数
{
    m_width = m_height = 0;
    m_type = -1;
    m_buf_supported = false;
    m_scale_denom = 1;
}

bool BaseImageDecoder::setSource( const String& filename )//设置解码器的数据源--来自文件
{
    m_filename = filename;
    m_buf.release();
    return true;
}

bool BaseImageDecoder::setSource( const Mat& buf )//设置解码器的数据源--来自内存
{
    if( !m_buf_supported )
        return false;
    m_filename = String();
    m_buf = buf;
    return true;
}

size_t BaseImageDecoder::signatureLength() const//获取对应格式signature的大小
{
    return m_signature.size();
}

bool BaseImageDecoder::checkSignature( const String& signature ) const//传入的signature和解码器对应的signature是否一致
{
    size_t len = signatureLength();
    return signature.size() >= len && memcmp( signature.c_str(), m_signature.c_str(), len ) == 0;
}

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

ImageDecoder BaseImageDecoder::newDecoder() const
{
    return ImageDecoder();
}

BaseImageEncoder::BaseImageEncoder()
{
    m_buf = 0;
    m_buf_supported = false;
}

bool  BaseImageEncoder::isFormatSupported( int depth ) const
{
    return depth == CV_8U;
}

String BaseImageEncoder::getDescription() const
{
    return m_description;
}

bool BaseImageEncoder::setDestination( const String& filename )//设置编码目的--编码到文件
{
    m_filename = filename;
    m_buf = 0;
    return true;
}

bool BaseImageEncoder::setDestination( std::vector<uchar>& buf )//设置编码目的--编码到内存
{
    if( !m_buf_supported )
        return false;
    m_buf = &buf;
    m_buf->clear();
    m_filename = String();
    return true;
}

bool BaseImageEncoder::writemulti(const std::vector<Mat>&, const std::vector<int>& )
{
    return false;
}

ImageEncoder BaseImageEncoder::newEncoder() const
{
    return ImageEncoder();
}

void BaseImageEncoder::throwOnEror() const
{
    if(!m_last_error.empty())
    {
        String msg = "Raw image encoder error: " + m_last_error;
        CV_Error( CV_BadImageSize, msg.c_str() );
    }
}

}

        至此,关于OpenCV的ImageDecoders和ImageEncoders就讲解完了。下面在以JPEG为例分析解码过程。这里我们已经知道了有一个图像解码的基类BaseImageDecoder,下面看看OpenCV JPEG解码器JpegDecoder是如何处理的,其定义如下,可在grfmt_jpeg.hpp中找到。

namespace cv
{

class JpegDecoder CV_FINAL : public BaseImageDecoder
{
public:

    JpegDecoder();
    virtual ~JpegDecoder();

    bool  readData( Mat& img ) CV_OVERRIDE;
    bool  readHeader() CV_OVERRIDE;
    void  close();

    ImageDecoder newDecoder() const CV_OVERRIDE;

protected:

    FILE* m_f;
    void* m_state;

private:
    JpegDecoder(const JpegDecoder &); // copy disabled
    JpegDecoder& operator=(const JpegDecoder &); // assign disabled
};


class JpegEncoder CV_FINAL : public BaseImageEncoder
{
public:
    JpegEncoder();
    virtual ~JpegEncoder();

    bool  write( const Mat& img, const std::vector<int>& params ) CV_OVERRIDE;
    ImageEncoder newEncoder() const CV_OVERRIDE;
};

}

        可以看到,无论是JPEG encoder是从基类BaseImageEncoder中派生而来的,JpegDecoder是从BaseImageDecoder中派生而来,除了基类的一些实现外,派生类也有新的实现。这部分代码在OpenCV源码剖析之imread JPEG 中有该部分的分析,这里将不在展开讲述。

        至此我们对OpenCV的imread也有了一个清晰的认识,JpegDecoder类继承BaseImageDecoder,并且在JpegDecoder类的实现中调用libJpeg的API实现图片的解码,最终将解码的数据返回给Mat的ptr,而Mat ptr又和data相连,最终就是读入图片通过调用底层解码将解码的数据返回到Mat data中了。

猜你喜欢

转载自blog.csdn.net/xxboy61/article/details/80467220
今日推荐