1. 程式人生 > >原始碼解析: Imread函式

原始碼解析: Imread函式

在第一篇處,我們只是在最表層的上面操作函式,當別人問我們時,我們其實什麼也不知道的。就知道,imread是讀取函數了,然後掉用其它的函式的樂樂。當然,上面我們可以好好學習人家為什麼要這樣做了!這裡,看一個函式finddecoder()。這個函式主要是獲取decoder物件,從而決定讀取什麼樣字尾名的影象(jpg,bmp等等)

findDecoder()函式

宣告:

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資料,然後再去讀取下面的資料。所以,要很清楚的瞭解影象的頭。

ImageDecoder類