1. 檢視opencv中函數的源代碼
為了檢視opencv中的源代碼,可以安裝cmake,編譯一個.sln工程,如下圖所示。這樣可以很友善的檢視函數的源代碼。
關于配置cmake的方法可以直接百度,有很多相關教程

2. imread()的源代碼
下面是轉載自hujingshuang的部落格(http://blog.csdn.net/hujingshuang/article/details/47184717),對imread()的源代碼添加了注釋,友善閱讀了解。
imread函數原型如下,filename是檔案名,flags預設為1,其含義同上iscolor枚舉。
[cpp] view plain copy
- CV_EXPORTS_W Mat imread( const string& filename, int flags=1 );
進一步檢視imread源碼,發現實際上調用的還是imread_函數,現在這裡的參數就是LOAD_MAT了。 [cpp] view plain copy
- Mat imread( const string& filename, int flags )
- {
- Mat img;//定義一個Mat類,用于裝載圖檔
- imread_( filename, flags, LOAD_MAT, &img );//讀圖像
- return img;
- }
顯然,不論是cvLoadImage還是imread,都是調用的imread_函數。那麼我們就由此及彼,由表及裡,去粗取精,去僞存真的去剖析其源碼。
imread_函數源碼(在源檔案loadsave.cpp中): [cpp] view plain copy
- static void*
- imread_( const string& filename, int flags, int hdrtype, Mat* mat=0 )
- {
- IplImage* image = 0;//定義一個IplImage結構體
- CvMat *matrix = 0;//定義一個CvMat結構體
- Mat temp, *data = &temp;//data中儲存的是temp的位址,temp是一個Mat類容器
- ImageDecoder decoder = findDecoder(filename);//①譯碼器
- if( decoder.empty() )
- return 0;
- decoder->setSource(filename);
- if( !decoder->readHeader() )//②讀取資訊頭
- return 0;
- CvSize size;
- size.width = decoder->width();
- size.height = decoder->height();
- int type = decoder->type();
- if( flags != -1 )//③
- {
- if( (flags & CV_LOAD_IMAGE_ANYDEPTH) == 0 )
- type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));
- if( (flags & CV_LOAD_IMAGE_COLOR) != 0 ||
- ((flags & CV_LOAD_IMAGE_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) )
- type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);//彩色
- else
- type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1);//灰階
- }
- if( hdrtype == LOAD_CVMAT || hdrtype == LOAD_MAT )//④
- {
- if( hdrtype == LOAD_CVMAT )
- {
- matrix = cvCreateMat( size.height, size.width, type );
- temp = cvarrToMat(matrix);//temp與matrix同址
- }
- else
- {
- mat->create( size.height, size.width, type );
- data = mat;//data與mat同址
- }
- }
- else
- {
- image = cvCreateImage( size, cvIplDepth(type), CV_MAT_CN(type) );
- temp = cvarrToMat(image);//temp與image同址
- }
- if( !decoder->readData( *data ))//⑤
- {
- cvReleaseImage( &image );
- cvReleaseMat( &matrix );
- if( mat )
- mat->release();
- return 0;
- }
- //根據指針及同址關系,可知matrix、image、mat資料(若存在)與data資料一緻
- return hdrtype == LOAD_CVMAT ? (void*)matrix :
- hdrtype == LOAD_IMAGE ? (void*)image : (void*)mat;
- }
在imread_中,有幾個地方值得注意①②③④⑤,下面一一分析:
①findDecoder(),這是一個很重要的重載函數,它的目的是:解析圖檔資訊,并确定應該使用的譯碼器(.jpg格式使用Jpeg譯碼器),其内部源碼如下:
[cpp] view plain copy
- static ImageCodecInitializer codecs;
- static ImageDecoder findDecoder( const string& filename )
- {
- size_t i, maxlen = 0;
- for( i = 0; i < codecs.decoders.size(); i++ )
- {
- size_t len = codecs.decoders[i]->signatureLength();
- maxlen = std::max(maxlen, len);
- }
- FILE* f= fopen( filename.c_str(), "rb" );//讀取二進制檔案
- if( !f )
- return ImageDecoder();
- string signature(maxlen, ' ');
- maxlen = fread( &signature[0], 1, maxlen, f );
- fclose(f);
- signature = signature.substr(0, maxlen);
- for( i = 0; i < codecs.decoders.size(); i++ )
- {
- if( codecs.decoders[i]->checkSignature(signature) )
- return codecs.decoders[i]->newDecoder();
- }
- return ImageDecoder();
- }
譯碼器的定義如下:
[cpp] view plain copy
- struct ImageCodecInitializer
- {
- ImageCodecInitializer()
- {
- decoders.push_back( new BmpDecoder );//Bmp譯碼器
- encoders.push_back( new BmpEncoder );//Bmp編碼器
- #ifdef HAVE_JPEG
- decoders.push_back( new JpegDecoder );//Jpeg
- encoders.push_back( new JpegEncoder );
- #endif
- decoders.push_back( new SunRasterDecoder );
- encoders.push_back( new SunRasterEncoder );
- decoders.push_back( new PxMDecoder );PxM
- encoders.push_back( new PxMEncoder );
- #ifdef HAVE_TIFF
- decoders.push_back( new TiffDecoder );//Tiff
- #endif
- encoders.push_back( new TiffEncoder );
- #ifdef HAVE_PNG
- decoders.push_back( new PngDecoder );//Png
- 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
- }
- vector<ImageDecoder> decoders;
- vector<ImageEncoder> encoders;
- };
②decoder->readHeader(),它是屬于decoder類方法,它的作用是:根據上述譯碼器類型(即Jpeg譯碼器),對圖檔進行解壓,并讀取圖檔資訊頭。其源碼如下:
[cpp] view plain copy
- bool JpegDecoder::readHeader()//Jpeg格式譯碼器
- {
- bool result = false;
- close();
- JpegState* state = new JpegState;
- m_state = state;
- state->cinfo.err = jpeg_std_error(&state->jerr.pub);
- state->jerr.pub.error_exit = error_exit;
- if( setjmp( state->jerr.setjmp_buffer ) == 0 )
- {
- jpeg_create_decompress( &state->cinfo );
- if( !m_buf.empty() )
- {
- jpeg_buffer_src(&state->cinfo, &state->source);
- state->source.pub.next_input_byte = m_buf.data;
- state->source.pub.bytes_in_buffer = m_buf.cols*m_buf.rows*m_buf.elemSize();
- }
- else
- {
- m_f = fopen( m_filename.c_str(), "rb" );
- if( m_f )
- jpeg_stdio_src( &state->cinfo, m_f );
- }
- if (state->cinfo.src != 0)
- {
- jpeg_read_header( &state->cinfo, TRUE );
- m_width = state->cinfo.image_width;//寬
- m_height = state->cinfo.image_height;//高
- m_type = state->cinfo.num_components > 1 ? CV_8UC3 : CV_8UC1;
- result = true;
- }
- }
- if( !result )
- close();
- return result;
- }
③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
- bool JpegDecoder::readData( Mat& img )
- {
- bool result = false;
- int step = (int)img.step;
- bool color = img.channels() > 1;
- if( m_state && m_width && m_height )
- {
- jpeg_decompress_struct* cinfo = &((JpegState*)m_state)->cinfo;
- JpegErrorMgr* jerr = &((JpegState*)m_state)->jerr;
- JSAMPARRAY buffer = 0;
- if( setjmp( jerr->setjmp_buffer ) == 0 )
- {
- if ( cinfo->ac_huff_tbl_ptrs[0] == NULL &&
- cinfo->ac_huff_tbl_ptrs[1] == NULL &&
- cinfo->dc_huff_tbl_ptrs[0] == NULL &&
- cinfo->dc_huff_tbl_ptrs[1] == NULL )
- {
- my_jpeg_load_dht( cinfo,
- my_jpeg_odml_dht,
- cinfo->ac_huff_tbl_ptrs,
- cinfo->dc_huff_tbl_ptrs );
- }
- if( color )
- {
- if( cinfo->num_components != 4 )
- {
- cinfo->out_color_space = JCS_RGB;
- cinfo->out_color_components = 3;
- }
- else
- {
- cinfo->out_color_space = JCS_CMYK;
- cinfo->out_color_components = 4;
- }
- }
- else
- {
- if( cinfo->num_components != 4 )
- {
- cinfo->out_color_space = JCS_GRAYSCALE;
- cinfo->out_color_components = 1;
- }
- else
- {
- cinfo->out_color_space = JCS_CMYK;
- cinfo->out_color_components = 4;
- }
- }
- jpeg_start_decompress( cinfo );
- buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,
- JPOOL_IMAGE, m_width*4, 1 );
- uchar* data = img.data;
- for( ; m_height--; data += step )
- {
- jpeg_read_scanlines( cinfo, buffer, 1 );
- if( color )
- {
- if( cinfo->out_color_components == 3 )
- icvCvt_RGB2BGR_8u_C3R( buffer[0], 0, data, 0, cvSize(m_width,1) );
- else
- icvCvt_CMYK2BGR_8u_C4C3R( buffer[0], 0, data, 0, cvSize(m_width,1) );
- }
- else
- {
- if( cinfo->out_color_components == 1 )
- memcpy( data, buffer[0], m_width );
- else
- icvCvt_CMYK2Gray_8u_C4C1R( buffer[0], 0, data, 0, cvSize(m_width,1) );
- }
- }
- result = true;
- jpeg_finish_decompress( cinfo );
- }
- }
- close();
- return result;
- }
imread_經過5個關鍵步驟,将讀取到的圖檔資訊頭,以及圖檔資料以Mat類型傳回(若是cvLoadImage則會被強制轉換成IplImage類型)。
總結下來,方法①②,讀取圖像的過程都是:輸入filename—>解析圖檔—>确定譯碼器—>譯碼函數進行資訊、資料的讀取—>存放于Mat容器—>傳回。