原始碼解析: Imread函式
阿新 • • 發佈:2019-02-09
在第一篇處,我們只是在最表層的上面操作函式,當別人問我們時,我們其實什麼也不知道的。就知道,imread是讀取函數了,然後掉用其它的函式的樂樂。當然,上面我們可以好好學習人家為什麼要這樣做了!這裡,看一個函式finddecoder()。這個函式主要是獲取decoder物件,從而決定讀取什麼樣字尾名的影象(jpg,bmp等等)
findDecoder()函式
宣告:
這個是一個函式的過載了,在第一篇,即imread函式中呼叫的是第一個,這裡就跟進第一個。ImageDecoder findDecoder( const string& filename );//在highgui/loadsave.cpp中 ImageDecoder findDecoder( const Mat& buf );
定義:
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資料,然後再去讀取下面的資料。所以,要很清楚的瞭解影象的頭。