在第一篇處,我們只是在最表層的上面操作函數,當別人問我們時,我們其實什麼也不知道的。就知道,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數據,然後再去讀取下面的數據。所以,要很清楚的瞭解圖像的頭。