OpenCV之Python學習筆記
一直都在用Python+OpenCV做一些算法的原型。本來想留下發布一些文章的,可是整理一下就有點無奈了,都是寫零散不成系統的小片段。現在看到一本國外的新書《OpenCV Computer Vision with Python》,于是就看一遍,順便把自己掌握的東西整合一下,寫成學習筆記了。更需要的朋友參考。
閱讀須知:
??????? 本文不是純粹的譯文,只是比較貼近原文的筆記; ??????? 請設法購買到出版社出版的書,支持正版。
?????? 從書名就能看出來本書是介紹在Python中使用OpenCV,全書分為5章,兩個附錄:
第一章OpenCV設置 ,介紹如何在Windows、Mac和Ubuntu上設置Pyhton、OpenCV和相關庫的環境。還討論了OpenCV社區、OpenCV文檔以及官方的示例代碼。 第二章處理文件、攝像頭和GUI ,討論OpenCV的I/O功能,接著使用面向對象的設計編寫一個主應用程序,用于顯示攝像頭實時場景、處理鍵盤輸入、將攝像頭寫入視頻文件和靜態圖像文件。 第三章圖像過濾 ,介紹使用OpenCV、NumPy和SciPy來編寫圖像過濾器。過濾器可用于線性顏色操作、曲線顏色操作、模糊化、銳化和尋找邊緣。本章修改第一章的主程序,將過濾器應用到實時攝像頭場景中。 第四章使用Haar Cascades追蹤人臉 ,本章將編寫一個層次化的人臉追蹤器,使用OpenCV定位圖像中的臉部、眼睛、鼻子和嘴巴。同時還編寫了用于復制和改變圖像中某塊區域的大小。同樣,本章也將修改之前的主應用程序,讓其可以用于找到并處理攝像頭場景中的人臉。 第五章檢測前景/背景區域和深度 。通過本章將了解有關OpenCV(在OpenNI和SensorKinect的支持下)從深度攝像頭中獲得的數據類型的信息。接著編寫一些函數,使用這些數據對前景區域施加一些限制效果。最后將這些函數整合到主程序中,使得在處理人臉之前先進行細化操作。 附錄A,與Pygame整合 。修改主程序,用Pygame替換OpenCV來處理特定的I/O事件。(Pygame提供了更多樣的事件處理函數。) 附錄B,為自定義目標生成Haar Cascades ,允許我們檢測一系列的OpenCV工具,來對任何類型的目標或模式構建跟蹤器,而不僅僅是人臉。
本書第一章是介紹在不同操作系統上對OpenCV、Python及相關庫的配置,這里就不介紹了。下一篇文章將直接從第二章開始介紹。
OpenCV Python教程(1、圖像的載入、顯示和保存)
本文是OpenCV? 2 Computer Vision Application Programming Cookbook讀書筆記的第一篇。在筆記中將以Python語言改寫每章的代碼。
PythonOpenCV的配置這里就不介紹了。
注意,現在OpenCV for Python就是通過NumPy進行綁定的。所以在使用時必須掌握一些NumPy的相關知識!
圖像就是一個矩陣,在OpenCV for Python中,圖像就是NumPy中的數組!
如果讀取圖像首先要導入OpenCV包,方法為:
[python] ?view plaincopy
import ?cv2??
讀取并顯示圖像
在Python中不需要聲明變量,所以也就不需要C++中的cv::Mat xxxxx了。只需這樣:
[python] ?view plaincopy
img?=?cv2.imread( "D:\cat.jpg" )??
OpenCV目前支持讀取bmp、jpg、png、tiff等常用格式。更詳細的請參考OpenCV的參考文檔。
接著創建一個窗口
[python] ?view plaincopy
cv2.namedWindow( "Image" )??
然后在窗口中顯示圖像
[python] ?view plaincopy
cv2.imshow( "Image" ,?img)??
最后還要添上一句:
[python] ?view plaincopy
cv2.waitKey?( 0 )??
如果不添最后一句,在IDLE中執行窗口直接無響應。在命令行中執行的話,則是一閃而過。
完整的程序為:
[python] ?view plaincopy
import ?cv2??? ?? img?=?cv2.imread("D:\\cat.jpg" )??? cv2.namedWindow("Image" )??? cv2.imshow("Image" ,?img)??? cv2.waitKey?(0 )?? cv2.destroyAllWindows()??
最后釋放窗口是個好習慣!
創建/復制圖像
新的OpenCV的接口中沒有CreateImage接口。即沒有cv2.CreateImage這樣的函數。如果要創建圖像,需要使用numpy的函數(現在使用OpenCV-Python綁定,numpy是必裝的)。如下:
[python] ?view plaincopy
emptyImage?=?np.zeros(img.shape,?np.uint8)??
在新的OpenCV-Python綁定中,圖像使用NumPy數組的屬性來表示圖像的尺寸和通道信息。如果輸出img.shape,將得到(500, 375, 3),這里是以OpenCV自帶的cat.jpg為示例。最后的3表示這是一個RGB圖像。
也可以復制原有的圖像來獲得一副新圖像。
[python] ?view plaincopy
emptyImage2?=?img.copy();??
如果不怕麻煩,還可以用cvtColor獲得原圖像的副本。
[python] ?view plaincopy
emptyImage3=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)?? ??
后面的emptyImage3[...]=0是將其轉成空白的黑色圖像。
保存圖像?
保存圖像很簡單,直接用cv2.imwrite即可。
cv2.imwrite("D:\\cat2.jpg", img)
第一個參數是保存的路徑及文件名,第二個是圖像矩陣。其中,imwrite()有個可選的第三個參數,如下:
cv2.imwrite("D:\\cat2.jpg", img,[int(cv2.IMWRITE_JPEG_QUALITY), 5])
第三個參數針對特定的格式: 對于JPEG,其表示的是圖像的質量,用0-100的整數表示,默認為95。 注意,cv2.IMWRITE_JPEG_QUALITY類型為Long,必須轉換成int。下面是以不同質量存儲的兩幅圖:
對于PNG,第三個參數表示的是壓縮級別。cv2.IMWRITE_PNG_COMPRESSION,從0到9,壓縮級別越高,圖像尺寸越小。默認級別為3:
[python] ?view plaincopy
cv2.imwrite( "./cat.png" ,?img,?[int(cv2.IMWRITE_PNG_COMPRESSION),? 0 ])??? cv2.imwrite("./cat2.png" ,?img,?[int(cv2.IMWRITE_PNG_COMPRESSION),? 9 ])??
保存的圖像尺寸如下:
還有一種支持的圖像,一般不常用。
完整的代碼為:
[python] ?view plaincopy
import ?cv2?? import ?numpy?as?np?? ?? img?=?cv2.imread("./cat.jpg" )?? emptyImage?=?np.zeros(img.shape,?np.uint8)?? ?? emptyImage2?=?img.copy()?? ?? emptyImage3=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)?? ?? ?? cv2.imshow("EmptyImage" ,?emptyImage)?? cv2.imshow("Image" ,?img)?? cv2.imshow("EmptyImage2" ,?emptyImage2)?? cv2.imshow("EmptyImage3" ,?emptyImage3)?? cv2.imwrite("./cat2.jpg" ,?img,?[int(cv2.IMWRITE_JPEG_QUALITY),? 5 ])?? cv2.imwrite("./cat3.jpg" ,?img,?[int(cv2.IMWRITE_JPEG_QUALITY),? 100 ])?? cv2.imwrite("./cat.png" ,?img,?[int(cv2.IMWRITE_PNG_COMPRESSION),? 0 ])?? cv2.imwrite("./cat2.png" ,?img,?[int(cv2.IMWRITE_PNG_COMPRESSION),? 9 ])?? cv2.waitKey?(0 )?? cv2.destroyAllWindows()??
參考資料:
《OpenCV References Manuel》
《OpenCV? 2 Computer Vision Application Programming Cookbook》
《OpenCV Computer Vision with Python》
OpenCV Python教程(2、圖像元素的訪問、通道分離與合并)
OpenCV Python教程之圖像元素的訪問、通道分離與合并
轉載請詳細注明原作者及出處,謝謝!
訪問像素
像素的訪問和訪問numpy中ndarray的方法完全一樣,灰度圖為:
[python] ?view plaincopy
img[j,i]?=? 255 ??
其中j,i分別表示圖像的行和列。對于BGR圖像,為:
[python] ?view plaincopy
img[j,i, 0 ]=? 255 ?? img[j,i,1 ]=? 255 ?? img[j,i,2 ]=? 255 ??
第三個數表示通道。
下面通過對圖像添加人工的椒鹽現象來進一步說明OpenCV?Python中需要注意的一些問題。完整代碼如下:
[python] ?view plaincopy
import ?cv2?? import ?numpy?as?np?? ?? def ?salt(img,?n):?? ????for ?k? in ?range(n):?? ????????i?=?int(np.random.random()?*?img.shape[1 ]);?? ????????j?=?int(np.random.random()?*?img.shape[0 ]);?? ????????if ?img.ndim?==? 2 :??? ????????????img[j,i]?=?255 ?? ????????elif ?img.ndim?==? 3 :??? ????????????img[j,i,0 ]=? 255 ?? ????????????img[j,i,1 ]=? 255 ?? ????????????img[j,i,2 ]=? 255 ?? ????return ?img?? ?? if ?__name__?==? '__main__' :?? ????img?=?cv2.imread("圖像路徑" )?? ????saltImage?=?salt(img,?500 )?? ????cv2.imshow("Salt" ,?saltImage)?? ????cv2.waitKey(0 )?? ????cv2.destroyAllWindows()??
處理后能得到類似下面這樣帶有模擬椒鹽現象的圖片:
上面的代碼需要注意幾點:
1、與C++不同,在Python中灰度圖的img.ndim = 2,而C++中灰度圖圖像的通道數img.channel() =1
2、為什么使用np.random.random()? 這里使用了numpy的隨機數,Python自身也有一個隨機數生成函數。這里只是一種習慣,np.random模塊中擁有更多的方法,而Python自帶的random只是一個輕量級的模塊。不過需要注意的是np.random.seed()不是線程安全的,而Python自帶的random.seed()是線程安全的。如果使用隨機數時需要用到多線程,建議使用Python自帶的random()和random.seed(),或者構建一個本地的np.random.Random類的實例。
分離、合并通道
由于OpenCV Python和NumPy結合的很緊,所以即可以使用OpenCV自帶的split函數,也可以直接操作numpy數組來分離通道。直接法為:
[python] ?view plaincopy
import ?cv2?? import ?numpy?as?np?? ?? img?=?cv2.imread("D:/cat.jpg" )?? b,?g,?r?=?cv2.split(img)?? cv2.imshow("Blue" ,?r)?? cv2.imshow("Red" ,?g)?? cv2.imshow("Green" ,?b)?? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
其中split返回RGB三個通道,如果只想返回其中一個通道,可以這樣:
[python] ?view plaincopy
b?=?cv2.split(img)[ 0 ]?? g?=?cv2.split(img)[1 ]?? r?=?cv2.split(img)[2 ]??
最后的索引指出所需要的通道。
也可以直接操作NumPy數組來達到這一目的:
[python] ?view plaincopy
import ?cv2?? import ?numpy?as?np?? ?? img?=?cv2.imread("D:/cat.jpg" )?? ?? b?=?np.zeros((img.shape[0 ],img.shape[ 1 ]),?dtype=img.dtype)?? g?=?np.zeros((img.shape[0 ],img.shape[ 1 ]),?dtype=img.dtype)?? r?=?np.zeros((img.shape[0 ],img.shape[ 1 ]),?dtype=img.dtype)?? ?? b[:,:]?=?img[:,:,0 ]?? g[:,:]?=?img[:,:,1 ]?? r[:,:]?=?img[:,:,2 ]?? ?? cv2.imshow("Blue" ,?r)?? cv2.imshow("Red" ,?g)?? cv2.imshow("Green" ,?b)?? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
注意先要開辟一個相同大小的圖片出來。這是由于numpy中數組的復制有些需要注意的地方,具體事例如下:
[python] ?view plaincopy
>>>?c=?np.zeros(img.shape,?dtype=img.dtype)?? >>>?c[:,:,:]?=?img[:,:,:]?? >>>?d[:,:,:]?=?img[:,:,:]?? >>>?c?is ?a?? False ?? >>>?d?is ?a?? False ?? >>>?c.base?is ?a?? False ?? >>>?d.base?is ?a? ?? True ??
這里,d只是a的鏡像,具體請參考《
NumPy簡明教程(二,數組3
)》中的“復制和鏡像”一節。
通道合并
同樣,通道合并也有兩種方法。第一種是OpenCV自帶的merge函數,如下:
[python] ?view plaincopy
merged?=?cv2.merge([b,g,r])? ??
接著是NumPy的方法:
[python] ?view plaincopy
mergedByNp?=?np.dstack([b,g,r])???
注意:這里只是演示,實際使用時請用OpenCV自帶的merge函數!
用NumPy組合的結果不能在OpenCV中其他函數使用,因為其組合方式與OpenCV自帶的不一樣,如下:
[python] ?view plaincopy
merged?=?cv2.merge([b,g,r])?? print ? "Merge?by?OpenCV" ??? print ?merged.strides?? ?? mergedByNp?=?np.dstack([b,g,r])??? print ? "Merge?by?NumPy?" ??? print ?mergedByNp.strides??
結果為:
[python] ?view plaincopy
Merge?by?OpenCV?? (1125 ,? 3 ,? 1 )?? Merge?by?NumPy?? (1 ,? 500 ,? 187500 )??
NumPy數組的strides屬性表示的是在每個維數上以字節計算的步長。這怎么理解呢,看下面這個簡單點的例子:
[python] ?view plaincopy
>>>?a?=?np.arange( 6 )?? >>>?a?? array([0 ,? 1 ,? 2 ,? 3 ,? 4 ,? 5 ])?? >>>?a.strides?? (4 ,)??
a數組中每個元素都是NumPy中的整數類型,占4個字節,所以第一維中相鄰元素之間的步長為4(個字節)。
同樣,2維數組如下:
[python] ?view plaincopy
>>>?b?=?np.arange( 12 ).reshape( 3 , 4 )?? >>>?b?? array([[?0 ,?? 1 ,?? 2 ,?? 3 ],?? ???????[?4 ,?? 5 ,?? 6 ,?? 7 ],?? ???????[?8 ,?? 9 ,? 10 ,? 11 ]])?? >>>?b.strides?? (16 ,? 4 )??
從里面開始看,里面是一個4個元素的一維整數數組,所以步長應該為4。外面是一個含有3個元素,每個元素的長度是4×4=16。所以步長為16。
下面來看下3維數組:
[python] ?view plaincopy
>>>?c?=?np.arange( 27 ).reshape( 3 , 3 , 3 )??
其結果為:
[python] ?view plaincopy
array([[[? 0 ,?? 1 ,?? 2 ],?? ????????[?3 ,?? 4 ,?? 5 ],?? ????????[?6 ,?? 7 ,?? 8 ]],?? ?? ???????[[?9 ,? 10 ,? 11 ],?? ????????[12 ,? 13 ,? 14 ],?? ????????[15 ,? 16 ,? 17 ]],?? ?? ???????[[18 ,? 19 ,? 20 ],?? ????????[21 ,? 22 ,? 23 ],?? ????????[24 ,? 25 ,? 26 ]]])??
根據前面了解的,推斷下這個數組的步長。從里面開始算,應該為(3×4×3,3×4,4)。驗證一下:
[python] ?view plaincopy
>>>?c.strides?? (36 ,? 12 ,? 4 )??
完整的代碼為:
[python] ?view plaincopy
import ?cv2?? import ?numpy?as?np?? ?? img?=?cv2.imread("D:/cat.jpg" )?? ?? b?=?np.zeros((img.shape[0 ],img.shape[ 1 ]),?dtype=img.dtype)?? g?=?np.zeros((img.shape[0 ],img.shape[ 1 ]),?dtype=img.dtype)?? r?=?np.zeros((img.shape[0 ],img.shape[ 1 ]),?dtype=img.dtype)?? ?? b[:,:]?=?img[:,:,0 ]?? g[:,:]?=?img[:,:,1 ]?? r[:,:]?=?img[:,:,2 ]?? ?? merged?=?cv2.merge([b,g,r])?? print ? "Merge?by?OpenCV" ??? print ?merged.strides?? print ?merged?? ?? mergedByNp?=?np.dstack([b,g,r])??? print ? "Merge?by?NumPy?" ??? print ?mergedByNp.strides?? print ?mergedByNp?? ?? cv2.imshow("Merged" ,?merged)?? cv2.imshow("MergedByNp" ,?merged)?? cv2.imshow("Blue" ,?b)?? cv2.imshow("Red" ,?r)?? cv2.imshow("Green" ,?g)?? cv2.waitKey(0 )?? cv2.destroyAllWindows() ?
from: http://blog.csdn.net/sunny2038/article/category/904451
總結
以上是生活随笔 為你收集整理的OpenCV之Python学习笔记(1)(2): 图像的载入、显示和保存 图像元素的访问、通道分离与合并 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。