在第一篇处,我们只是在最表层的上面操作函数,当别人问我们时,我们其实什么也不知道的。就知道,imread是读取函数了,然后掉用其它的函数的乐乐。当然,上面我们可以好好学习人家为什么要这样做了!这里,看一个函数finddecoder()。这个函数主要是获取decoder对象,从而决定读取什么样后缀名的图像(jpg,bmp等等)
声明:
ImageDecoder findDecoder( const string& filename );//在highgui/loadsave.cpp中
ImageDecoder findDecoder( const Mat& buf );
//这个是一个函数的重载了,在第一篇,即imread函数中调用的是第一个,这里就跟进第一个。
定义:
ImageDecoder findDecoder( const string& filename )
{
size_t i, maxlen = 0;
for( i = 0; i < decoders.size(); i++ )//这里第一个decoders是什么呢?在文件中有这样的一个定义:static vector<ImageDecoder> decoders;好家伙,原来是一个向量,这里第一问,为什么要是一个向量,而且还是全局静态变量,就是说整个程序运行期间它都存在。其只初始化一遍。
{
size_t len = decoders[i]->signatureLength();//这一个循环是寻找第一个数据点保存的数据:signature。从这里可以看出,其实后缀名在这里没有什么用处,文件本身是保存了这个类型值的。
maxlen = std::max(maxlen, len);//读取数据长度为一个最大的。
}
FILE* f= fopen( filename.c_str(), "rb" );//熟悉的c函数,读取文件。哈哈,从这个可以看到,所有的文件都可以有FILE来读取的。
if( !f )
return ImageDecoder();//没有读取成功,返回一个空的decoder。处理错误的能力。
string signature(maxlen, ' ');
maxlen = fread( &signature[0], 1, maxlen, f );//从文件中读取signature数据,这里是用了string,且是按字节读取。string底层用了什么结构呢?string[0]返回的是一个什么值呢?这有待查询。
/*这里是一个试验:
int maxlen = 10 ;
string sig(maxlen,' ');
cout<<&sig<<endl;
cout<<((int*)&sig[0])<<endl;
cout<<((int*)&sig[1])<<endl;
从其中可以看出两个量的值是不同的:
0x22ff40
0x3e3cbc //这里和后面的数据相差一个字节,代表这是一个char类型的
0x3e3cbd
*/
fclose(f);
signature = signature.substr(0, maxlen);
for( i = 0; i < decoders.size(); i++ )
{
if( decoders[i]->checkSignature(signature) )//检测读取出来的数据是否与保存的数据signature一样,一样就代表是这个类型了。
return decoders[i]->newDecoder();//创建这个类型的decoder变量。
}
return ImageDecoder();
}
//下面是decoders数据内容的来源:
struct ImageCodecInitializer
{
ImageCodecInitializer()
{
decoders.push_back( new BmpDecoder );
encoders.push_back( new BmpEncoder );
#ifdef HAVE_JPEG
decoders.push_back( new JpegDecoder );
encoders.push_back( new JpegEncoder );
#endif
decoders.push_back( new SunRasterDecoder );
encoders.push_back( new SunRasterEncoder );
decoders.push_back( new PxMDecoder );
encoders.push_back( new PxMEncoder );
#ifdef HAVE_TIFF
decoders.push_back( new TiffDecoder );
#endif
encoders.push_back( new TiffEncoder );
#ifdef HAVE_PNG
decoders.push_back( new PngDecoder );
encoders.push_back( new PngEncoder );
#endif
#ifdef HAVE_JASPER
decoders.push_back( new Jpeg2KDecoder );
encoders.push_back( new Jpeg2KEncoder );
#endif
#ifdef HAVE_OPENEXR
decoders.push_back( new ExrDecoder );
encoders.push_back( new ExrEncoder );
#endif
// because it is a generic image I/O API, supporting many formats,
// it should be last in the list.
#ifdef HAVE_IMAGEIO
decoders.push_back( new ImageIODecoder );
encoders.push_back( new ImageIOEncoder );
#endif
}
};
static ImageCodecInitializer initialize_codecs; //这里直接的给定了decoders的内容。
这样读取图像数据又清晰了一步,首先是我们会首先保存好要解析的图像格式,即支持什么类型的图像。然后第一步是取读取保存在第一个数据点上的signature数据,然后再去读取下面的数据。所以,要很清楚的了解图像的头。