一、打开图片流程分析
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的方向标志旋转图像。 |