天天看點

如何檢視Opencv中函數的源代碼--imread()的源代碼

1. 檢視opencv中函數的源代碼

為了檢視opencv中的源代碼,可以安裝cmake,編譯一個.sln工程,如下圖所示。這樣可以很友善的檢視函數的源代碼。

關于配置cmake的方法可以直接百度,有很多相關教程

如何檢視Opencv中函數的源代碼--imread()的源代碼

2. imread()的源代碼

下面是轉載自hujingshuang的部落格(http://blog.csdn.net/hujingshuang/article/details/47184717),對imread()的源代碼添加了注釋,友善閱讀了解。

imread函數原型如下,filename是檔案名,flags預設為1,其含義同上iscolor枚舉。

[cpp]  view plain  copy

  1. CV_EXPORTS_W Mat imread( const string& filename, int flags=1 );  

進一步檢視imread源碼,發現實際上調用的還是imread_函數,現在這裡的參數就是LOAD_MAT了。 [cpp]  view plain  copy

  1. Mat imread( const string& filename, int flags )  
  2. {  
  3.     Mat img;//定義一個Mat類,用于裝載圖檔  
  4.     imread_( filename, flags, LOAD_MAT, &img );//讀圖像  
  5.     return img;  
  6. }  

顯然,不論是cvLoadImage還是imread,都是調用的imread_函數。那麼我們就由此及彼,由表及裡,去粗取精,去僞存真的去剖析其源碼。

imread_函數源碼(在源檔案loadsave.cpp中): [cpp]  view plain  copy

  1. static void*  
  2. imread_( const string& filename, int flags, int hdrtype, Mat* mat=0 )  
  3. {  
  4.     IplImage* image = 0;//定義一個IplImage結構體  
  5.     CvMat *matrix = 0;//定義一個CvMat結構體  
  6.     Mat temp, *data = &temp;//data中儲存的是temp的位址,temp是一個Mat類容器  
  7.     ImageDecoder decoder = findDecoder(filename);//①譯碼器  
  8.     if( decoder.empty() )  
  9.         return 0;  
  10.     decoder->setSource(filename);  
  11.     if( !decoder->readHeader() )//②讀取資訊頭  
  12.         return 0;  
  13.     CvSize size;  
  14.     size.width = decoder->width();  
  15.     size.height = decoder->height();  
  16.     int type = decoder->type();  
  17.     if( flags != -1 )//③  
  18.     {  
  19.         if( (flags & CV_LOAD_IMAGE_ANYDEPTH) == 0 )  
  20.             type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));  
  21.         if( (flags & CV_LOAD_IMAGE_COLOR) != 0 ||  
  22.            ((flags & CV_LOAD_IMAGE_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) )  
  23.             type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);//彩色  
  24.         else  
  25.             type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1);//灰階  
  26.     }  
  27.     if( hdrtype == LOAD_CVMAT || hdrtype == LOAD_MAT )//④  
  28.     {  
  29.         if( hdrtype == LOAD_CVMAT )  
  30.         {  
  31.             matrix = cvCreateMat( size.height, size.width, type );  
  32.             temp = cvarrToMat(matrix);//temp與matrix同址  
  33.         }  
  34.         else  
  35.         {  
  36.             mat->create( size.height, size.width, type );  
  37.             data = mat;//data與mat同址  
  38.         }  
  39.     }  
  40.     else  
  41.     {  
  42.         image = cvCreateImage( size, cvIplDepth(type), CV_MAT_CN(type) );  
  43.         temp = cvarrToMat(image);//temp與image同址  
  44.     }  
  45.     if( !decoder->readData( *data ))//⑤  
  46.     {  
  47.         cvReleaseImage( &image );  
  48.         cvReleaseMat( &matrix );  
  49.         if( mat )  
  50.             mat->release();  
  51.         return 0;  
  52.     }  
  53.     //根據指針及同址關系,可知matrix、image、mat資料(若存在)與data資料一緻  
  54.     return hdrtype == LOAD_CVMAT ? (void*)matrix :  
  55.         hdrtype == LOAD_IMAGE ? (void*)image : (void*)mat;  
  56. }  

在imread_中,有幾個地方值得注意①②③④⑤,下面一一分析:

①findDecoder(),這是一個很重要的重載函數,它的目的是:解析圖檔資訊,并确定應該使用的譯碼器(.jpg格式使用Jpeg譯碼器),其内部源碼如下:

[cpp]  view plain  copy

  1. static ImageCodecInitializer codecs;  
  2. static ImageDecoder findDecoder( const string& filename )  
  3. {  
  4.     size_t i, maxlen = 0;  
  5.     for( i = 0; i < codecs.decoders.size(); i++ )  
  6.     {  
  7.         size_t len = codecs.decoders[i]->signatureLength();  
  8.         maxlen = std::max(maxlen, len);  
  9.     }  
  10.     FILE* f= fopen( filename.c_str(), "rb" );//讀取二進制檔案  
  11.     if( !f )  
  12.         return ImageDecoder();  
  13.     string signature(maxlen, ' ');  
  14.     maxlen = fread( &signature[0], 1, maxlen, f );  
  15.     fclose(f);  
  16.     signature = signature.substr(0, maxlen);  
  17.     for( i = 0; i < codecs.decoders.size(); i++ )  
  18.     {  
  19.         if( codecs.decoders[i]->checkSignature(signature) )  
  20.             return codecs.decoders[i]->newDecoder();  
  21.     }  
  22.     return ImageDecoder();  
  23. }  

譯碼器的定義如下:

[cpp]  view plain  copy

  1. struct ImageCodecInitializer  
  2. {  
  3.     ImageCodecInitializer()  
  4.     {  
  5.         decoders.push_back( new BmpDecoder );//Bmp譯碼器  
  6.         encoders.push_back( new BmpEncoder );//Bmp編碼器  
  7.     #ifdef HAVE_JPEG  
  8.         decoders.push_back( new JpegDecoder );//Jpeg  
  9.         encoders.push_back( new JpegEncoder );  
  10.     #endif  
  11.         decoders.push_back( new SunRasterDecoder );  
  12.         encoders.push_back( new SunRasterEncoder );  
  13.         decoders.push_back( new PxMDecoder );PxM  
  14.         encoders.push_back( new PxMEncoder );  
  15.     #ifdef HAVE_TIFF  
  16.         decoders.push_back( new TiffDecoder );//Tiff  
  17.     #endif  
  18.         encoders.push_back( new TiffEncoder );  
  19.     #ifdef HAVE_PNG  
  20.         decoders.push_back( new PngDecoder );//Png  
  21.         encoders.push_back( new PngEncoder );  
  22.     #endif  
  23.     #ifdef HAVE_JASPER  
  24.         decoders.push_back( new Jpeg2KDecoder );  
  25.         encoders.push_back( new Jpeg2KEncoder );  
  26.     #endif  
  27.     #ifdef HAVE_OPENEXR  
  28.         decoders.push_back( new ExrDecoder );  
  29.         encoders.push_back( new ExrEncoder );  
  30.     #endif  
  31.     // because it is a generic image I/O API, supporting many formats,  
  32.     // it should be last in the list.  
  33.     #ifdef HAVE_IMAGEIO  
  34.         decoders.push_back( new ImageIODecoder );  
  35.         encoders.push_back( new ImageIOEncoder );  
  36.     #endif  
  37.     }  
  38.     vector<ImageDecoder> decoders;  
  39.     vector<ImageEncoder> encoders;  
  40. };  

②decoder->readHeader(),它是屬于decoder類方法,它的作用是:根據上述譯碼器類型(即Jpeg譯碼器),對圖檔進行解壓,并讀取圖檔資訊頭。其源碼如下:

[cpp]  view plain  copy

  1. bool  JpegDecoder::readHeader()//Jpeg格式譯碼器  
  2. {  
  3.     bool result = false;  
  4.     close();  
  5.     JpegState* state = new JpegState;  
  6.     m_state = state;  
  7.     state->cinfo.err = jpeg_std_error(&state->jerr.pub);  
  8.     state->jerr.pub.error_exit = error_exit;  
  9.     if( setjmp( state->jerr.setjmp_buffer ) == 0 )  
  10.     {  
  11.         jpeg_create_decompress( &state->cinfo );  
  12.         if( !m_buf.empty() )  
  13.         {  
  14.             jpeg_buffer_src(&state->cinfo, &state->source);  
  15.             state->source.pub.next_input_byte = m_buf.data;  
  16.             state->source.pub.bytes_in_buffer = m_buf.cols*m_buf.rows*m_buf.elemSize();  
  17.         }  
  18.         else  
  19.         {  
  20.             m_f = fopen( m_filename.c_str(), "rb" );  
  21.             if( m_f )  
  22.                 jpeg_stdio_src( &state->cinfo, m_f );  
  23.         }  
  24.         if (state->cinfo.src != 0)  
  25.         {  
  26.             jpeg_read_header( &state->cinfo, TRUE );  
  27.             m_width = state->cinfo.image_width;//寬  
  28.             m_height = state->cinfo.image_height;//高  
  29.             m_type = state->cinfo.num_components > 1 ? CV_8UC3 : CV_8UC1;  
  30.             result = true;  
  31.         }  
  32.     }  
  33.     if( !result )  
  34.         close();  
  35.     return result;  
  36. }  

③flags是用于判斷讀取圖檔的方式。

④hdrtype的值不是LOAD_CVMAT就是LOAD_MAT或者LOAD_IMAGE,因為IplImage、cvMat都是由cvArr派生出來的,是以hdrtype不論是LOAD_CVMAT還是LOAD_IMAGE,最終都會cvarrToMat()轉換成為Mat類型。

⑤decoder->readData(),它屬于decoder類方法,是用來解析圖檔資料的,它将解析出的資料存放于傳入的參數中,其源碼如下。在源碼的一些函數中,用到Jpeg解壓并涉及到了DCT變換的代碼,到了底層是彙編代碼,在memcpy.asm中(此處我們隻看目的,不究過程):

[cpp]  view plain  copy

  1. bool  JpegDecoder::readData( Mat& img )  
  2. {  
  3.     bool result = false;  
  4.     int step = (int)img.step;  
  5.     bool color = img.channels() > 1;  
  6.     if( m_state && m_width && m_height )  
  7.     {  
  8.         jpeg_decompress_struct* cinfo = &((JpegState*)m_state)->cinfo;  
  9.         JpegErrorMgr* jerr = &((JpegState*)m_state)->jerr;  
  10.         JSAMPARRAY buffer = 0;  
  11.         if( setjmp( jerr->setjmp_buffer ) == 0 )  
  12.         {  
  13.             if ( cinfo->ac_huff_tbl_ptrs[0] == NULL &&  
  14.                 cinfo->ac_huff_tbl_ptrs[1] == NULL &&  
  15.                 cinfo->dc_huff_tbl_ptrs[0] == NULL &&  
  16.                 cinfo->dc_huff_tbl_ptrs[1] == NULL )  
  17.             {  
  18.                 my_jpeg_load_dht( cinfo,  
  19.                     my_jpeg_odml_dht,  
  20.                     cinfo->ac_huff_tbl_ptrs,  
  21.                     cinfo->dc_huff_tbl_ptrs );  
  22.             }  
  23.             if( color )  
  24.             {  
  25.                 if( cinfo->num_components != 4 )  
  26.                 {  
  27.                     cinfo->out_color_space = JCS_RGB;  
  28.                     cinfo->out_color_components = 3;  
  29.                 }  
  30.                 else  
  31.                 {  
  32.                     cinfo->out_color_space = JCS_CMYK;  
  33.                     cinfo->out_color_components = 4;  
  34.                 }  
  35.             }  
  36.             else  
  37.             {  
  38.                 if( cinfo->num_components != 4 )  
  39.                 {  
  40.                     cinfo->out_color_space = JCS_GRAYSCALE;  
  41.                     cinfo->out_color_components = 1;  
  42.                 }  
  43.                 else  
  44.                 {  
  45.                     cinfo->out_color_space = JCS_CMYK;  
  46.                     cinfo->out_color_components = 4;  
  47.                 }  
  48.             }  
  49.             jpeg_start_decompress( cinfo );  
  50.             buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,  
  51.                                               JPOOL_IMAGE, m_width*4, 1 );  
  52.             uchar* data = img.data;  
  53.             for( ; m_height--; data += step )  
  54.             {  
  55.                 jpeg_read_scanlines( cinfo, buffer, 1 );  
  56.                 if( color )  
  57.                 {  
  58.                     if( cinfo->out_color_components == 3 )  
  59.                         icvCvt_RGB2BGR_8u_C3R( buffer[0], 0, data, 0, cvSize(m_width,1) );  
  60.                     else  
  61.                         icvCvt_CMYK2BGR_8u_C4C3R( buffer[0], 0, data, 0, cvSize(m_width,1) );  
  62.                 }  
  63.                 else  
  64.                 {  
  65.                     if( cinfo->out_color_components == 1 )  
  66.                         memcpy( data, buffer[0], m_width );  
  67.                     else  
  68.                         icvCvt_CMYK2Gray_8u_C4C1R( buffer[0], 0, data, 0, cvSize(m_width,1) );  
  69.                 }  
  70.             }  
  71.             result = true;  
  72.             jpeg_finish_decompress( cinfo );  
  73.         }  
  74.     }  
  75.     close();  
  76.     return result;  
  77. }  

imread_經過5個關鍵步驟,将讀取到的圖檔資訊頭,以及圖檔資料以Mat類型傳回(若是cvLoadImage則會被強制轉換成IplImage類型)。

總結下來,方法①②,讀取圖像的過程都是:輸入filename—>解析圖檔—>确定譯碼器—>譯碼函數進行資訊、資料的讀取—>存放于Mat容器—>傳回。

繼續閱讀