转载:【opencv入门教程之三】:图片的载入|显示|输出
本系列文章由@淺墨_毛星云 出品,轉載請注明出處。??
文章鏈接:?http://blog.csdn.net/poem_qianmo/article/details/20537737
作者:毛星云(淺墨)????郵箱:?happylifemxy@163.com?
寫作當前博文時配套使用的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程序的時候,如下這三句是標配:
?
二、開胃菜之二???關于Mat類型
cv::Mat類是用于保存圖像以及其他矩陣數據的數據結構。默認情況下,其尺寸為0,我們也可以指定初始尺寸,比如,比如定義一個Mat類對象,就要寫cv::Mat pic(320,640,cv::Scalar(100));
?
Mat類型作為OpenCV2新紀元的重要代表“人物”,淺墨準備在稍后的文章中,花長篇幅詳細講解它,現在我們只要理解,它是對應于OpenCV1.0時代的IplImage的主要用來存放圖像的數據結構就行了。對于這篇文章,我們需要用到關于Mat其實就簡單的這樣一句代碼:
Mat myMat= imread("dota.jpg");?
表示從工程目錄下把一幅名為dota.jpg的jpg類型的圖像載入到Mat類型的myMat中。這里的imread函數這篇文章的下文就會詳細剖析到。
?
好吧,開胃菜就是這么多了,下面來看看今天的主要內容,圖像的載入和顯示,處理圖像混合,設置感興趣區域以及如何輸出圖像,一項一項來擊破吧。
?
三、圖像的載入和顯示
在新版本的OpenCV2中,最簡單的圖像載入和顯示只需要3句代碼,非常便捷。這三句代碼分別對應了三個函數,他們分別是:
imread( ), namedWindow( )以及imshow( )。我們依次來解析一下這三個函數。
?
?
1.imread函數
首先,我們看imread函數,可以在OpenCV官方文檔中查到其原型如下:
?
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中發現這個枚舉的定義是這樣的:
相應的解釋:
- 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通道的彩色圖像。
?
?
好了,講了這么多,來幾個載入示例,一看就懂:
?
2.namedWindow函數
顧名思義,namedWindow函數,用于創建一個窗口。
函數原型是這樣的:
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函數
在指定的窗口中顯示一幅圖像。
void imshow(const string& winname, InputArray mat);?■?第一個參數,const string&類型的winname,填需要顯示的窗口標識名稱。
?■?第二個參數,InputArray?類型的mat,填需要顯示的圖像。
?
這里的InputArray?我們講一下吧,不然一直是個梗在這邊。通過轉到定義大法,我們可以在
Highgui.hpp中查到imshow的原型:
?
CV_EXPORTS_W void imshow(const string&winname, InputArray mat);?
進一步對InputArray轉到定義,在core.hpp中查到一個typedef聲明:
typedef const _InputArray& InputArray;這其實一個類型聲明引用,就是說_InputArray和InputArray是一個意思,既然他們是一個意思,我們就來做最后一步,對_InputArray進行轉到定義,終于,我們在core.hpp發現了InputArray的真身:
可以看到,_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函數,它的聲明如下:
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
五、一個綜合示例
最后是一個綜合示例,載入圖像,進行簡單圖像混合,顯示圖像,并且輸出混合后的圖像到jpg。
由于篇幅原因,這里的圖像混合具體細節我們放到稍后的文章中再講,現在先給大家看看混合的效果和源碼。囧,因為opencv圖像處理真的很少涉及到設計模式的問題,所以很多時候往往就是main函數中塞滿一串串代碼打天下,即便是OpenCV官方的示例都是如此。
好了,如下就是這篇文章配套綜合示例的配套源碼,非常的簡單明了:
?
運行這個程序,會彈出四個我們在OpenCV中創建的窗口。
下面是運行截圖。首先是圖像載入和顯示的演示,我們載入了一張動漫人物圖:
?
?
接著是載入一張dota2原畫和dota2logo圖,為圖像融合做準備:
?
logo圖:
最終,經過處理,得到dota2原畫+logo的融合,并輸出一張名為我喜歡打dota2 by淺墨.jpg的圖片到工程目錄下。
?
?
?嗯,本篇文章到這里就基本結束了,最后放出本篇文章配套示例程序的下載地址。
本篇文章的配套源代碼請點擊這里下載:
【淺墨OpenCV入門教程之三】圖像的載入,顯示和輸出配套源代碼下載
OK,本節的內容大概就是這些,我們下篇文章見:)
?
淺墨_毛星云 博客專家 原創文章 159獲贊 9891訪問量 755萬+ 已關注 他的留言板總結
以上是生活随笔為你收集整理的转载:【opencv入门教程之三】:图片的载入|显示|输出的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转载:【opencv入门教程之三】:组件
- 下一篇: 转载:【OpenCV入门教程之四】 RO