Opencv学习笔记 - imread源码解读

一、打开图片流程分析

1、读取图片头,进行解码器的寻找

2、根据参数flags,确定图像通道和是否缩放

3、给解码器指定缩放参数和源

4、使用解码器读取图像的头,确保没有问题,失败则输出错误并返回

5、确定图像的宽高,并判断是否超出处理范围。

6、通过解码器获取图像类型

7、根据宽、高、图像类型创建mat

8、通过解码器把图像数据读取到mat中

9、判断是否需要调整大小

二、imread源码查看

源码位置:opencv\modules\imgcodecs\src\loadsave.cpp

入口方法:

/**
 * Read an image
 *
 *  This function merely calls the actual implementation above and returns itself.
 *
 * @param[in] filename File to load
 * @param[in] flags Flags you wish to set.
*/
Mat imread( const String& filename, int flags )
{
    //用于应用程序性能分析 参见https://github.com/opencv/opencv/wiki/Profiling-OpenCV-Applications
    CV_TRACE_FUNCTION();

    /// create the basic container
    Mat img;

    /// load the data
    imread_( filename, flags, img );

    /// optionally rotate the data if EXIF' orientation flag says so
    if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
    {
        ApplyExifOrientation(filename, img);
    }

    /// return a reference to the data
    return img;
}

主要方法:

/**
 * Read an image into memory and return the information
 *
 * @param[in] filename File to load
 * @param[in] flags Flags
 * @param[in] hdrtype { LOAD_CVMAT=0,
 *                      LOAD_IMAGE=1,
 *                      LOAD_MAT=2
 *                    }
 * @param[in] mat Reference to C++ Mat object (If LOAD_MAT)
 *
*/
static bool
imread_( const String& filename, int flags, Mat& mat )
{
    /// Search for the relevant decoder to handle the imagery
    ImageDecoder decoder;

#ifdef HAVE_GDAL
    if(flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL ){
        decoder = GdalDecoder().newDecoder();
    }else{
#endif
        decoder = findDecoder( filename );
#ifdef HAVE_GDAL
    }
#endif

    /// if no decoder was found, return nothing.
    if( !decoder ){
        return 0;
    }

    int scale_denom = 1;
    if( flags > IMREAD_LOAD_GDAL )
    {
        if( flags & IMREAD_REDUCED_GRAYSCALE_2 )
            scale_denom = 2;
        else if( flags & IMREAD_REDUCED_GRAYSCALE_4 )
            scale_denom = 4;
        else if( flags & IMREAD_REDUCED_GRAYSCALE_8 )
            scale_denom = 8;
    }

    /// set the scale_denom in the driver
    decoder->setScale( scale_denom );

    /// set the filename in the driver
    decoder->setSource( filename );

    try
    {
        // read the header to make sure it succeeds
        if( !decoder->readHeader() )
            return 0;
    }
    catch (const cv::Exception& e)
    {
        std::cerr << "imread_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush;
        return 0;
    }
    catch (...)
    {
        std::cerr << "imread_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush;
        return 0;
    }


    // established the required input image size
    Size size = validateInputImageSize(Size(decoder->width(), decoder->height()));

    // grab the decoded type
    int type = decoder->type();
    if( (flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED )
    {
        if( (flags & IMREAD_ANYDEPTH) == 0 )
            type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));

        if( (flags & IMREAD_COLOR) != 0 ||
           ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) )
            type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);
        else
            type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1);
    }

    mat.create( size.height, size.width, type );

    // read the image data
    bool success = false;
    try
    {
        if (decoder->readData(mat))
            success = true;
    }
    catch (const cv::Exception& e)
    {
        std::cerr << "imread_('" << filename << "'): can't read data: " << e.what() << std::endl << std::flush;
    }
    catch (...)
    {
        std::cerr << "imread_('" << filename << "'): can't read data: unknown exception" << std::endl << std::flush;
    }
    if (!success)
    {
        mat.release();
        return false;
    }

    if( decoder->setScale( scale_denom ) > 1 ) // if decoder is JpegDecoder then decoder->setScale always returns 1
    {
        resize( mat, mat, Size( size.width / scale_denom, size.height / scale_denom ), 0, 0, INTER_LINEAR_EXACT);
    }

    return true;
}

获取解码器:

下面是png解码器片段,可以看出m_signature就是png图片的标识

PngDecoder::PngDecoder()
{
    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;
}

下面是校验的方法

bool BaseImageDecoder::checkSignature( const String& signature ) const
{
    size_t len = signatureLength();
    return signature.size() >= len && memcmp( signature.c_str(), m_signature.c_str(), len ) == 0;
}

 获取解码器,从文件头部读取标识,和所有解码器比较

static ImageDecoder findDecoder( const String& filename ) {

    size_t i, maxlen = 0;

    /// iterate through list of registered codecs
    for( i = 0; i < codecs.decoders.size(); i++ )
    {
        size_t len = codecs.decoders[i]->signatureLength();
        maxlen = std::max(maxlen, len);
    }

    /// Open the file
    FILE* f= fopen( filename.c_str(), "rb" );

    /// in the event of a failure, return an empty image decoder
    if( !f )
        return ImageDecoder();

    // read the file signature
    String signature(maxlen, ' ');
    maxlen = fread( (void*)signature.c_str(), 1, maxlen, f );
    fclose(f);
    signature = signature.substr(0, maxlen);

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

    /// If no decoder was found, return base type
    return ImageDecoder();
}

 校验图像宽高:这里可以看出宽高以及宽*高都有限制

static const size_t CV_IO_MAX_IMAGE_PARAMS = cv::utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PARAMS", 50);
static const size_t CV_IO_MAX_IMAGE_WIDTH = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_WIDTH", 1 << 20);
static const size_t CV_IO_MAX_IMAGE_HEIGHT = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_HEIGHT", 1 << 20);
static const size_t CV_IO_MAX_IMAGE_PIXELS = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PIXELS", 1 << 30);

static Size validateInputImageSize(const Size& size)
{
    CV_Assert(size.width > 0);
    CV_Assert(static_cast<size_t>(size.width) <= CV_IO_MAX_IMAGE_WIDTH);
    CV_Assert(size.height > 0);
    CV_Assert(static_cast<size_t>(size.height) <= CV_IO_MAX_IMAGE_HEIGHT);
    uint64 pixels = (uint64)size.width * (uint64)size.height;
    CV_Assert(pixels <= CV_IO_MAX_IMAGE_PIXELS);
    return size;
}

三、常见几种格式图片文件头标识

png -> 89 50 4e 47 0d 0a 1a 0a

jpg -> FF D8 FF

bmp -> BM  /  42 4D

四、imread函数支持读取的图像格式

  • Windows bitmaps - *.bmp, *.dib (always supported)
  • JPEG files - *.jpeg, *.jpg, *.jpe (see the Note section)
  • JPEG 2000 files - *.jp2 (see the Note section)
  • Portable Network Graphics - *.png (see the Note section)
  • WebP - *.webp (see the Note section)
  • Portable image format - *.pbm, *.pgm, *.ppm *.pxm, *.pnm (always supported)
  • PFM files - *.pfm (see the Note section)
  • Sun rasters - *.sr, *.ras (always supported)
  • TIFF files - *.tiff, *.tif (see the Note section)
  • OpenEXR Image files - *.exr (see the Note section)
  • Radiance HDR - *.hdr, *.pic (always supported)
  • Raster and Vector geospatial data supported by GDAL (see the Note section)

五、flags参数

c++定义 python定义 说明
IMREAD_UNCHANGED Python: cv.IMREAD_UNCHANGED 如果设置,则按原样返回加载的图像(使用Alpha通道,否则会被裁剪)
IMREAD_GRAYSCALE Python: cv.IMREAD_GRAYSCALE 如果设置,则始终将图像转换为单通道灰度图像(编解码器内部转换)。
IMREAD_COLOR Python: cv.IMREAD_COLOR 如果设置,请始终将图像转换为3通道BGR彩色图像。
IMREAD_ANYDEPTH Python: cv.IMREAD_ANYDEPTH 如果设置,则在输入具有相应深度时返回16位/ 32位图像,否则将其转换为8位。
IMREAD_ANYCOLOR Python: cv.IMREAD_ANYCOLOR 如果设置,则以任何可能的颜色格式读取图像。
IMREAD_LOAD_GDAL Python: cv.IMREAD_LOAD_GDAL 如果设置,使用gdal驱动程序加载图像
IMREAD_REDUCED_GRAYSCALE_2 Python: cv.IMREAD_REDUCED_GRAYSCALE_2 如果设置,则始终将图像转换为单通道灰度图像,图像尺寸减小1/2。
IMREAD_REDUCED_COLOR_2 Python: cv.IMREAD_REDUCED_COLOR_2 如果设置,则始终将图像转换为3通道BGR彩色图像,图像尺寸减小1/2。
IMREAD_REDUCED_GRAYSCALE_4 Python: cv.IMREAD_REDUCED_GRAYSCALE_4 如果设置,则始终将图像转换为单通道灰度图像,图像尺寸减小1/4
IMREAD_REDUCED_COLOR_4 Python: cv.IMREAD_REDUCED_COLOR_4 如果设置,则始终将图像转换为3通道BGR彩色图像,图像尺寸减小1/4
IMREAD_REDUCED_GRAYSCALE_8 Python: cv.IMREAD_REDUCED_GRAYSCALE_8 如果设置,则始终将图像转换为单通道灰度图像,图像尺寸减小1/8。
IMREAD_REDUCED_COLOR_8 Python: cv.IMREAD_REDUCED_COLOR_8 如果设置,则始终将图像转换为3通道BGR彩色图像,图像尺寸减小1/8。
IMREAD_IGNORE_ORIENTATION Python: cv.IMREAD_IGNORE_ORIENTATION 如果设置,请不要根据EXIF的方向标志旋转图像。

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/110078253