imread探究
imread是opencv用于图片读取的一个函数
Mat imread( const String& filename, int flags )
Mat imread( const String& filename, int flags )
{
CV_TRACE_FUNCTION();
/// create the basic container
Mat img;
/// load the data
imread_( filename, flags, LOAD_MAT, &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;
}
对imread的解读可以参见博文
【opencv】opencv源码分析(一):imread、cvLoadImage、waitKey、imshow函数
通过对比可以发现对比多了一个对EXIF旋转信息参数的读取,并且对图片进行一个旋转操作
if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
{
ApplyExifOrientation(filename, img);
}
imread_
static void*
imread_( const String& filename, int flags, int hdrtype, Mat* mat=0 )
{
IplImage* image = 0;
CvMat *matrix = 0;
Mat temp, *data = &temp;
/// 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 ); //解读1
#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 );
CV_TRY
{
// read the header to make sure it succeeds
if( !decoder->readHeader() )
return 0;
}
CV_CATCH (cv::Exception, e)
{
std::cerr << "imread_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush;
return 0;
}
CV_CATCH_ALL
{
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();
//type是通过readHeader函数读取出来的
if( (flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED )
{
if( (flags & CV_LOAD_IMAGE_ANYDEPTH) == 0 )
type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));
if( (flags & CV_LOAD_IMAGE_COLOR) != 0 ||
((flags & CV_LOAD_IMAGE_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) )
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);
else
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1);
}
if( hdrtype == LOAD_CVMAT || hdrtype == LOAD_MAT )
{
if( hdrtype == LOAD_CVMAT )
{
matrix = cvCreateMat( size.height, size.width, type );
temp = cvarrToMat( matrix );
}
else
{
mat->create( size.height, size.width, type );
data = mat;
}
}
else
{
image = cvCreateImage( size, cvIplDepth(type), CV_MAT_CN(type) );
temp = cvarrToMat( image );
}
// read the image data
bool success = false;
CV_TRY
{
if (decoder->readData(*data))
success = true;
}
CV_CATCH (cv::Exception, e)
{
std::cerr << "imread_('" << filename << "'): can't read data: " << e.what() << std::endl << std::flush;
}
CV_CATCH_ALL
{
std::cerr << "imread_('" << filename << "'): can't read data: unknown exception" << std::endl << std::flush;
}
if (!success)
{
cvReleaseImage( &image );
cvReleaseMat( &matrix );
if( mat )
mat->release();
return 0;
}
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 hdrtype == LOAD_CVMAT ? (void*)matrix :
hdrtype == LOAD_IMAGE ? (void*)image : (void*)mat;
}
解读1
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 );
//通过maxlen提取signature
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();
//通过比较signature来判断解码器
}
/// If no decoder was found, return base type
return ImageDecoder();
}
struct ImageCodecInitializer
{
/**
* Default Constructor for the ImageCodeInitializer
*/
ImageCodecInitializer()
{
/// BMP Support
decoders.push_back( makePtr<BmpDecoder>() );
encoders.push_back( makePtr<BmpEncoder>() );
decoders.push_back( makePtr<HdrDecoder>() );
encoders.push_back( makePtr<HdrEncoder>() );
#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
decoders.push_back( makePtr<SunRasterDecoder>() );
encoders.push_back( makePtr<SunRasterEncoder>() );
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) );
#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*/
decoders.push_back( makePtr<PAMDecoder>() );
encoders.push_back( makePtr<PAMEncoder>() );
}
std::vector<ImageDecoder> decoders;
std::vector<ImageEncoder> encoders;
};
static ImageCodecInitializer codecs;
codecs是个静态变量,其构造函数将不同的解码器指针压到vector中
typedef Ptr< BaseImageDecoder> ImageDecoder;
ImageDecoder是一个虚基类指针
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;
};
按照上面的参考博文仍然以JpegDecoder为例
首先是获取文件的signature
m_signature = "\xFF\xD8\xFF";//Jpeg图片的signature
bool checkSignature(const String& signature) const
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;
}
memcm函数说明
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
比较指针指向地址中的num字节
与strcmp函数不同的是,该函数遇到’\0’字符不会停止比较
返回值说明
返回值 | 说明 |
---|---|
<0 | ptr1指向地址字节的ASCII码小于ptr2 |
=0 | ptr1与ptr2指向地址字节的ASCII吗相同 |
0 |
ptr1>ptr2 |
opencv3源码比较与opencv2有有改动但是大体流程和上述参考博文中总结的是一致的
输入filename—>解析图片—>确定译码器—>译码函数进行信息、数据的读取—>存放于Mat容器—>返回
imwrite探究
imwrite函数定义
bool imwrite( const String& filename, InputArray _img,
const std::vector<int>& params )
{
CV_TRACE_FUNCTION();
std::vector<Mat> img_vec;
//Did we get a Mat or a vector of Mats?
if (_img.isMat())
img_vec.push_back(_img.getMat());
else if (_img.isMatVector())
_img.getMatVector(img_vec);
return imwrite_(filename, img_vec, params, false);
}
InpuArray 通过成员变量flags判断类型,输入是单个Ma还是vector< Mat >
将img存储到vecto< Mat >中然后调用 imwrite 进行存储
imwrite_
static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
const std::vector<int>& params, bool flipv )
{
bool isMultiImg = img_vec.size() > 1; //判断是否是多个Mat变量
std::vector<Mat> write_vec;
ImageEncoder encoder = findEncoder( filename ); //查找解码器->解读1
if( !encoder )
CV_Error( CV_StsError, "could not find a writer for the specified extension" );
for (size_t page = 0; page < img_vec.size(); page++)
{
Mat image = img_vec[page];
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 );
//说明只能存储1/3/4通道的图片
Mat temp;
if( !encoder->isFormatSupported(image.depth()) )
{
CV_Assert( encoder->isFormatSupported(CV_8U) );
image.convertTo( temp, CV_8U );
image = temp;
}
if( flipv )//翻转
{
flip(image, temp, 0);
image = temp;
}
write_vec.push_back(image);
}
encoder->setDestination( filename );
CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2);
bool code;
if (!isMultiImg)
code = encoder->write( write_vec[0], params );
else
code = encoder->writemulti( write_vec, params ); //to be implemented
// CV_Assert( code );
return code;
}
解读1
以JpegEncoder为例
static ImageEncoder findEncoder( const String& _ext )
{
if( _ext.size() <= 1 )
return ImageEncoder();
const char* ext = strrchr( _ext.c_str(), '.' );
if( !ext )
return ImageEncoder();
int len = 0;
for( ext++; len < 128 && isalnum(ext[len]); len++ )
;
for( size_t i = 0; i < codecs.encoders.size(); i++ )
{
String description = codecs.encoders[i]->getDescription();
const char* descr = strchr( description.c_str(), '(' );
while( descr )
{
descr = strchr( descr + 1, '.' );
if( !descr )
break;
int j = 0;
for( descr++; j < len && isalnum(descr[j]) ; j++ )
{
int c1 = tolower(ext[j]);
int c2 = tolower(descr[j]);
if( c1 != c2 )
break;
}
if( j == len && !isalnum(descr[j]))
return codecs.encoders[i]->newEncoder();
descr += j;
}
}
return ImageEncoder();
}
- 获取当前文件名的后缀名
- 将当前文件的后缀名与解码器中的m_description(getDescription)(JpegEncoder->m_description = “JPEG files (.jpeg;.jpg;*.jpe)”)成员变量进行对比
- 对比一致返回JpegEncoder
总结一下imwrite存取图片的流程
传入文件名->获取解码器->判断图片通道数->使用解码器存储图片