手把手教你使用OpenCV庫(附例項、Python程式碼解析)

数据派THU發表於2019-03-22

OpenCV Python 教程

在這個OpenCV Python 的教程中, 我們將使用Python中的OpenCV庫來介紹計算機視覺的各個方面。OpenCV 長期以來一直是軟體開發的重要部分。對開發人員來說學習OpenCV是提高程式設計能力並幫助他們發展軟體開發職業生涯的好方法。

什麼是計算機視覺

為了簡化這個問題的答案, 讓我們來試想一個場景。

假設你和你的朋友去度假,然後你上傳了很多照片到Facebook上。但是現在在每張照片中找到你朋友的臉並標記它們要花費很多時間。實際上,Facebook已經足夠智慧,它可以幫你標記人物。

那麼,你認為自動的特徵標記是如何工作的呢? 簡單來說,它通過計算機視覺來實現。

計算機視覺是一個跨學科領域,它解決如何使計算機從數字影象或視訊中獲得高層次的理解的問題。

這裡的想法是將人類視覺系統可以完成的任務自動化。因此,計算機應該能夠識別諸如人臉或者燈柱甚至雕像之類的物體。

計算機如何讀取影象?

思考以下圖片:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

我們可以認出它是紐約天際線的圖片。 但是計算機可以自己發現這一切嗎?答案是不!

計算機將任何圖片都讀取為一組0到255之間的值。

對於任何一張彩色圖片,有三個主通道——紅色(R),綠色(G)和藍色(B)。它的工作原理非常簡單。

對每個原色建立一個矩陣,然後,組合這些矩陣以提供R, G和B各個顏色的畫素值。

每一個矩陣的元素提供與畫素的亮度強度有關的資料。

思考下圖:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

如圖所示,影象的大小被計算為B x A x 3。

注意:對於黑白圖片,只有一個單一通道。

現在讓我們來看看OpenCV究竟是什麼。

什麼是OpenCV

OpenCV是一個旨在解決計算機視覺問題的Python庫。OpenCV最初由Intel在1999年開發,但是後來由Willow Garage資助。它支援很多程式語言,如C++,Python,Java等等。它也支援多種平臺,包括Windows,Linux和MacOS。

OpenCV Python只是一個與Python一起使用的原始C++庫的包裝類。通過使用它,所有OpenCV陣列結構都能被轉化為NumPy陣列或從NumPy陣列轉化而來。這樣就可以輕鬆地將其與其他使用NumPy的庫整合。例如,SciPy和Matplotlib等庫。

OpenCV的基礎操作?

Opencv能完成以下從載入影象到調整大小等基本操作:

  • 使用OpenCV載入圖片

  • 檢視圖片形狀/解析度

  • 顯示圖片

  • 調整影象大小

1. 使用OpenCV載入圖片

Import cv2  

 # colored Image  

Img = cv2.imread ("Penguins.jpg",1)  

 # Black and White (gray scale)  

Img_1 = cv2.imread ("Penguins.jpg",0)  

如以上程式碼所示,第一個要求是匯入OpenCV模組。

之後,我們可以用imread模組讀取圖片。引數中的1代表這是一個彩色圖片。如果這個引數的值是0,就意味著這個將被匯入的圖片是黑白圖片。這裡的圖片名稱是“Penguins”。很簡單吧?

2. 檢視圖片形狀/解析度

我們可以使用shape子函式來輸出圖片的形狀。看看以下程式碼:

Import cv2  

# Black and White (gray scale)  

Img = cv2.imread ("Penguins.jpg",0)  

Print(img.shape)  

對於圖片的形狀,我們指的是NumPy陣列的形狀。執行程式碼之後你將會看到這個矩陣由768行和1024列組成。

3. 顯示圖片

使用OpenCV顯示圖片非常簡單和直接。思考以下圖片:

import cv2  

 

# Black and White (gray scale)  

Img = cv2.imread ("Penguins.jpg",0)  


cv2.imshow("Penguins", img)  

 

cv2.waitKey(0)  


# cv2.waitKey(2000)  


cv2.destroyAllWindows()  

正如你所見,我們首先使用imread匯入圖片。我們需要一個輸出視窗來顯示這個圖片,對吧? 

然後,我們等待使用者事件。waitKey使視窗保持靜態直到使用者按下一個鍵。傳入的引數是以毫秒為單位的時間。

最後,我們根據waitForKey的引數使用destroyAllWindows關閉視窗。

4. 調整影象大小

類似地,調整影象大小非常簡單。 這裡有另一個程式碼段:

import cv2  


# Black and White (gray scale)  

img = cv2.imread ("Penguins.jpg",0)  


resized_image = cv2.resize(img, (650,500))  


cv2.imshow("Penguins", resized_image)  


cv2.waitKey(0)  


cv2.destroyAllWindows()  

這裡,resize函式用於將影象大小調整為所需的形狀。這裡的引數是新調整大小後的影象的形狀。

與之前的程式碼相比,剩下的程式碼非常簡單,對嗎?

我相信你們對企鵝很好奇,這是我們想要輸出的圖片!

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

這是另一個向resize函式傳遞引數的方法。看看下面的表示方法:

Resized_image = cv2.resize(img, int(img.shape[1]/2), int(img.shape[0]/2)))  

這裡,我們得到的新影象大小是原始影象的一半。

使用OpenCV進行人臉檢測

這看起來很複雜,但實際上很容易。 讓我帶你瞭解整個過程,然後你也會有同樣的感受。

第一步:想一想我們的先決條件。我們首先需要一個影象。然後,我們需要建立一個級聯分類器,它最後會給我們提供面部特徵。

 第二步:這一步要使用到OpenCV讀取影象和特徵檔案。所以這個時候,原始資料點是NumPy陣列的形式。

我們要做的就是搜尋面部 NumPy n維陣列的行和列的值。這是具有面部矩形座標的陣列。

第三步:最後一步是使用矩形面框顯示影象。

看看下面的圖片,這裡我以圖片的形式總結了上述的三個步驟以便於閱讀:

非常直接明瞭,對吧?

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

首先,如之前所述,我們建立CascadeClassifier物件來提取面部特徵。包含面部特徵的XML檔案路徑是此處的引數

下一步是讀取一個包含面部的圖片,並且使用COLOR_BGR2GREY將其轉化為黑白圖片。接下來,我們搜尋影象的座標。這是使用detectMultiScale來實現的。

你問什麼座標?它是面部矩形的座標。scaleFactor被用來減小5%的形狀值,直到找到面部。因此,總的來說,值越小,準確度越高。

最後,這張臉被顯示到視窗。

  • 給識別的人臉新增矩形面框

這個邏輯很簡單——就像使用for迴圈語句一樣簡單。看看下面的圖片:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

我們通過傳遞引數(比如圖片物件,輪廓框的RGB值和矩形的寬度),使用cv2.rectangle來定義方法以建立一個矩形。

讓我們來看看面部檢測的完整程式碼:

import cv2  

# Create a CascadeClassifier Object  

face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")  

# Reading the image as it is  

img = cv2.imread("photo.jpg") 

# Reading the image as gray scale image  

gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  

# Search the co-ordintes of the image  

faces = face_cascade.detectMultiScale(gray_img, scaleFactor = 1.05,  minNeighbors=5)  

for x,y,w,h in faces:  

    img = cv2.rectangle(img, (x,y), (x+w,y+h),(0,255,0),3)  

resized = cv2.resize(img, (int(img.shape[1]/7),int(img.shape[0]/7)))   

cv2.imshow("Gray", resized)  

cv2.waitKey(0)  

cv2.destroyAllWindows()  

使用OpenCV捕獲視訊

使用OpenCV捕獲視訊同樣非常簡單。以下的流程能夠給你一個更好的理解。看看這個:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

圖片被逐個讀取,因此由於幀的快速處理而產生視訊,這使得獨立的圖片動起來。

看一看下面這張圖:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

首先我們像平常一樣匯入OpenCV庫。接下來,我們有一個叫做VideoCapture的方法,用來建立VideoCapture物件。這個方法用於觸發使用者計算機上的攝像頭。這個函式的引數表示程式應該使用內建攝像頭還是附加攝像頭。“0”表示在這個例子中使用內建攝像頭。

最後,release方法用於在幾毫秒內釋放攝像頭。

當你繼續並嘗試執行上述程式碼時,你會注意到攝像頭指示燈會在一瞬間開啟並稍後關閉。為什麼會這樣? 

這是因為沒有持續時間來保持相機功能。

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

看看以上程式碼,我們有新的一行time.sleep(3)。這使指令碼停止3秒。請注意,傳遞的引數是以秒為單位的時間。因此,當程式碼執行時,網路攝像頭將開啟3秒鐘。

  • 新增視窗

新增一個視窗來顯示視訊的輸出非常簡單,與用於影象的相同方法差不多。 但是,還是有一點變化。 請看以下程式碼:

我很確定除了一兩行外,你能很大程度地理解以上的程式碼。 

在這裡,我們定義了一個NumPy陣列,用於表示視訊捕獲的第一個影象。它被儲存在frame陣列中。 

我們還有check。這是一個布林資料型別。如果Python能夠訪問和讀取VideoCapture物件則返回True。 

看看下面的輸出:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

如你所見,我們得到的輸出為True,並列印了frame陣列的一部分。

但是我們需要讀取視訊的第一個幀/圖才能開始,是吧? 

要做到這一點,首先我們需要建立一個frame物件,它將讀取VideoCapture物件的影象。

如上所示,imshow方法用於捕獲視訊的第一幀。 

在此期間,我們已經嘗試了捕獲視訊的第一影象/幀。 

那麼我們如何在OpenCV中捕獲視訊而不是第一張影象呢? 

  • 直接捕獲視訊

為了捕獲視訊,我們將使用while迴圈。while的條件是這樣的:除非“check”值為True,否則Python將顯示幀。 

這是程式碼段的圖片:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

我們利用cvtColor函式將每個幀轉換為灰度影象,如前所述。

waitKey(1)將確保在每毫秒的間隙後生成一個新幀。

重要的是要注意while迴圈是完全有效的,以幫助完全迭代幀並在最終顯示視訊。

這裡還有一個使用者事件觸發器。一旦使用者按下“q”鍵,程式視窗就會關閉。 

OpenCV很容易掌握,對吧? 我個人喜歡它的良好的可讀性以及初學者開始使用OpenCV時極快的上手速度。 

使用案例:使用OpenCV的運動檢測器

1. 問題描述

你正在接觸一家研究人類行為的公司。你的任務是為他們提供可以檢測前方運動的網路攝像頭。它應該返回一個圖,並且這個圖應該包含人/物體在攝像頭前面的時間。

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

那麼現在我們已經定義了我們的問題陳述,我們需要構建一個解決方案邏輯以結構化的方式來解決問題。

看看下面的圖表:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

最開始,我們將影象儲存在特定的frame當中。

下一步是將影象轉化為高斯模糊影象。這樣做是為了保證我們能計算模糊影象和真實影象之間的明顯的差異。

此時,影象仍然不是物件。我們定義一個閾值來去除瑕疵,比如影象中的陰影和其他噪聲。

物件的邊框稍後定義。我們在物件周圍新增一個矩形框,正如我們在本教程前面所討論的那樣。 

最後,我們計算物件出現在畫面和退出畫面的時間。

很簡單吧?

這是程式碼段:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

這裡也遵循同樣的原則。我們首先匯入包並建立VideoCapture物件,以確保我們使用網路攝像頭捕獲視訊。

While迴圈遍歷視訊的各個幀。我們將彩色幀轉換為灰度影象,然後將此灰度影象轉化為高斯模糊圖。

我們需要儲存視訊的第一個影象/幀,對吧?出於這個目的,我們使用if語句。 

現在,讓我們深入瞭解一下程式碼: 

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

我們使用absdiff函式計算第一個幀和其他所有幀之間的差異。 

threshold函式提供閾值,它將差異值小於30的畫素轉換為黑。如果畫素的差異值大於30,則轉換為白色。THRESH_BINARY就適用於此目的。 

然後,我們使用findContours函式給影象定義輪廓區域。我們也在這個階段加入邊界。 

就像之前解釋過的,contourArea函式可消除陰影和噪聲。為了簡化,它將只保留白色部分,正如我們定義的,白色部分面積大於1000畫素。

然後,在我們的工作幀中在物件的周圍建立一個矩形框。 

接下來是這個簡單的程式碼:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

如前所述,幀每毫秒改變一次,並且當使用者輸入“q”時,跳出迴圈並關閉視窗。

在我們的用例中還有一件事就是我們要計算物件在攝像頭前的時間。

2. 計算時間

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

我們用DataFrame儲存物件出現在幀中被檢測到的時間和運動的時間。

接下來是我們之前解釋過的VideoCapture函式。但是在這裡,我們有一個標誌位,稱之為status。我們在記錄開始時設定status為0,因為物件在最初是不可見的。

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

如上圖所示,當檢測到物件時,我們將status標誌更改為1。很簡單吧?

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

我們將建立一個status的列表儲存每一個掃描到的幀的狀態,然後如果某處發生改變則使用datetime在列表中記錄日期和時間。

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

如以上的解釋圖所示,我們將時間值儲存在 DataFrame中。我們以把DataFrame寫入CSV檔案中結束,如圖所示。

3. 繪製運動檢測圖

我們例項的最後一步是顯示結果。我們將要顯示的是表示兩軸上的運動的圖形。看看以下程式碼:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

首先,我們從motion_detector.py檔案中匯入DataFrame。

下一步是將時間轉換為可讀的並且可以解析的字串形式。

最後,使用Bokeh plots在瀏覽器上繪製時間值的DataFrame。

輸出:

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

結論: 

我希望這個OpenCV Python的教程能幫助你學習所有由Python開始使用OpenCV所需的基礎。

當你嘗試開發需要影象識別和類似原理的軟體時就會非常方便了。現在你還能夠在Python OpenCV的幫助下輕鬆地使用這些概念來開發應用程式。

原文標題:

Computer Vision Using OpenCV

原文連結:

https://dzone.com/articles/opencv-python-tutorial-computer-vision-using-openc

譯者簡介

手把手教你使用OpenCV庫(附例項、Python程式碼解析)

吳金笛,雪城大學電腦科學碩士一年級在讀。迎難而上是我最舒服的狀態,動心忍性,曾益我所不能。我的目標是做個早睡早起的Cool Girl。

相關文章