opencv圖像的載入,顯示和輸出完全解析

轉自http://blog.csdn.net/poem_qianmo/article/details/20537737

菜鳥再次向淺墨大神學習!



本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。  

文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/20537737

作者:毛星雲(淺墨)    郵箱: [email protected] 

寫作當前博文時配套使用的OpenCV版本: 2.4.8


這篇文章中,我們將詳細而深入地弄懂入門OpenCV2最基本的問題,那就是圖像的載入,顯示和輸出。

PS:文章末尾提供了博文配套程序源代碼的下載。

依然是先看一張運行截圖:



瞭解過之前老版本OpenCV的童鞋們都應該清楚,對於OpenCV1.0時代的基於 C 語言接口而建的圖像存儲格式IplImage*,如果在退出前忘記release掉的話,就會照成內存泄露。而且用起來超級麻煩,我們往往在debug的時候,很大一部分時間在糾結手動釋放內存的問題。雖然對於小型的程序來說手動管理內存不是問題,但一旦我們寫的代碼變得越來越龐大,我們便會開始越來越多地糾纏於內存管理的問題,而不是着力解決你的開發目標。

這,就有些捨本逐末的感覺了。


而淺墨在這篇文章開頭想說,自從OpenCV踏入2.0時代,用Mat類數據結構作爲主打之後,OpenCV變得越發像需要很少編程涵養的Matlab那樣,上手超級快。甚至有些函數名稱都和matlab一樣,比如大家所熟知的imread,imwrite,imshow等函數。

這對於我們廣大圖像處理領域的孩子們來說,這的確是一個可喜可賀的事情。

 

這篇文章中,我們主要來詳細看一看入門OpenCV2最基本的問題,那就圖像的載入,顯示和輸出。


 


 

一、開胃菜之一 • 關於OpenCV的命名空間

 


OpenCV中的C++類和函數都是定義在命名空間cv之內的,有兩種方法可以訪問。第一種是,在代碼開頭的適當位置,加上usingnamespace cv;這句。

另外一種是在使用OpenCV類和函數時,都加入cv::命名空間。不過這種情況難免會不爽,每用一個OpenCV的類或者函數,都要多敲四下鍵盤寫出cv::,很麻煩。

所以,淺墨推崇大家在代碼開頭的適當位置,加上using namespace cv;這句。於是和opencv命名空間一了百了了。

 

 

比如淺墨,在寫簡單的OpenCV程序的時候,如下這三句是標配:

  1. #include <opencv2/core/core.hpp>  
  2. #include<opencv2/highgui/highgui.hpp>  
  3.    
  4. using namespace cv;  



 

二、開胃菜之二 • 關於Mat類型



cv::Mat類是用於保存圖像以及其他矩陣數據的數據結構。默認情況下,其尺寸爲0,我們也可以指定初始尺寸,比如,比如定義一個Mat類對象,就要寫cv::Mat pic(320,640,cv::Scalar(100));

 

Mat類型作爲OpenCV2新紀元的重要代表“人物”,淺墨準備在稍後的文章中,花長篇幅詳細講解它,現在我們只要理解,它是對應於OpenCV1.0時代的IplImage的主要用來存放圖像的數據結構就行了。對於這篇文章,我們需要用到關於Mat其實就簡單的這樣一句代碼:

  1. Mat myMat= imread("dota.jpg");  

 

表示從工程目錄下把一幅名爲dota.jpg的jpg類型的圖像載入到Mat類型的myMat中。這裏的imread函數這篇文章的下文就會詳細剖析到。

 

好吧,開胃菜就是這麼多了,下面來看看今天的主要內容,圖像的載入和顯示,處理圖像混合,設置感興趣區域以及如何輸出圖像,一項一項來擊破吧。

 


三、圖像的載入和顯示



在新版本的OpenCV2中,最簡單的圖像載入和顯示只需要3句代碼,非常便捷。這三句代碼分別對應了三個函數,他們分別是:

imread( ), namedWindow( )以及imshow( )。我們依次來解析一下這三個函數。

 

 

1.imread函數


首先,我們看imread函數,可以在OpenCV官方文檔中查到其原型如下:

 

  1. Mat imread(const string& filename, intflags=1 );  

 

■ 第一個參數,const string&類型的filename,填我們需要載入的圖片路徑名。

在Windows操作系統下,OpenCV的imread函數支持如下類型的圖像載入:

 

  • Windows位圖 - *.bmp, *.dib
  • JPEG文件 - *.jpeg, *.jpg, *.jpe
  • JPEG 2000文件- *.jp2
  • PNG圖片 - *.png
  • 便攜文件格式- *.pbm, *.pgm, *.ppm
  • Sun rasters光柵文件 - *.sr, *.ras
  • TIFF 文件 - *.tiff, *.tif

■ 第二個參數,int類型的flags,爲載入標識,它指定一個加載圖像的顏色類型。可以看到它自帶缺省值1.所以有時候這個參數在調用時我們可以忽略,在看了下面的講解之後,我們就會發現,如果在調用時忽略這個參數,就表示載入三通道的彩色圖像。

可以在OpenCV中標識圖像格式的枚舉體中取值。通過轉到定義,我們可以在higui_c.h中發現這個枚舉的定義是這樣的:

  1. enum  
  2. {  
  3. /* 8bit, color or not */  
  4.    CV_LOAD_IMAGE_UNCHANGED  =-1,  
  5. /* 8bit, gray */  
  6.    CV_LOAD_IMAGE_GRAYSCALE  =0,  
  7. /* ?, color */  
  8.    CV_LOAD_IMAGE_COLOR      =1,  
  9. /* any depth, ? */  
  10.    CV_LOAD_IMAGE_ANYDEPTH   =2,  
  11. /* ?, any color */  
  12.    CV_LOAD_IMAGE_ANYCOLOR   =4  
  13. };  

相應的解釋:

  • CV_LOAD_IMAGE_UNCHANGED,這個標識在新版本中被廢置了,忽略。
  • CV_LOAD_IMAGE_ANYDEPTH- 如果取這個標識的話,若載入的圖像的深度爲16位或者32位,就返回對應深度的圖像,否則,就轉換爲8位圖像再返回。
  • CV_LOAD_IMAGE_COLOR- 如果取這個標識的話,總是轉換圖像到彩色一體
  • CV_LOAD_IMAGE_GRAYSCALE- 如果取這個標識的話,始終將圖像轉換成灰度1

 

如果輸入有衝突的標誌,將採用較小的數字值。比如CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYCOLOR 將載入3通道圖。

如果想要載入最真實的圖像,選擇CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR。

 

因爲flags是int型的變量,如果我們不在這個枚舉體中取值的話,還可以這樣來:

  • flags >0返回一個3通道的彩色圖像。
  • flags =0返回灰度圖像。
  • flags <0返回包含Alpha通道的加載的圖像。

需要注意的點:輸出的圖像默認情況下是不載入Alpha通道進來的。如果我們需要載入Alpha通道的話呢,這裏就需要取負值。

 

如果你搞怪,flags取1999,也是可以的,這時就表示返回一個3通道的彩色圖像。

 

 

好了,講了這麼多,來幾個載入示例,一看就懂:

 

  1. Mat image0=imread("dota.jpg",CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);//載入最真實的圖像  
  2. ge1=imread("dota.jpg",0);//載入灰度圖  
  3. Mat image2=imread("dota.jpg",199);//載入3通道的彩色圖像  
  4. Mat logo=imread("dota_logo.jpg");//載入3通道的彩色圖像  



2.namedWindow函數


顧名思義,namedWindow函數,用於創建一個窗口。

函數原型是這樣的:

  1. void namedWindow(const string& winname,int flags=WINDOW_AUTOSIZE );   

      ■ 第一個參數,const string&型的name,即填被用作窗口的標識符的窗口名稱。

      ■ 第二個參數,int 類型的flags ,窗口的標識,可以填如下的值:

  • WINDOW_NORMAL設置了這個值,用戶便可以改變窗口的大小(沒有限制)
  • WINDOW_AUTOSIZE如果設置了這個值,窗口大小會自動調整以適應所顯示的圖像,並且不能手動改變窗口大小。
  • WINDOW_OPENGL 如果設置了這個值的話,窗口創建的時候便會支持OpenGL。

函數剖析:

首先需要注意的是,它有默認值WINDOW_AUTOSIZE,所以,一般情況下,這個函數我們填一個變量就行了。

namedWindow函數的作用是,通過指定的名字,創建一個可以作爲圖像和進度條的容器窗口。如果具有相同名稱的窗口已經存在,則函數不做任何事情。

我們可以調用destroyWindow()或者destroyAllWindows()函數來關閉窗口,並取消之前分配的與窗口相關的所有內存空間。

但話是這樣說,其實對於代碼量不大的簡單小程序來說,我們完全沒有必要手動調用上述的destroyWindow()或者destroyAllWindows()函數,因爲在退出時,所有的資源和應用程序的窗口會被操作系統會自動關閉。

 

3.imshow函數


在指定的窗口中顯示一幅圖像。

  1. void imshow(const string& winname, InputArray mat);  

 ■ 第一個參數,const string&類型的winname,填需要顯示的窗口標識名稱。

 ■ 第二個參數,InputArray 類型的mat,填需要顯示的圖像。

 

這裏的InputArray 我們講一下吧,不然一直是個梗在這邊。通過轉到定義大法,我們可以在

Highgui.hpp中查到imshow的原型:

 

  1. CV_EXPORTS_W void imshow(const string&winname, InputArray mat);  

 

進一步對InputArray轉到定義,在core.hpp中查到一個typedef聲明:

  1. typedef const _InputArray& InputArray;  

這其實一個類型聲明引用,就是說_InputArray和InputArray是一個意思,既然他們是一個意思,我們就來做最後一步,對_InputArray進行轉到定義,終於,我們在core.hpp發現了InputArray的真身:


  1. class CV_EXPORTS _InputArray  
  2. {  
  3. public:  
  4.    enum {  
  5.        KIND_SHIFT = 16,  
  6.         FIXED_TYPE = 0x8000 << KIND_SHIFT,  
  7.        FIXED_SIZE = 0x4000 << KIND_SHIFT,  
  8.        KIND_MASK = ~(FIXED_TYPE|FIXED_SIZE) - (1 << KIND_SHIFT) + 1,  
  9.    
  10.        NONE              = 0 <<KIND_SHIFT,  
  11.        MAT               = 1 <<KIND_SHIFT,  
  12.        MATX              = 2 <<KIND_SHIFT,  
  13.        STD_VECTOR        = 3 <<KIND_SHIFT,  
  14.        STD_VECTOR_VECTOR = 4 << KIND_SHIFT,  
  15.        STD_VECTOR_MAT    = 5 <<KIND_SHIFT,  
  16.        EXPR              = 6 <<KIND_SHIFT,  
  17.        OPENGL_BUFFER     = 7 <<KIND_SHIFT,  
  18.        OPENGL_TEXTURE    = 8 <<KIND_SHIFT,  
  19.        GPU_MAT           = 9 <<KIND_SHIFT,  
  20.        OCL_MAT           =10 <<KIND_SHIFT  
  21.    };  
  22.    _InputArray();  
  23.    
  24.    _InputArray(const Mat& m);  
  25.    _InputArray(const MatExpr& expr);  
  26.    template<typename _Tp> _InputArray(const _Tp* vec, int n);  
  27.    template<typename _Tp> _InputArray(const vector<_Tp>&vec);  
  28.    template<typename _Tp> _InputArray(constvector<vector<_Tp> >& vec);  
  29.    _InputArray(const vector<Mat>& vec);  
  30.    template<typename _Tp> _InputArray(const vector<Mat_<_Tp>>& vec);  
  31.    template<typename _Tp> _InputArray(const Mat_<_Tp>& m);  
  32.    template<typename _Tp, int m, int n> _InputArray(constMatx<_Tp, m, n>& matx);  
  33.    _InputArray(const Scalar& s);  
  34.    _InputArray(const double& val);  
  35.    // < Deprecated  
  36.    _InputArray(const GlBuffer& buf);  
  37.    _InputArray(const GlTexture& tex);  
  38.    // >  
  39.    _InputArray(const gpu::GpuMat& d_mat);  
  40.    _InputArray(const ogl::Buffer& buf);  
  41.    _InputArray(const ogl::Texture2D& tex);  
  42.    
  43.    virtual Mat getMat(int i=-1) const;  
  44.    virtual void getMatVector(vector<Mat>& mv) const;  
  45.    // < Deprecated  
  46.    virtual GlBuffer getGlBuffer() const;  
  47.    virtual GlTexture getGlTexture() const;  
  48.    // >  
  49.    virtual gpu::GpuMat getGpuMat() const;  
  50.    /*virtual*/ ogl::Buffer getOGlBuffer() const;  
  51.    /*virtual*/ ogl::Texture2D getOGlTexture2D() const;  
  52.    
  53.    virtual int kind() const;  
  54.    virtual Size size(int i=-1) const;  
  55.    virtual size_t total(int i=-1) const;  
  56.    virtual int type(int i=-1) const;  
  57.    virtual int depth(int i=-1) const;  
  58.    virtual int channels(int i=-1) const;  
  59.    virtual bool empty() const;  
  60.    
  61. #ifdefOPENCV_CAN_BREAK_BINARY_COMPATIBILITY  
  62.    virtual ~_InputArray();  
  63. #endif  
  64.    
  65.    int flags;  
  66.    void* obj;  
  67.    Size sz;  
  68. };  


可以看到,_InputArray類的裏面首先定義了一個枚舉,然後是各類的模板類型和一些方法。更復雜的我們暫且不挖深講了,很多時候,遇到函數原型中的InputArray類型,我們把它簡單地當做Mat類型就行了。

 

imshow 函數詳解:

imshow 函數用於在指定的窗口中顯示圖像。如果窗口是用CV_WINDOW_AUTOSIZE(默認值)標誌創建的,那麼顯示圖像原始大小。否則,將圖像進行縮放以適合窗口。而imshow 函數縮放圖像,取決於圖像的深度:

  • 如果載入的圖像是8位無符號類型(8-bit unsigned),就顯示圖像本來的樣子。
  • 如果圖像是16位無符號類型(16-bit unsigned)或32位整型(32-bit integer),便用像素值除以256。也就是說,值的範圍是[0,255 x 256]映射到[0,255]。
  • 如果圖像是32位浮點型(32-bit floating-point),像素值便要乘以255。也就是說,該值的範圍是[0,1]映射到[0,255]。


還有一點,若窗口創建(namedWindow函數)的時候,如果設定了支持OpenGL(WINDOW_OPENGL ),那麼imshow還支持ogl::Buffer ,ogl::Texture2D以及gpu::GpuMat作爲輸入。

 



 

四、輸出圖像到文件——imwrite函數


在OpenCV中,輸出圖像到文件,我們一般都用imwrite函數,它的聲明如下:

  1. bool imwrite(const string& filename,InputArray img, const vector<int>& params=vector<int>() );  

 

 ■ 第一個參數,const string&類型的filename,填需要寫入的文件名就行了,帶上後綴,比如,“123.jpg”這樣。

 ■ 第二個參數,InputArray類型的img,一般填一個Mat類型的圖像數據就行了。

 ■ 第三個參數,const vector<int>&類型的params,表示爲特定格式保存的參數編碼,它有默認值vector<int>(),所以一般情況下不需要填寫。而如果要填寫的話,有下面這些需要了解的地方:

 

    • 對於JPEG格式的圖片,這個參數表示從0到100的圖片質量(CV_IMWRITE_JPEG_QUALITY),默認值是95.
    • 對於PNG格式的圖片,這個參數表示壓縮級別(CV_IMWRITE_PNG_COMPRESSION)從0到9。較高的值意味着更小的尺寸和更長的壓縮時間,而默認值是3。
    • 對於PPM,PGM,或PBM格式的圖片,這個參數表示一個二進制格式標誌(CV_IMWRITE_PXM_BINARY),取值爲0或1,而默認值是1。

 

函數解析:

imwrite函數用於將圖像保存到指定的文件。圖像格式是基於文件擴展名的,可保存的擴展名和imread中可以讀取的圖像擴展名一樣,爲了方便查看,我們在這裏再列一遍:

    • Windows位圖 - *.bmp, *.dib
    • JPEG文件 - *.jpeg, *.jpg, *.jpe
    • JPEG 2000文件- *.jp2
    • PNG圖片 - *.png
    • 便攜文件格式- *.pbm, *.pgm, *.ppm
    • Sun rasters光柵格式 - *.sr, *.ras
    • TIFF 文件 - *.tiff, *.tif

  1. #include <vector>  
  2. #include <stdio.h>  
  3. #include<opencv2/opencv.hpp>  
  4.    
  5. using namespace cv;  
  6. using namespace std;  
  7.    
  8. void createAlphaMat(Mat &mat)  
  9. {  
  10. for(int i = 0; i < mat.rows; ++i) {  
  11.         for(int j = 0; j < mat.cols; ++j) {  
  12.                Vec4b&rgba = mat.at<Vec4b>(i, j);  
  13.                rgba[0]= UCHAR_MAX;  
  14.                rgba[1]= saturate_cast<uchar>((float (mat.cols - j)) / ((float)mat.cols) *UCHAR_MAX);  
  15.                rgba[2]= saturate_cast<uchar>((float (mat.rows - i)) / ((float)mat.rows) *UCHAR_MAX);  
  16.                rgba[3]= saturate_cast<uchar>(0.5 * (rgba[1] + rgba[2]));  
  17.         }  
  18. }  
  19. }  
  20.    
  21. int main( )  
  22. {  
  23. //創建帶alpha通道的Mat  
  24. Mat mat(480, 640, CV_8UC4);  
  25. createAlphaMat(mat);  
  26.    
  27. vector<int>compression_params;  
  28. compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);  
  29. compression_params.push_back(9);  
  30.    
  31. try{  
  32.         imwrite("透明Alpha值圖.png", mat, compression_params);  
  33. }  
  34. catch(runtime_error& ex) {  
  35.         fprintf(stderr,"圖像轉換成PNG格式發生錯誤:%s\n", ex.what());  
  36.         return1;  
  37. }  
  38.    
  39. fprintf(stdout,"PNG圖片文件的alpha數據保存完畢~\n");  
  40. return 0;  
  41. }  




五、一個綜合示例



最後是一個綜合示例,載入圖像,進行簡單圖像混合,顯示圖像,並且輸出混合後的圖像到jpg。

由於篇幅原因,這裏的圖像混合具體細節我們放到稍後的文章中再講,現在先給大家看看混合的效果和源碼。囧,因爲opencv圖像處理真的很少涉及到設計模式的問題,所以很多時候往往就是main函數中塞滿一串串代碼打天下,即便是OpenCV官方的示例都是如此。

好了,如下就是這篇文章配套綜合示例的配套源碼,非常的簡單明瞭:

 

  1. //-----------------------------------【程序說明】----------------------------------------------  
  2. //  程序名稱::【OpenCV入門教程之三】圖像的載入,顯示與輸出 一站式完全解析 博文配套源碼  
  3. // VS2010版   OpenCV版本:2.4.8  
  4. //      2014年3月5日 Create by 淺墨  
  5. //  描述: 圖像的載入,顯示與輸出 一站式剖析   配套源碼  
  6. //  圖片素材出處:dota2原畫聖堂刺客 dota2 logo  動漫人物  
  7. //------------------------------------------------------------------------------------------------  
  8.    
  9.    
  10. #include<opencv2/core/core.hpp>  
  11. #include<opencv2/highgui/highgui.hpp>  
  12.    
  13. using namespace cv;  
  14.    
  15.    
  16. int main( )  
  17. {  
  18. //-----------------------------------【一、圖像的載入和顯示】--------------------------------------  
  19. //     描述:以下三行代碼用於完成圖像的載入和顯示  
  20. //--------------------------------------------------------------------------------------------------  
  21.    
  22. Mat girl=imread("girl.jpg"); //載入圖像到Mat  
  23. namedWindow("【1】動漫圖"); //創建一個名爲 "【1】動漫圖"的窗口   
  24. imshow("【1】動漫圖",girl);//顯示名爲 "【1】動漫圖"的窗口   
  25.    
  26. //-----------------------------------【二、初級圖像混合】--------------------------------------  
  27. //     描述:二、初級圖像混合  
  28. //-----------------------------------------------------------------------------------------------  
  29. //載入圖片  
  30. Mat image= imread("dota.jpg",199);  
  31. Mat logo= imread("dota_logo.jpg");  
  32.    
  33. //載入後先顯示  
  34. namedWindow("【2】原畫圖");  
  35. imshow("【2】原畫圖",image);  
  36.    
  37. namedWindow("【3】logo圖");  
  38. imshow("【3】logo圖",logo);  
  39.    
  40. //定義一個Mat類型,用於存放,圖像的ROI  
  41. Mat imageROI;  
  42. //方法一  
  43. imageROI=image(Rect(800,350,logo.cols,logo.rows));  
  44. //方法二  
  45. //imageROI=image(Range(350,350+logo.rows),Range(800,800+logo.cols));  
  46.    
  47. //將logo加到原圖上  
  48. addWeighted(imageROI,0.5,logo,0.3,0.,imageROI);  
  49.    
  50. //顯示結果  
  51. namedWindow("【4】原畫+logo圖");  
  52. imshow("【4】原畫+logo圖",image);  
  53.    
  54. //-----------------------------------【三、圖像的輸出】--------------------------------------  
  55. //     描述:將一個Mat圖像輸出到圖像文件  
  56. //-----------------------------------------------------------------------------------------------  
  57. //輸出一張jpg圖片到工程目錄下  
  58. imwrite("我喜歡打dota2 by淺墨.jpg",image);  
  59.    
  60. waitKey();  
  61.    
  62. return 0;  
  63. }  



運行這個程序,會彈出四個我們在OpenCV中創建的窗口。

下面是運行截圖。首先是圖像載入和顯示的演示,我們載入了一張動漫人物圖:

 

 


接着是載入一張dota2原畫和dota2logo圖,爲圖像融合做準備:

 


logo圖:


最終,經過處理,得到dota2原畫+logo的融合,並輸出一張名爲我喜歡打dota2 by淺墨.jpg的圖片到工程目錄下。

 

 

 嗯,本篇文章到這裏就基本結束了,最後放出本篇文章配套示例程序的下載地址。


本篇文章的配套源代碼請點擊這裏下載:



【淺墨OpenCV入門教程之三】圖像的載入,顯示和輸出配套源代碼下載


OK,本節的內容大概就是這些,我們下篇文章見:)


本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。  

文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/20537737

作者:毛星雲(淺墨)    郵箱: [email protected] 

寫作當前博文時配套使用的OpenCV版本: 2.4.8


這篇文章中,我們將詳細而深入地弄懂入門OpenCV2最基本的問題,那就是圖像的載入,顯示和輸出。

PS:文章末尾提供了博文配套程序源代碼的下載。

依然是先看一張運行截圖:



瞭解過之前老版本OpenCV的童鞋們都應該清楚,對於OpenCV1.0時代的基於 C 語言接口而建的圖像存儲格式IplImage*,如果在退出前忘記release掉的話,就會照成內存泄露。而且用起來超級麻煩,我們往往在debug的時候,很大一部分時間在糾結手動釋放內存的問題。雖然對於小型的程序來說手動管理內存不是問題,但一旦我們寫的代碼變得越來越龐大,我們便會開始越來越多地糾纏於內存管理的問題,而不是着力解決你的開發目標。

這,就有些捨本逐末的感覺了。


而淺墨在這篇文章開頭想說,自從OpenCV踏入2.0時代,用Mat類數據結構作爲主打之後,OpenCV變得越發像需要很少編程涵養的Matlab那樣,上手超級快。甚至有些函數名稱都和matlab一樣,比如大家所熟知的imread,imwrite,imshow等函數。

這對於我們廣大圖像處理領域的孩子們來說,這的確是一個可喜可賀的事情。

 

這篇文章中,我們主要來詳細看一看入門OpenCV2最基本的問題,那就圖像的載入,顯示和輸出。


 


 

一、開胃菜之一 • 關於OpenCV的命名空間

 


OpenCV中的C++類和函數都是定義在命名空間cv之內的,有兩種方法可以訪問。第一種是,在代碼開頭的適當位置,加上usingnamespace cv;這句。

另外一種是在使用OpenCV類和函數時,都加入cv::命名空間。不過這種情況難免會不爽,每用一個OpenCV的類或者函數,都要多敲四下鍵盤寫出cv::,很麻煩。

所以,淺墨推崇大家在代碼開頭的適當位置,加上using namespace cv;這句。於是和opencv命名空間一了百了了。

 

 

比如淺墨,在寫簡單的OpenCV程序的時候,如下這三句是標配:

  1. #include <opencv2/core/core.hpp>  
  2. #include<opencv2/highgui/highgui.hpp>  
  3.    
  4. using namespace cv;  



 

二、開胃菜之二 • 關於Mat類型



cv::Mat類是用於保存圖像以及其他矩陣數據的數據結構。默認情況下,其尺寸爲0,我們也可以指定初始尺寸,比如,比如定義一個Mat類對象,就要寫cv::Mat pic(320,640,cv::Scalar(100));

 

Mat類型作爲OpenCV2新紀元的重要代表“人物”,淺墨準備在稍後的文章中,花長篇幅詳細講解它,現在我們只要理解,它是對應於OpenCV1.0時代的IplImage的主要用來存放圖像的數據結構就行了。對於這篇文章,我們需要用到關於Mat其實就簡單的這樣一句代碼:

  1. Mat myMat= imread("dota.jpg");  

 

表示從工程目錄下把一幅名爲dota.jpg的jpg類型的圖像載入到Mat類型的myMat中。這裏的imread函數這篇文章的下文就會詳細剖析到。

 

好吧,開胃菜就是這麼多了,下面來看看今天的主要內容,圖像的載入和顯示,處理圖像混合,設置感興趣區域以及如何輸出圖像,一項一項來擊破吧。

 


三、圖像的載入和顯示



在新版本的OpenCV2中,最簡單的圖像載入和顯示只需要3句代碼,非常便捷。這三句代碼分別對應了三個函數,他們分別是:

imread( ), namedWindow( )以及imshow( )。我們依次來解析一下這三個函數。

 

 

1.imread函數


首先,我們看imread函數,可以在OpenCV官方文檔中查到其原型如下:

 

  1. Mat imread(const string& filename, intflags=1 );  

 

■ 第一個參數,const string&類型的filename,填我們需要載入的圖片路徑名。

在Windows操作系統下,OpenCV的imread函數支持如下類型的圖像載入:

 

  • Windows位圖 - *.bmp, *.dib
  • JPEG文件 - *.jpeg, *.jpg, *.jpe
  • JPEG 2000文件- *.jp2
  • PNG圖片 - *.png
  • 便攜文件格式- *.pbm, *.pgm, *.ppm
  • Sun rasters光柵文件 - *.sr, *.ras
  • TIFF 文件 - *.tiff, *.tif

■ 第二個參數,int類型的flags,爲載入標識,它指定一個加載圖像的顏色類型。可以看到它自帶缺省值1.所以有時候這個參數在調用時我們可以忽略,在看了下面的講解之後,我們就會發現,如果在調用時忽略這個參數,就表示載入三通道的彩色圖像。

可以在OpenCV中標識圖像格式的枚舉體中取值。通過轉到定義,我們可以在higui_c.h中發現這個枚舉的定義是這樣的:

  1. enum  
  2. {  
  3. /* 8bit, color or not */  
  4.    CV_LOAD_IMAGE_UNCHANGED  =-1,  
  5. /* 8bit, gray */  
  6.    CV_LOAD_IMAGE_GRAYSCALE  =0,  
  7. /* ?, color */  
  8.    CV_LOAD_IMAGE_COLOR      =1,  
  9. /* any depth, ? */  
  10.    CV_LOAD_IMAGE_ANYDEPTH   =2,  
  11. /* ?, any color */  
  12.    CV_LOAD_IMAGE_ANYCOLOR   =4  
  13. };  

相應的解釋:

  • CV_LOAD_IMAGE_UNCHANGED,這個標識在新版本中被廢置了,忽略。
  • CV_LOAD_IMAGE_ANYDEPTH- 如果取這個標識的話,若載入的圖像的深度爲16位或者32位,就返回對應深度的圖像,否則,就轉換爲8位圖像再返回。
  • CV_LOAD_IMAGE_COLOR- 如果取這個標識的話,總是轉換圖像到彩色一體
  • CV_LOAD_IMAGE_GRAYSCALE- 如果取這個標識的話,始終將圖像轉換成灰度1

 

如果輸入有衝突的標誌,將採用較小的數字值。比如CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYCOLOR 將載入3通道圖。

如果想要載入最真實的圖像,選擇CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR。

 

因爲flags是int型的變量,如果我們不在這個枚舉體中取值的話,還可以這樣來:

  • flags >0返回一個3通道的彩色圖像。
  • flags =0返回灰度圖像。
  • flags <0返回包含Alpha通道的加載的圖像。

需要注意的點:輸出的圖像默認情況下是不載入Alpha通道進來的。如果我們需要載入Alpha通道的話呢,這裏就需要取負值。

 

如果你搞怪,flags取1999,也是可以的,這時就表示返回一個3通道的彩色圖像。

 

 

好了,講了這麼多,來幾個載入示例,一看就懂:

 

  1. Mat image0=imread("dota.jpg",CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);//載入最真實的圖像  
  2. ge1=imread("dota.jpg",0);//載入灰度圖  
  3. Mat image2=imread("dota.jpg",199);//載入3通道的彩色圖像  
  4. Mat logo=imread("dota_logo.jpg");//載入3通道的彩色圖像  



2.namedWindow函數


顧名思義,namedWindow函數,用於創建一個窗口。

函數原型是這樣的:

  1. void namedWindow(const string& winname,int flags=WINDOW_AUTOSIZE );   

      ■ 第一個參數,const string&型的name,即填被用作窗口的標識符的窗口名稱。

      ■ 第二個參數,int 類型的flags ,窗口的標識,可以填如下的值:

  • WINDOW_NORMAL設置了這個值,用戶便可以改變窗口的大小(沒有限制)
  • WINDOW_AUTOSIZE如果設置了這個值,窗口大小會自動調整以適應所顯示的圖像,並且不能手動改變窗口大小。
  • WINDOW_OPENGL 如果設置了這個值的話,窗口創建的時候便會支持OpenGL。

函數剖析:

首先需要注意的是,它有默認值WINDOW_AUTOSIZE,所以,一般情況下,這個函數我們填一個變量就行了。

namedWindow函數的作用是,通過指定的名字,創建一個可以作爲圖像和進度條的容器窗口。如果具有相同名稱的窗口已經存在,則函數不做任何事情。

我們可以調用destroyWindow()或者destroyAllWindows()函數來關閉窗口,並取消之前分配的與窗口相關的所有內存空間。

但話是這樣說,其實對於代碼量不大的簡單小程序來說,我們完全沒有必要手動調用上述的destroyWindow()或者destroyAllWindows()函數,因爲在退出時,所有的資源和應用程序的窗口會被操作系統會自動關閉。

 

3.imshow函數


在指定的窗口中顯示一幅圖像。

  1. void imshow(const string& winname, InputArray mat);  

 ■ 第一個參數,const string&類型的winname,填需要顯示的窗口標識名稱。

 ■ 第二個參數,InputArray 類型的mat,填需要顯示的圖像。

 

這裏的InputArray 我們講一下吧,不然一直是個梗在這邊。通過轉到定義大法,我們可以在

Highgui.hpp中查到imshow的原型:

 

  1. CV_EXPORTS_W void imshow(const string&winname, InputArray mat);  

 

進一步對InputArray轉到定義,在core.hpp中查到一個typedef聲明:

  1. typedef const _InputArray& InputArray;  

這其實一個類型聲明引用,就是說_InputArray和InputArray是一個意思,既然他們是一個意思,我們就來做最後一步,對_InputArray進行轉到定義,終於,我們在core.hpp發現了InputArray的真身:


  1. class CV_EXPORTS _InputArray  
  2. {  
  3. public:  
  4.    enum {  
  5.        KIND_SHIFT = 16,  
  6.         FIXED_TYPE = 0x8000 << KIND_SHIFT,  
  7.        FIXED_SIZE = 0x4000 << KIND_SHIFT,  
  8.        KIND_MASK = ~(FIXED_TYPE|FIXED_SIZE) - (1 << KIND_SHIFT) + 1,  
  9.    
  10.        NONE              = 0 <<KIND_SHIFT,  
  11.        MAT               = 1 <<KIND_SHIFT,  
  12.        MATX              = 2 <<KIND_SHIFT,  
  13.        STD_VECTOR        = 3 <<KIND_SHIFT,  
  14.        STD_VECTOR_VECTOR = 4 << KIND_SHIFT,  
  15.        STD_VECTOR_MAT    = 5 <<KIND_SHIFT,  
  16.        EXPR              = 6 <<KIND_SHIFT,  
  17.        OPENGL_BUFFER     = 7 <<KIND_SHIFT,  
  18.        OPENGL_TEXTURE    = 8 <<KIND_SHIFT,  
  19.        GPU_MAT           = 9 <<KIND_SHIFT,  
  20.        OCL_MAT           =10 <<KIND_SHIFT  
  21.    };  
  22.    _InputArray();  
  23.    
  24.    _InputArray(const Mat& m);  
  25.    _InputArray(const MatExpr& expr);  
  26.    template<typename _Tp> _InputArray(const _Tp* vec, int n);  
  27.    template<typename _Tp> _InputArray(const vector<_Tp>&vec);  
  28.    template<typename _Tp> _InputArray(constvector<vector<_Tp> >& vec);  
  29.    _InputArray(const vector<Mat>& vec);  
  30.    template<typename _Tp> _InputArray(const vector<Mat_<_Tp>>& vec);  
  31.    template<typename _Tp> _InputArray(const Mat_<_Tp>& m);  
  32.    template<typename _Tp, int m, int n> _InputArray(constMatx<_Tp, m, n>& matx);  
  33.    _InputArray(const Scalar& s);  
  34.    _InputArray(const double& val);  
  35.    // < Deprecated  
  36.    _InputArray(const GlBuffer& buf);  
  37.    _InputArray(const GlTexture& tex);  
  38.    // >  
  39.    _InputArray(const gpu::GpuMat& d_mat);  
  40.    _InputArray(const ogl::Buffer& buf);  
  41.    _InputArray(const ogl::Texture2D& tex);  
  42.    
  43.    virtual Mat getMat(int i=-1) const;  
  44.    virtual void getMatVector(vector<Mat>& mv) const;  
  45.    // < Deprecated  
  46.    virtual GlBuffer getGlBuffer() const;  
  47.    virtual GlTexture getGlTexture() const;  
  48.    // >  
  49.    virtual gpu::GpuMat getGpuMat() const;  
  50.    /*virtual*/ ogl::Buffer getOGlBuffer() const;  
  51.    /*virtual*/ ogl::Texture2D getOGlTexture2D() const;  
  52.    
  53.    virtual int kind() const;  
  54.    virtual Size size(int i=-1) const;  
  55.    virtual size_t total(int i=-1) const;  
  56.    virtual int type(int i=-1) const;  
  57.    virtual int depth(int i=-1) const;  
  58.    virtual int channels(int i=-1) const;  
  59.    virtual bool empty() const;  
  60.    
  61. #ifdefOPENCV_CAN_BREAK_BINARY_COMPATIBILITY  
  62.    virtual ~_InputArray();  
  63. #endif  
  64.    
  65.    int flags;  
  66.    void* obj;  
  67.    Size sz;  
  68. };  


可以看到,_InputArray類的裏面首先定義了一個枚舉,然後是各類的模板類型和一些方法。更復雜的我們暫且不挖深講了,很多時候,遇到函數原型中的InputArray類型,我們把它簡單地當做Mat類型就行了。

 

imshow 函數詳解:

imshow 函數用於在指定的窗口中顯示圖像。如果窗口是用CV_WINDOW_AUTOSIZE(默認值)標誌創建的,那麼顯示圖像原始大小。否則,將圖像進行縮放以適合窗口。而imshow 函數縮放圖像,取決於圖像的深度:

  • 如果載入的圖像是8位無符號類型(8-bit unsigned),就顯示圖像本來的樣子。
  • 如果圖像是16位無符號類型(16-bit unsigned)或32位整型(32-bit integer),便用像素值除以256。也就是說,值的範圍是[0,255 x 256]映射到[0,255]。
  • 如果圖像是32位浮點型(32-bit floating-point),像素值便要乘以255。也就是說,該值的範圍是[0,1]映射到[0,255]。


還有一點,若窗口創建(namedWindow函數)的時候,如果設定了支持OpenGL(WINDOW_OPENGL ),那麼imshow還支持ogl::Buffer ,ogl::Texture2D以及gpu::GpuMat作爲輸入。

 



 

四、輸出圖像到文件——imwrite函數


在OpenCV中,輸出圖像到文件,我們一般都用imwrite函數,它的聲明如下:

  1. bool imwrite(const string& filename,InputArray img, const vector<int>& params=vector<int>() );  

 

 ■ 第一個參數,const string&類型的filename,填需要寫入的文件名就行了,帶上後綴,比如,“123.jpg”這樣。

 ■ 第二個參數,InputArray類型的img,一般填一個Mat類型的圖像數據就行了。

 ■ 第三個參數,const vector<int>&類型的params,表示爲特定格式保存的參數編碼,它有默認值vector<int>(),所以一般情況下不需要填寫。而如果要填寫的話,有下面這些需要了解的地方:

 

    • 對於JPEG格式的圖片,這個參數表示從0到100的圖片質量(CV_IMWRITE_JPEG_QUALITY),默認值是95.
    • 對於PNG格式的圖片,這個參數表示壓縮級別(CV_IMWRITE_PNG_COMPRESSION)從0到9。較高的值意味着更小的尺寸和更長的壓縮時間,而默認值是3。
    • 對於PPM,PGM,或PBM格式的圖片,這個參數表示一個二進制格式標誌(CV_IMWRITE_PXM_BINARY),取值爲0或1,而默認值是1。

 

函數解析:

imwrite函數用於將圖像保存到指定的文件。圖像格式是基於文件擴展名的,可保存的擴展名和imread中可以讀取的圖像擴展名一樣,爲了方便查看,我們在這裏再列一遍:

    • Windows位圖 - *.bmp, *.dib
    • JPEG文件 - *.jpeg, *.jpg, *.jpe
    • JPEG 2000文件- *.jp2
    • PNG圖片 - *.png
    • 便攜文件格式- *.pbm, *.pgm, *.ppm
    • Sun rasters光柵格式 - *.sr, *.ras
    • TIFF 文件 - *.tiff, *.tif

  1. #include <vector>  
  2. #include <stdio.h>  
  3. #include<opencv2/opencv.hpp>  
  4.    
  5. using namespace cv;  
  6. using namespace std;  
  7.    
  8. void createAlphaMat(Mat &mat)  
  9. {  
  10. for(int i = 0; i < mat.rows; ++i) {  
  11.         for(int j = 0; j < mat.cols; ++j) {  
  12.                Vec4b&rgba = mat.at<Vec4b>(i, j);  
  13.                rgba[0]= UCHAR_MAX;  
  14.                rgba[1]= saturate_cast<uchar>((float (mat.cols - j)) / ((float)mat.cols) *UCHAR_MAX);  
  15.                rgba[2]= saturate_cast<uchar>((float (mat.rows - i)) / ((float)mat.rows) *UCHAR_MAX);  
  16.                rgba[3]= saturate_cast<uchar>(0.5 * (rgba[1] + rgba[2]));  
  17.         }  
  18. }  
  19. }  
  20.    
  21. int main( )  
  22. {  
  23. //創建帶alpha通道的Mat  
  24. Mat mat(480, 640, CV_8UC4);  
  25. createAlphaMat(mat);  
  26.    
  27. vector<int>compression_params;  
  28. compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);  
  29. compression_params.push_back(9);  
  30.    
  31. try{  
  32.         imwrite("透明Alpha值圖.png", mat, compression_params);  
  33. }  
  34. catch(runtime_error& ex) {  
  35.         fprintf(stderr,"圖像轉換成PNG格式發生錯誤:%s\n", ex.what());  
  36.         return1;  
  37. }  
  38.    
  39. fprintf(stdout,"PNG圖片文件的alpha數據保存完畢~\n");  
  40. return 0;  
  41. }  




五、一個綜合示例



最後是一個綜合示例,載入圖像,進行簡單圖像混合,顯示圖像,並且輸出混合後的圖像到jpg。

由於篇幅原因,這裏的圖像混合具體細節我們放到稍後的文章中再講,現在先給大家看看混合的效果和源碼。囧,因爲opencv圖像處理真的很少涉及到設計模式的問題,所以很多時候往往就是main函數中塞滿一串串代碼打天下,即便是OpenCV官方的示例都是如此。

好了,如下就是這篇文章配套綜合示例的配套源碼,非常的簡單明瞭:

 

  1. //-----------------------------------【程序說明】----------------------------------------------  
  2. //  程序名稱::【OpenCV入門教程之三】圖像的載入,顯示與輸出 一站式完全解析 博文配套源碼  
  3. // VS2010版   OpenCV版本:2.4.8  
  4. //      2014年3月5日 Create by 淺墨  
  5. //  描述: 圖像的載入,顯示與輸出 一站式剖析   配套源碼  
  6. //  圖片素材出處:dota2原畫聖堂刺客 dota2 logo  動漫人物  
  7. //------------------------------------------------------------------------------------------------  
  8.    
  9.    
  10. #include<opencv2/core/core.hpp>  
  11. #include<opencv2/highgui/highgui.hpp>  
  12.    
  13. using namespace cv;  
  14.    
  15.    
  16. int main( )  
  17. {  
  18. //-----------------------------------【一、圖像的載入和顯示】--------------------------------------  
  19. //     描述:以下三行代碼用於完成圖像的載入和顯示  
  20. //--------------------------------------------------------------------------------------------------  
  21.    
  22. Mat girl=imread("girl.jpg"); //載入圖像到Mat  
  23. namedWindow("【1】動漫圖"); //創建一個名爲 "【1】動漫圖"的窗口   
  24. imshow("【1】動漫圖",girl);//顯示名爲 "【1】動漫圖"的窗口   
  25.    
  26. //-----------------------------------【二、初級圖像混合】--------------------------------------  
  27. //     描述:二、初級圖像混合  
  28. //-----------------------------------------------------------------------------------------------  
  29. //載入圖片  
  30. Mat image= imread("dota.jpg",199);  
  31. Mat logo= imread("dota_logo.jpg");  
  32.    
  33. //載入後先顯示  
  34. namedWindow("【2】原畫圖");  
  35. imshow("【2】原畫圖",image);  
  36.    
  37. namedWindow("【3】logo圖");  
  38. imshow("【3】logo圖",logo);  
  39.    
  40. //定義一個Mat類型,用於存放,圖像的ROI  
  41. Mat imageROI;  
  42. //方法一  
  43. imageROI=image(Rect(800,350,logo.cols,logo.rows));  
  44. //方法二  
  45. //imageROI=image(Range(350,350+logo.rows),Range(800,800+logo.cols));  
  46.    
  47. //將logo加到原圖上  
  48. addWeighted(imageROI,0.5,logo,0.3,0.,imageROI);  
  49.    
  50. //顯示結果  
  51. namedWindow("【4】原畫+logo圖");  
  52. imshow("【4】原畫+logo圖",image);  
  53.    
  54. //-----------------------------------【三、圖像的輸出】--------------------------------------  
  55. //     描述:將一個Mat圖像輸出到圖像文件  
  56. //-----------------------------------------------------------------------------------------------  
  57. //輸出一張jpg圖片到工程目錄下  
  58. imwrite("我喜歡打dota2 by淺墨.jpg",image);  
  59.    
  60. waitKey();  
  61.    
  62. return 0;  
  63. }  



運行這個程序,會彈出四個我們在OpenCV中創建的窗口。

下面是運行截圖。首先是圖像載入和顯示的演示,我們載入了一張動漫人物圖:

 

 


接着是載入一張dota2原畫和dota2logo圖,爲圖像融合做準備:

 


logo圖:


最終,經過處理,得到dota2原畫+logo的融合,並輸出一張名爲我喜歡打dota2 by淺墨.jpg的圖片到工程目錄下。

 

 

 嗯,本篇文章到這裏就基本結束了,最後放出本篇文章配套示例程序的下載地址。


本篇文章的配套源代碼請點擊這裏下載:



【淺墨OpenCV入門教程之三】圖像的載入,顯示和輸出配套源代碼下載


OK,本節的內容大概就是這些,我們下篇文章見:)

發佈了1 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章