一、Python OpenCV 入門
歡迎閱讀系列教程,內容涵蓋 OpenCV,它是一個圖像和視頻處理庫,包含 C ++,C,Python 和 Java 的綁定。 OpenCV 用于各種圖像和視頻分析,如面部識別和檢測,車牌閱讀,照片編輯,高級機器人視覺,光學字符識別等等。
你將需要兩個主要的庫,第三個可選:python-OpenCV,Numpy 和 Matplotlib。
Windows 用戶:
python-OpenCV:有其他的方法,但這是最簡單的。 下載相應的 wheel(.whl)文件,然后使用pip進行安裝。 觀看視頻來尋求幫助。
不熟悉使用pip? 請參閱pip安裝教程來獲得幫助。
Linux/Mac 用戶
pip3 install numpy
或者
你可能需要apt-get來安裝python3-pip。
pip3 install matplotlib
或者
Matplotlib 是用于展示來自視頻或圖像的幀的可選選項。 我們將在這里展示幾個使用它的例子。 Numpy 被用于“數(shù)值和 Python”的所有東西。 我們主要利用 Numpy 的數(shù)組功能。 最后,我們使用python-OpenCV,它是 Python 特定的 OpenCV 綁定。
OpenCV 有一些操作,如果沒有完整安裝 OpenCV (大小約 3GB),你將無法完成,但是實際上你可以用 python-OpenCV 最簡安裝。 我們將在本系列的后續(xù)部分中使用 OpenCV 的完整安裝,如果你愿意的話,你可以隨意獲得它,但這三個模塊將使我們忙碌一段時間!
通過運行 Python 并執(zhí)行下列命令來確保你安裝成功:
import cv2import matplotlibimport numpy
如果你沒有錯誤,那么你已經(jīng)準備好了。好了嘛?讓我們下潛吧!
首先,在圖像和視頻分析方面,我們應該了解一些基本的假設和范式。對現(xiàn)在每個攝像機的記錄方式來說,記錄實際上是一幀一幀地顯示,每秒 30-60 次。但是,它們的核心是靜態(tài)幀,就像圖像一樣。因此,圖像識別和視頻分析大部分使用相同的方法。有些東西,如方向跟蹤,將需要連續(xù)的圖像(幀),但像面部檢測或物體識別等東西,在圖像和視頻中代碼幾乎完全相同。
接下來,大量的圖像和視頻分析歸結為盡可能簡化來源。這幾乎總是起始于轉換為灰度,但也可以是彩色濾鏡,漸變或這些的組合。從這里,我們可以對來源執(zhí)行各種分析和轉化。一般來說,這里發(fā)生的事情是轉換完成,然后是分析,然后是任何覆蓋,我們希望應用在原始來源上,這就是你可以經(jīng)??吹?,對象或面部識別的“成品”在全色圖像或視頻上顯示。然而,數(shù)據(jù)實際上很少以這種原始形式處理。有一些我們可以在基本層面上做些什么的例子。所有這些都使用基本的網(wǎng)絡攝像頭來完成,沒有什么特別的:
背景提取
顏色過濾
邊緣檢測
用于對象識別的特征匹配
一般對象識別
在邊緣檢測的情況下,黑色對應于(0,0,0)的像素值,而白色線條是(255,255,255)。視頻中的每個圖片和幀都會像這樣分解為像素,并且像邊緣檢測一樣,我們可以推斷,邊緣是基于白色與黑色像素對比的地方。然后,如果我們想看到標記邊緣的原始圖像,我們記錄下白色像素的所有坐標位置,然后在原始圖像或視頻上標記這些位置。
到本教程結束時,你將能夠完成上述所有操作,并且能夠訓練你的機器識別你想要的任何對象。就像我剛開始說的,第一步通常是轉換為灰度。在此之前,我們需要加載圖像。因此,我們來做吧!在整個教程中,我極力鼓勵你使用你自己的數(shù)據(jù)來玩。如果你有攝像頭,一定要使用它,否則找到你認為很有趣的圖像。如果你有麻煩,這是一個手表的圖像:
首先,我們正在導入一些東西,我已經(jīng)安裝了這三個模塊。接下來,我們將img定義為cv2.read(image file, parms)。默認值是IMREAD_COLOR,這是沒有任何 alpha 通道的顏色。如果你不熟悉,alpha 是不透明度(與透明度相反)。如果你需要保留 Alpha 通道,也可以使用IMREAD_UNCHANGED。很多時候,你會讀取顏色版本,然后將其轉換為灰度。如果你沒有網(wǎng)絡攝像機,這將是你在本教程中使用的主要方法,即加載圖像。
你可以不使用IMREAD_COLOR ...等,而是使用簡單的數(shù)字。你應該熟悉這兩種選擇,以便了解某個人在做什么。對于第二個參數(shù),可以使用-1,0或1。顏色為1,灰度為0,不變?yōu)?1。因此,對于灰度,可以執(zhí)行cv2.imread('watch.jpg', 0)。
一旦加載完成,我們使用cv2.imshow(title,image)來顯示圖像。從這里,我們使用cv2.waitKey(0)來等待,直到有任何按鍵被按下。一旦完成,我們使用cv2.destroyAllWindows()來關閉所有的東西。
正如前面提到的,你也可以用 Matplotlib 顯示圖像,下面是一些如何實現(xiàn)的代碼:
import cv2import numpy as npfrom matplotlib import pyplot as pltimg = cv2.imread('watch.jpg',cv2.IMREAD_GRAYSCALE)plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axisplt.plot([200,300,400],[100,200,300],'c', linewidth=5)plt.show()
請注意,你可以繪制線條,就像任何其他 Matplotlib 圖表一樣,使用像素位置作為坐標的。 不過,如果你想繪制你的圖片,Matplotlib 不是必需的。 OpenCV 為此提供了很好的方法。 當你完成修改后,你可以保存,如下所示:
將圖片導入 OpenCV 似乎很容易,加載視頻源如何? 在下一個教程中,我們將展示如何加載攝像頭或視頻源。
二、加載視頻源
在這個 Python OpenCV 教程中,我們將介紹一些使用視頻和攝像頭的基本操作。 除了起始行,處理來自視頻的幀與處理圖像是一樣的。 我們來舉例說明一下:
import numpy as npimport cv2cap = cv2.VideoCapture(0) while(True): ret, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) cv2.imshow('frame',gray) if cv2.waitKey(1) & 0xFF == ord('q'): breakcap.release()cv2.destroyAllWindows()
首先,我們導入numpy和cv2,沒有什么特別的。 接下來,我們可以cap = cv2.VideoCapture(0)。 這將從你計算機上的第一個網(wǎng)絡攝像頭返回視頻。 如果你正在觀看視頻教程,你將看到我正在使用1,因為我的第一個攝像頭正在錄制我,第二個攝像頭用于實際的教程源。
這段代碼啟動了一個無限循環(huán)(稍后將被break語句打破),其中ret和frame被定義為cap.read()。 基本上,ret是一個代表是否有返回的布爾值,frame是每個返回的幀。 如果沒有幀,你不會得到錯誤,你會得到None。
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
在這里,我們定義一個新的變量gray,作為轉換為灰度的幀。 注意這個BGR2GRAY。 需要注意的是,OpenCV 將顏色讀取為 BGR(藍綠色紅色),但大多數(shù)計算機應用程序讀取為 RGB(紅綠藍)。 記住這一點。
請注意,盡管是視頻流,我們仍然使用imshow。 在這里,我們展示了轉換為灰色的源。 如果你想同時顯示,你可以對原始幀和灰度執(zhí)行imshow,將出現(xiàn)兩個窗口。
if cv2.waitKey(1) & 0xFF == ord('q'): break
這個語句每幀只運行一次。 基本上,如果我們得到一個按鍵,那個鍵是q,我們將退出while循環(huán),然后運行:
這將釋放網(wǎng)絡攝像頭,然后關閉所有的imshow()窗口。
在某些情況下,你可能實際上需要錄制,并將錄制內容保存到新文件中。 以下是在 Windows 上執(zhí)行此操作的示例:
import numpy as npimport cv2cap = cv2.VideoCapture(1)fourcc = cv2.VideoWriter_fourcc(*'XVID')out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))while(True): ret, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) out.write(frame) cv2.imshow('frame',gray) if cv2.waitKey(1) & 0xFF == ord('q'): breakcap.release()out.release()cv2.destroyAllWindows()
這里主要要注意的是正在使用的編解碼器,以及在while循環(huán)之前定義的輸出信息。 然后,在while循環(huán)中,我們使用out.write()來輸出幀。 最后,在while循環(huán)之外,在我們釋放攝像頭之后,我們也釋放out。
太好了,現(xiàn)在我們知道如何操作圖像和視頻。 如果你沒有網(wǎng)絡攝像頭,你可以使用圖像甚至視頻來跟隨教程的其余部分。 如果你希望使用視頻而不是網(wǎng)絡攝像頭作為源,則可以為視頻指定文件路徑,而不是攝像頭號碼。
現(xiàn)在我們可以使用來源了,讓我們來展示如何繪制東西。 此前你已經(jīng)看到,你可以使用 Matplotlib 在圖片頂部繪制,但是 Matplotlib 并不真正用于此目的,特別是不能用于視頻源。 幸運的是,OpenCV 提供了一些很棒的工具,來幫助我們實時繪制和標記我們的源,這就是我們將在下一個教程中討論的內容。
三、在圖像上繪制和寫字
在這個 Python OpenCV 教程中,我們將介紹如何在圖像和視頻上繪制各種形狀。 想要以某種方式標記檢測到的對象是相當普遍的,所以我們人類可以很容易地看到我們的程序是否按照我們的希望工作。 一個例子就是之前顯示的圖像之一:
對于這個臨時的例子,我將使用下面的圖片:
鼓勵你使用自己的圖片。 像往常一樣,我們的起始代碼可以是這樣的:
下面,我們可以開始繪制,這樣:
cv2.line(img,(0,0),(150,150),(255,255,255),15)cv2.imshow('image',img)cv2.waitKey(0)cv2.destroyAllWindows()
cv2.line()接受以下參數(shù):圖片,開始坐標,結束坐標,顏色(bgr),線條粗細。
結果在這里:
好吧,很酷,讓我們繪制更多形狀。 接下來是一個矩形:
這里的參數(shù)是圖像,左上角坐標,右下角坐標,顏色和線條粗細。
圓怎么樣?
cv2.circle(img,(100,63), 55, (0,255,0), -1)
這里的參數(shù)是圖像/幀,圓心,半徑,顏色和。 注意我們粗細為-1。 這意味著將填充對象,所以我們會得到一個圓。
線條,矩形和圓都很酷,但是如果我們想要五邊形,八邊形或十八邊形? 沒問題!
首先,我們將坐標數(shù)組稱為pts(點的簡稱)。 然后,我們使用cv2.polylines來畫線。 參數(shù)如下:繪制的對象,坐標,我們應該連接終止的和起始點,顏色和粗細。
你可能想要做的最后一件事是在圖像上寫字。 這可以這樣做:
font = cv2.FONT_HERSHEY_SIMPLEXcv2.putText(img,'OpenCV Tuts!',(0,130), font, 1, (200,255,155), 2, cv2.LINE_AA)
目前為止的完整代碼:
結果:
在下一個教程中,我們將介紹我們可以執(zhí)行的基本圖像操作。
四、圖像操作
在 OpenCV 教程中,我們將介紹一些我們可以做的簡單圖像操作。 每個視頻分解成幀。 然后每一幀,就像一個圖像,分解成存儲在行和列中的,幀/圖片中的像素。 每個像素都有一個坐標位置,每個像素都由顏色值組成。 讓我們列舉訪問不同的位的一些例子。
我們將像往常一樣讀取圖像(如果可以,請使用自己的圖像,但這里是我在這里使用的圖像):
import cv2import numpy as npimg = cv2.imread('watch.jpg',cv2.IMREAD_COLOR)
現(xiàn)在我們可以實際引用特定像素,像這樣:
下面我們可以實際修改像素:
img[55,55] = [255,255,255]
之后重新引用:
現(xiàn)在應該不同了,下面我們可以引用 ROI,圖像區(qū)域:
px = img[100:150,100:150]print(px)
我們也可以修改 ROI,像這樣:
我們可以引用我們的圖像的特定特征:
print(img.shape)print(img.size)print(img.dtype)
我們可以像這樣執(zhí)行操作:
這會處理我的圖像,但是可能不能用于你的圖像,取決于尺寸。這是我的輸出:
這些是一些簡單的操作。 在下一個教程中,我們將介紹一些我們可以執(zhí)行的更高級的圖像操作。
五、圖像算術和邏輯運算
歡迎來到另一個 Python OpenCV 教程,在本教程中,我們將介紹一些簡單算術運算,我們可以在圖像上執(zhí)行的,并解釋它們的作用。 為此,我們將需要兩個相同大小的圖像來開始,然后是一個較小的圖像和一個較大的圖像。 首先,我將使用:
和
首先,讓我們看看簡單的加法會做什么:
import cv2import numpy as np# 500 x 250img1 = cv2.imread('3D-Matplotlib.png')img2 = cv2.imread('mainsvmimage.png')add = img1+img2cv2.imshow('add',add)cv2.waitKey(0)cv2.destroyAllWindows()
結果:
你不可能想要這種混亂的加法。 OpenCV 有一個“加法”方法,讓我們替換以前的“加法”,看看是什么:
結果:
這里可能不理想。 我們可以看到很多圖像是非常“白色的”。 這是因為顏色是 0-255,其中 255 是“全亮”。 因此,例如:(155,211,79) + (50, 170, 200) = 205, 381, 279...轉換為(205, 255,255)。
接下來,我們可以添加圖像,并可以假設每個圖像都有不同的“權重”。 這是如何工作的:
import cv2import numpy as npimg1 = cv2.imread('3D-Matplotlib.png')img2 = cv2.imread('mainsvmimage.png')weighted = cv2.addWeighted(img1, 0.6, img2, 0.4, 0)cv2.imshow('weighted',weighted)cv2.waitKey(0)cv2.destroyAllWindows()
對于addWeighted方法,參數(shù)是第一個圖像,權重,第二個圖像,權重,然后是伽馬值,這是一個光的測量值。 我們現(xiàn)在就把它保留為零。
這些是一些額外的選擇,但如果你真的想將一個圖像添加到另一個,最新的重疊在哪里? 在這種情況下,你會從最大的開始,然后添加較小的圖像。 為此,我們將使用相同的3D-Matplotlib.png圖像,但使用一個新的 Python 標志:
現(xiàn)在,我們可以選取這個標志,并把它放在原始圖像上。 這很容易(基本上使用我們在前一個教程中使用的相同代碼,我們用一個新的東西替換了圖像區(qū)域(ROI)),但是如果我們只想要標志部分而不是白色背景呢? 我們可以使用與之前用于 ROI 替換相同的原理,但是我們需要一種方法來“去除”標志的背景,使得白色不會不必要地阻擋更多背景圖像。 首先我將顯示完整的代碼,然后解釋:
這里發(fā)生了很多事情,出現(xiàn)了一些新的東西。 我們首先看到的是一個新的閾值:ret, mask = cv2.threshold(img2gray, 220, 255, cv2.THRESH_BINARY_INV)。
我們將在下一個教程中介紹更多的閾值,所以請繼續(xù)關注具體內容,但基本上它的工作方式是根據(jù)閾值將所有像素轉換為黑色或白色。 在我們的例子中,閾值是 220,但是我們可以使用其他值,或者甚至動態(tài)地選擇一個,這是ret變量可以使用的值。 接下來,我們看到:mask_inv = cv2.bitwise_not(mask)。 這是一個按位操作。 基本上,這些操作符與 Python 中的典型操作符非常相似,除了一點,但我們不會在這里觸及它。 在這種情況下,不可見的部分是黑色的地方。 然后,我們可以說,我們想在第一個圖像中將這個區(qū)域遮住,然后將空白區(qū)域替換為圖像 2 的內容。
下個教程中,我們深入討論閾值。
六、閾值
歡迎閱讀另一個 OpenCV 教程。在本教程中,我們將介紹圖像和視頻分析的閾值。閾值的思想是進一步簡化視覺數(shù)據(jù)的分析。首先,你可以轉換為灰度,但是你必須考慮灰度仍然有至少 255 個值。閾值可以做的事情,在最基本的層面上,是基于閾值將所有東西都轉換成白色或黑色。比方說,我們希望閾值為 125(最大為 255),那么 125 以下的所有內容都將被轉換為 0 或黑色,而高于 125 的所有內容都將被轉換為 255 或白色。如果你像平常一樣轉換成灰度,你會變成白色和黑色。如果你不轉換灰度,你會得到二值化的圖片,但會有顏色。
雖然這聽起來不錯,但通常不是。我們將在這里介紹多個示例和不同類型的閾值來說明這一點。我們將使用下面的圖片作為我們的示例圖片,但可以隨意使用你自己的圖片:
這個書的圖片就是個很好的例子,說明為什么一個人可能需要閾值。 首先,背景根本沒有白色,一切都是暗淡的,而且一切都是變化的。 有些部分很容易閱讀,另一部分則非常暗,需要相當多的注意力才能識別出來。 首先,我們嘗試一個簡單的閾值:
retval, threshold = cv2.threshold(img, 10, 255, cv2.THRESH_BINARY)
二元閾值是個簡單的“是或不是”的閾值,其中像素為 255 或 0。在很多情況下,這是白色或黑色,但我們已經(jīng)為我們的圖像保留了顏色,所以它仍然是彩色的。 這里的第一個參數(shù)是圖像。 下一個參數(shù)是閾值,我們選擇 10。下一個是最大值,我們選擇為 255。最后是閾值類型,我們選擇了THRESH_BINARY。 通常情況下,10 的閾值會有點差。 我們選擇 10,因為這是低光照的圖片,所以我們選擇低的數(shù)字。 通常 125-150 左右的東西可能效果最好。
結果:
現(xiàn)在的圖片稍微更便于閱讀了,但還是有點亂。 從視覺上來說,這樣比較好,但是仍然難以使用程序來分析它。 讓我們看看我們是否可以進一步簡化。
首先,讓我們灰度化圖像,然后使用一個閾值:
import cv2import numpy as npgrayscaled = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)retval, threshold = cv2.threshold(grayscaled, 10, 255, cv2.THRESH_BINARY)cv2.imshow('original',img)cv2.imshow('threshold',threshold)cv2.waitKey(0)cv2.destroyAllWindows()
更簡單,但是我們仍然在這里忽略了很多背景。 接下來,我們可以嘗試自適應閾值,這將嘗試改變閾值,并希望弄清楚彎曲的頁面。
還有另一個版本的閾值,可以使用,叫做大津閾值。 它在這里并不能很好發(fā)揮作用,但是:
retval2,threshold2 = cv2.threshold(grayscaled,125,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)cv2.imshow('original',img)cv2.imshow('Otsu threshold',threshold2)cv2.waitKey(0)cv2.destroyAllWindows()
七、顏色過濾
在這個 Python OpenCV 教程中,我們將介紹如何創(chuàng)建一個過濾器,回顧按位操作,其中我們將過濾特定的顏色,試圖顯示它?;蛘撸阋部梢詫iT篩選出特定的顏色,然后將其替換為場景,就像我們用其他方法替換ROI(圖像區(qū)域)一樣,就像綠屏的工作方式。
為了像這樣過濾,你有幾個選項。通常,你可能會將你的顏色轉換為 HSV,即“色調飽和度純度”。例如,這可以幫助你根據(jù)色調和飽和度范圍,使用變化的值確定一個更具體的顏色。如果你希望的話,你可以實際生成基于 BGR 值的過濾器,但是這會有點困難。如果你很難可視化 HSV,不要感到失落,查看維基百科頁面上的 HSV,那里有一個非常有用的圖形讓你可視化它。我最好親自描述顏色的色調飽和度和純度。現(xiàn)在讓我們開始:
這只是一個例子,以紅色為目標。 它的工作方式是,我們所看到的是我們范圍內的任何東西,基本上是 30-255,150-255 和 50-180。 它用于紅色,但可以隨便嘗試找到自己的顏色。 HSV 在這里效果最好的原因是,我們想要范圍內的顏色,這里我們通常需要相似的顏色。 很多時候,典型的紅色仍然會有一些綠色和藍色分量,所以我們必須允許一些綠色和藍色,但是我們會想要幾乎全紅。 這意味著我們會在這里獲得所有顏色的低光混合。
為了確定 HSV 的范圍,我認為最好的方法就是試錯。 OpenCV 內置了將 BGR 轉換為 HSV 的方法。 如果你想挑選單一的顏色,那么 BGR 到 HSV 將會很好用。 為了教學,下面是這個代碼的一個例子:
dark_red = np.uint8([[[12,22,121]]]) dark_red = cv2.cvtColor(dark_red,cv2.COLOR_BGR2HSV)
這里的結果是一個 HSV 值,與dark_red值相同。這很棒...但是,同樣...你遇到了顏色范圍和 HSV 范圍的基本問題。他們根本不同。你可能合理使用 BGR 范圍,它們仍然可以工作,但是對于檢測一種“顏色”,則無法正常工作。
回到主代碼,然而,我們首先要把幀轉換成 HSV。那里沒什么特別的。接下來,我們?yōu)榧t色指定一些 HSV 值。我們使用inRange函數(shù),為我們的特定范圍創(chuàng)建掩碼。這是真或假,黑色或白色。接下來,我們通過執(zhí)行按位操作來“恢復”我們的紅色?;旧?,我們顯示了frame and mask。掩碼的白色部分是紅色范圍,被轉換為純白色,而其他一切都變成黑色。最后我們展示所有東西。我選擇了顯示原始真,掩碼和最終結果,以便更好地了解發(fā)生的事情。
在下一個教程中,我們將對這個主題做一些介紹。你可能看到了,我們在這里還是有一些“噪音”。東西有顆粒感,紅色中的黑點很多,還有許多其他的小色點。我們可以做一些事情,試圖通過模糊和平滑來緩解這個問題,接下來我們將討論這個問題。
八、模糊和平滑
在這個 Python OpenCV 教程中,我們將介紹如何嘗試從我們的過濾器中消除噪聲,例如簡單的閾值,或者甚至我們以前的特定的顏色過濾器:
正如你所看到的,我們有很多黑點,其中我們喜歡紅色,還有很多其他的色點散落在其中。 我們可以使用各種模糊和平滑技術來嘗試彌補這一點。 我們可以從一些熟悉的代碼開始:
現(xiàn)在,讓我們應用一個簡單的平滑,我們計算每個像素塊的均值。 在我們的例子中,我們使用15x15正方形,這意味著我們有 225 個總像素。
kernel = np.ones((15,15),np.float32)/225 smoothed = cv2.filter2D(res,-1,kernel) cv2.imshow('Original',frame) cv2.imshow('Averaging',smoothed) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
這個很簡單,但是結果犧牲了很多粒度。 接下來,讓我們嘗試一些高斯模糊:
另一個選項是中值模糊:
median = cv2.medianBlur(res,15) cv2.imshow('Median Blur',median)
最后一個選項是雙向模糊:
所有模糊的對比:
至少在這種情況下,我可能會使用中值模糊,但是不同的照明,不同的閾值/過濾器,以及其他不同的目標和目標可能會決定你使用其中一個。
在下一個教程中,我們將討論形態(tài)變換。
九、形態(tài)變換
在這個 Python OpenCV 教程中,我們將介紹形態(tài)變換。 這些是一些簡單操作,我們可以基于圖像形狀執(zhí)行。
我們要談的第一對是腐蝕和膨脹。 腐蝕是我們將“腐蝕”邊緣。 它的工作方式是使用滑塊(核)。 我們讓滑塊滑動,如果所有的像素是白色的,那么我們得到白色,否則是黑色。 這可能有助于消除一些白色噪音。 另一個版本是膨脹,它基本上是相反的:讓滑塊滑動,如果整個區(qū)域不是黑色的,就會轉換成白色。 這是一個例子:
import cv2import numpy as npcap = cv2.VideoCapture(0)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) kernel = np.ones((5,5),np.uint8) erosion = cv2.erode(mask,kernel,iterations = 1) dilation = cv2.dilate(mask,kernel,iterations = 1) cv2.imshow('Original',frame) cv2.imshow('Mask',mask) cv2.imshow('Erosion',erosion) cv2.imshow('Dilation',dilation) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
結果:
下一對是“開放”和“關閉”。 開放的目標是消除“假陽性”。 有時在背景中,你會得到一些像素“噪音”。 “關閉”的想法是消除假陰性。 基本上就是你檢測了你的形狀,例如我們的帽子,但物體仍然有一些黑色像素。 關閉將嘗試清除它們。
另外兩個選項是tophat和blackhat,對我們的案例并不有用:
# It is the difference between input image and Opening of the image cv2.imshow('Tophat',tophat) # It is the difference between the closing of the input image and input image. cv2.imshow('Blackhat',blackhat)
在下一個教程中,我們將討論圖像漸變和邊緣檢測。
十、邊緣檢測和漸變
歡迎閱讀另一個 Python OpenCV 教程。 在本教程中,我們將介紹圖像漸變和邊緣檢測。 圖像漸變可以用來測量方向的強度,邊緣檢測就像它所說的:它找到了邊緣! 我敢打賭你肯定沒看到。
首先,我們來展示一些漸變的例子:
如果你想知道什么是cv2.CV_64F,那就是數(shù)據(jù)類型。 ksize是核大小。 我們使用 5,所以每次查詢5×5的漁區(qū)。
雖然我們可以使用這些漸變轉換為純邊緣,但我們也可以使用 Canny 邊緣檢測!
import cv2import numpy as npcap = cv2.VideoCapture(0)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) cv2.imshow('Original',frame) edges = cv2.Canny(frame,100,200) cv2.imshow('Edges',edges) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
這真是太棒了! 但是,這并不完美。 注意陰影導致了邊緣被檢測到。 其中最明顯的是藍狗窩發(fā)出的陰影。
在下一個 OpenCV 教程中,我們將討論如何在其他圖像中搜索和查找相同的圖像模板。
十一、模板匹配
歡迎閱讀另一個 Python OpenCV 教程,在本教程中,我們將介紹對象識別的一個基本版本。 這里的想法是,給出一定的閾值,找到匹配我們提供的模板圖像的相同區(qū)域。 對于具體的對象匹配,具有精確的照明/刻度/角度,這可以工作得很好。 通常會遇到這些情況的例子就是計算機上的任何 GUI。 按鈕等東西總是相同的,所以你可以使用模板匹配。 結合模板匹配和一些鼠標控制,你已經(jīng)實現(xiàn)了一個基于 Web 的機器人!
首先,你需要一個主要圖像和一個模板。 你應該從你正在圖像中查找的“東西”選取你的模板。 我將提供一個圖像作為例子,但隨意使用你最喜愛的網(wǎng)站的圖像或類似的東西。
主要圖像:
我們要搜索的模板:
這只是其中一個端口,但我們很好奇,看看我們是否可以匹配任何其他端口。 我們確實要選擇一個閾值,其中某種東西可能是 80% 匹配,那么我們說這就匹配。 所以,我們將開始加載和轉換圖像:
到目前為止,我們加載了兩個圖像,轉換為灰度。 我們保留原始的 RGB 圖像,并創(chuàng)建一個灰度版本。 我之前提到過這個,但是我們這樣做的原因是,我們在灰度版本上執(zhí)行所有的處理,然后在彩色圖像上使用相同的標簽來標記。
對于主要圖像,我們只有彩色版本和灰度版本。 我們加載模板并記下尺寸。
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)threshold = 0.8loc = np.where( res >= threshold)
在這里,我們用img_gray(我們的主圖像),模板,和我們要使用的匹配方法調用matchTemplate,并將返回值稱為res。 我們指定一個閾值,這里是 80%。 然后我們使用邏輯語句,找到res大于或等于 80% 的位置。
最后,我們使用灰度圖像中找到的坐標,標記原始圖像上的所有匹配:
所以我們得到了幾個匹配。也許需要降低閾值?我們試試 0.7。
這里有一些假陽性。 你可以繼續(xù)調整門檻,直到你達到 100%,但是如果沒有假陽性,你可能永遠不會達到它。 另一個選擇就是使用另一個模板圖像。 有時候,使用相同對象的多個圖像是有用的。 這樣,你可以使閾值足夠高的,來確保你的結果準確。
在下一個教程中,我們將介紹前景提取。
十二、GrabCut 前景提取
歡迎閱讀 Python OpenCV 前景提取教程。 這里的想法是找到前景,并刪除背景。 這很像綠屏,只是這里我們實際上不需要綠屏。
首先,我們將使用一個圖像:
隨意使用你自己的。
讓我們加載圖像并定義一些東西:
import numpy as npimport cv2from matplotlib import pyplot as pltimg = cv2.imread('opencv-python-foreground-extraction-tutorial.jpg')mask = np.zeros(img.shape[:2],np.uint8)bgdModel = np.zeros((1,65),np.float64)fgdModel = np.zeros((1,65),np.float64)rect = (161,79,150,150)
到目前為止,我們已經(jīng)導入了cv2,numpy和matplotlib。 然后我們加載圖像,創(chuàng)建一個掩碼,指定算法內部使用的背景和前景模型。 真正重要的部分是我們定義的矩形。 這是rect = (start_x, start_y, width, height)。
這是包圍我們的主要對象的矩形。 如果你正在使用我的圖片,那就是要使用的矩陣。 如果你使用自己的,找到適合你的圖像的坐標。
下面:
所以在這里我們使用了cv2.grabCut,它用了很多參數(shù)。 首先是輸入圖像,然后是掩碼,然后是主要對象的矩形,背景模型,前景模型,要運行的迭代量以及使用的模式。
這里改變了掩碼,使得所有像素 0 和 2 轉換為背景,而像素 1 和 3 現(xiàn)在是前景。 從這里,我們乘以輸入圖像,得到我們的最終結果:
下個教程中,我們打算討論如何執(zhí)行角點檢測。
十三、角點檢測
歡迎閱讀 Python OpenCV 角點檢測教程。 檢測角點的目的是追蹤運動,做 3D 建模,識別物體,形狀和角色等。
對于本教程,我們將使用以下圖像:
我們的目標是找到這個圖像中的所有角點。 我會注意到,在這里我們有一些別名問題(斜線的鋸齒),所以,如果我們允許的話,會發(fā)現(xiàn)很多角點,而且是正確的。 和往常一樣,OpenCV 已經(jīng)為我們完成了難題,我們需要做的就是輸入一些參數(shù)。 讓我們開始加載圖像并設置一些參數(shù):
import numpy as npimport cv2img = cv2.imread('opencv-corner-detection-sample.jpg')gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)gray = np.float32(gray)corners = cv2.goodFeaturesToTrack(gray, 100, 0.01, 10)corners = np.int0(corners)
到目前為止,我們加載圖像,轉換為灰度,然后是float32。 接下來,我們用goodFeaturesToTrack函數(shù)檢測角點。 這里的參數(shù)是圖像,檢測到的最大角點數(shù)量,品質和角點之間的最小距離。 如前所述,我們在這里的鋸齒問題將允許找到許多角點,所以我們對其進行了限制。 下面:
現(xiàn)在我們遍歷每個角點,在我們認為是角點的每個點上畫一個圓。
在下一個教程中,我們將討論功能匹配/單映射。
十四、特征匹配(單映射)爆破
歡迎閱讀 Python OpenCV 特征匹配教程。 特征匹配將是稍微更令人印象深刻的模板匹配版本,其中需要一個完美的,或非常接近完美的匹配。
我們從我們希望找到的圖像開始,然后我們可以在另一幅圖像中搜索這個圖像。 這里的完美是圖像不需要相同的光照,角度,旋轉等。 特征只需要匹配。
首先,我們需要一些示例圖像。 我們的“模板”,或者我們將要嘗試匹配的圖像:
之后是我們用于搜索這個模板的圖像:
在這里,我們的模板圖像在模板中,比在我們要搜索的圖像中要小一些。 它的旋轉也不同,陰影也有些不同。
現(xiàn)在我們將使用一種“爆破”匹配的形式。 我們將在這兩個圖像中找到所有特征。 然后我們匹配這些特征。 然后,我們可以繪制我們想要的,盡可能多的匹配。 但是要小心。 如果你繪制 500 個匹配,你會有很多誤報。 所以只繪制繪制前幾個。
import numpy as npimport cv2import matplotlib.pyplot as pltimg1 = cv2.imread('opencv-feature-matching-template.jpg',0)img2 = cv2.imread('opencv-feature-matching-image.jpg',0)
到目前為止,我們已經(jīng)導入了要使用的模塊,并定義了我們的兩個圖像,即模板(img1)和用于搜索模板的圖像(img2)。
這是我們打算用于特征的檢測器。
kp1, des1 = orb.detectAndCompute(img1,None)kp2, des2 = orb.detectAndCompute(img2,None)
在這里,我們使用orb探測器找到關鍵點和他們的描述符。
這就是我們的BFMatcher對象。
matches = bf.match(des1,des2)matches = sorted(matches, key = lambda x:x.distance)
這里我們創(chuàng)建描述符的匹配,然后根據(jù)它們的距離對它們排序。
這里我們繪制了前 10 個匹配。輸出:
十五、MOG 背景減弱
在這個 Python OpenCV 教程中,我們將要討論如何通過檢測運動來減弱圖像的背景。 這將要求我們回顧視頻的使用,或者有兩個圖像,一個沒有你想要追蹤的人物/物體,另一個擁有人物/物體。 如果你希望,你可以使用你的攝像頭,或者使用如下的視頻:
人們行走的樣例視頻
這里的代碼實際上很簡單,就是我們現(xiàn)在知道的:
import numpy as npimport cv2cap = cv2.VideoCapture('people-walking.mp4')fgbg = cv2.createBackgroundSubtractorMOG2()while(1): ret, frame = cap.read() fgmask = fgbg.apply(frame) cv2.imshow('fgmask',frame) cv2.imshow('frame',fgmask) k = cv2.waitKey(30) & 0xff if k == 27: break cap.release()cv2.destroyAllWindows()
結果:
pythonprogramming.net/static/imag…
這里的想法是從靜態(tài)背景中提取移動的前景。 你也可以使用這個來比較兩個相似的圖像,并立即提取它們之間的差異。
在我們的例子中,我們可以看到我們確實已經(jīng)檢測到了一些人,但是我們確實有一些“噪音”,噪音實際上是樹葉在周圍的風中移動了一下。 只要我們知道一種減少噪音的方法。 等一下! 我們的確知道! 一個瘋狂的挑戰(zhàn)已經(jīng)出現(xiàn)了你面前!
接下來的教程開始讓我們遠離濾鏡或變換的應用,并讓我們使用 Haar Cascades 來檢測一般對象,例如面部檢測等等。
十六、Haar Cascade 面部檢測
在這個 Python OpenCV 教程中,我們將討論 Haar Cascades 對象檢測。我們將從臉部和眼睛檢測來開始。為了使用層疊文件進行對象識別/檢測,首先需要層疊文件。對于非常流行的任務,這些已經(jīng)存在。檢測臉部,汽車,笑臉,眼睛和車牌等東西都是非常普遍的。
首先,我會告訴你如何使用這些層疊文件,然后我將告訴你如何開始創(chuàng)建你自己的層疊,這樣你就可以檢測到任何你想要的對象,這很酷!
你可以使用 Google 來查找你可能想要檢測的東西的各種 Haar Cascades。對于找到上述類型,你應該沒有太多的麻煩。我們將使用面部層疊和眼睛層疊。你可以在 Haar Cascades 的根目錄找到更多。請注意用于使用/分發(fā)這些 Haar Cascades 的許可證。
讓我們開始我們的代碼。我假設你已經(jīng)從上面的鏈接中下載了haarcascade_eye.xml和haarcascade_frontalface_default.xml,并將這些文件放在你項目的目錄中。
在這里,我們從導入cv2和numpy開始,然后加載我們的臉部和眼部的層疊。 目前為止很簡單。
while 1: ret, img = cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5)
現(xiàn)在我們開始我們的典型循環(huán),這里唯一的新事物就是臉部的創(chuàng)建。 更多信息請訪問detectMultiScale函數(shù)的文檔。 基本上,它找到了面部! 我們也想找到眼睛,但是在一個假陽性的世界里,在面部里面尋找眼睛,從邏輯上來說是不是很明智? 我們希望我們不尋找不在臉上的眼睛! 嚴格來說,“眼睛檢測”可能不會找到閑置的眼球。 大多數(shù)眼睛檢測使用周圍的皮膚,眼瞼,眼睫毛,眉毛也可以用于檢測。 因此,我們的下一步就是先去拆分面部,然后才能到達眼睛:
在這里,我們找到了面部,它們的大小,繪制矩形,并注意 ROI。 接下來,我們找了一些眼睛:
eyes = eye_cascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
如果我們找到這些,我們會繼續(xù)繪制更多的矩形。 接下來我們完成:
完整代碼:
import numpy as npimport cv2# multiple cascades: https://github.com/Itseez/opencv/tree/master/data/haarcascades#https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xmlface_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')#https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_eye.xmleye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')cap = cv2.VideoCapture(0)while 1: ret, img = cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) cv2.imshow('img',img) k = cv2.waitKey(30) & 0xff if k == 27: breakcap.release()cv2.destroyAllWindows()
不錯。你可能會注意到我不得不取下我的眼鏡。這些造成了一些麻煩。我的嘴也經(jīng)常被檢測為眼睛,有時甚至是一張臉,但你明白了。面部毛發(fā)和其他東西經(jīng)??梢云垓_基本面部檢測,除此之外,皮膚的顏色也會造成很大的麻煩,因為我們經(jīng)常試圖盡可能簡化圖像,從而失去了很多顏色值。甚至還有一個小型行業(yè),可以避免人臉檢測和識別。CVDazzle 網(wǎng)站就是一個例子。其中有些非常古怪,但他們很有效。你也可以總是走完整的面部重建手術的路線,以避免自動跟蹤和檢測,所以總是這樣,但是這更永久。做個發(fā)型比較短暫也容易做到。
好吧,檢測面部,眼睛和汽車是可以的,但我們是程序員。我們希望能夠做任何事情。事實證明,事情會變得相當混亂,建立自己的 Haar Cascades 有一定的難度,但是其他人也這么做......你也可以!這就是在下一個教程中所討論的。
十七、創(chuàng)建自己的 Haar Cascade
歡迎使用 Python OpenCV 對象檢測教程。在本教程中,你將看到如何創(chuàng)建你自己的 Haar Cascades,以便你可以跟蹤任何你想要的對象。由于這個任務的本質和復雜性,本教程將比平時稍長一些,但獎勵是巨大的。
雖然你可以在 Windows 中完成,我不會建議這樣。因此,對于本教程,我將使用 Linux VPS,并且我建議你也這樣做。你可以嘗試使用 Amazon Web Services 提供的免費套餐,但對你來說可能太痛苦了,你可能需要更多的內存。你還可以從 Digital Ocean 獲得低至五美元/月的 VPS。我推薦至少 2GB 的內存用于我們將要做的事情。現(xiàn)在大多數(shù)主機按小時收費,包括 DO。因此,你可以購買一個 20 美元/月的服務器,使用它一天,獲取你想要的文件,然后終止服務器,并支付很少的錢。你需要更多的幫助來設置服務器?如果是的話,看看這個具體的教程。
一旦你的服務器準備就緒,你會打算獲取實際的 OpenCV 庫。
將目錄更改到服務器的根目錄,或者你想放置工作區(qū)的地方:
首先,讓我們?yōu)樽约褐谱饕粋€漂亮的工作目錄:
mkdir opencv_workspacecd opencv_workspace
既然我們完成了,讓我們獲取 OpenCV。
我們這里克隆了 OpenCV 的最新版本。現(xiàn)在獲取一些必需品。
編譯器:sudo apt-get install build-essential
庫:sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
Python 綁定:sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
最后,讓我們獲取 OpenCV 開發(fā)庫:
sudo apt-get install libopencv-dev
現(xiàn)在,我們該如何完成這個過程呢?所以當你想建立一個 Haar Cascade 時,你需要“正片”圖像和“底片”圖像。 “正片”圖像是包含要查找的對象的圖像。這可以是具有對象的主要圖像,也可以是包含對象的圖像,并指定對象所在的 ROI(興趣區(qū)域)。有了這些正片圖像,我們建立一個矢量文件,基本上是所有這些東西放在一起。正片圖像的一個好處是,你可以實際只有一個你想要檢測的對象的圖像,然后有幾千個底片圖像。是的,幾千。底片圖像可以是任何東西,除了他們不能包含你的對象。
在這里,使用你的底片圖像,你可以使用opencv_createsamples命令來創(chuàng)建一堆正片的示例。你的正片圖像將疊加在這些底片上,而且會形成各種各樣的角度。它實際上可以工作得很好,特別是如果你只是在尋找一個特定的對象。但是,如果你正在尋找所有螺絲刀,則需要擁有數(shù)千個螺絲刀的獨特圖像,而不是使用opencv_createsamples為你生成樣品。我們將保持簡單,只使用一個正片圖像,然后用我們的底片創(chuàng)建一堆樣本。
我們的正片圖像:
這是另外一個場景,如果你使用自己的圖像,你可能會更喜歡這個。如果事情出錯了,試試看我的,但是我建議你自己畫一下。保持較小。 50x50像素應該可以。
好吧,獲得正片圖像是沒有問題的!只有一個問題。我們需要成千上萬的底片圖像。可能在未來,我們也可能需要成千上萬的正片圖像。我們可以在世界的哪個地方實現(xiàn)它?基于 WordNet 的概念,有一個非常有用的站點叫做 ImageNet。從這里,你可以找到幾乎任何東西的圖像。我們這里,我們想要手表,所以搜索手表,你會發(fā)現(xiàn)大量種類的手表。讓我們檢索電子表。真棒!看看下載標簽!存在用于所有電子表手表的 URL!很酷。好吧,但我說過我們只會使用一個正片,所以我們只是檢測一個手表。如果你想檢測“全部”手表,需要準備獲取多余 50,000 個手表圖像,至少 25000 個“底片”的圖像。之后,準備足夠的服務器,除非你想要你的 Haar Cascade 訓練花一個星期。那么我們如何得到底片? ImageNet 的全部重點是圖像訓練,所以他們的圖像非常具體。因此,如果我們搜索人,汽車,船只,飛機......無論什么,都不會有手表。你可能會看到一些人或類似的東西,但你明白了。既然你可能看到人周圍或上面的手表,我其實認為你也會得到人的圖像。我的想法是尋找做運動的人,他們可能沒有戴電子表。所以,我們來找一些批量圖片的 URL 鏈接。我發(fā)現(xiàn)體育/田徑鏈接有 1,888 張圖片,但你會發(fā)現(xiàn)很多這些都是完全損壞的。讓我們再來找一個:
好吧,我們擁有所有這些圖片,現(xiàn)在呢?那么,首先,我們希望所有這些大小都相同,而且要小很多!天哪,只要我們知道一個方法來操作圖像...嗯...哦,這是一個 OpenCV 教程!我們可以處理它。所以,首先,我們要做的就是編寫一個簡單的腳本,訪問這些 URL 列表,獲取鏈接,訪問鏈接,拉取圖像,調整大小,保存它們,然后重復,直到完成。當我們的目錄充滿圖像時,我們還需要一種描述圖像的描述文件。對于正片,手動創(chuàng)建這個文件特別痛苦,因為你需要指定你的對象,每個圖像的具體的興趣區(qū)域。幸運的是,create_samples方法將圖像隨機放置,并為我們做了所有工作。我們只需要一個用于底片的簡單描述符,但是這不是問題,在拉伸和操作圖像時我們可以實現(xiàn)。
www.youtube.com/embed/z_6fP…
在任何你喜歡的地方隨意運行這個代碼。 我要在我的主機上運行它,因為它應該快一點。 你可以在你的服務器上運行。 如果你想使用cv2模塊,請執(zhí)行sudo apt-get install python-OpenCV。 目前,我不知道在 Linux 上為 Python 3 獲得這些綁定的好方法。 我將要寫的腳本是 Python 3,所以記住這一點。 主要區(qū)別是Urllib處理。
很簡單,這個腳本將訪問鏈接,抓取網(wǎng)址,并繼續(xù)訪問它們。從這里,我們抓取圖像,轉換成灰度,調整大小,然后保存。我們使用一個簡單的計數(shù)器來命名圖像。繼續(xù)運行它。你可能看到,有很多確實的圖片等。沒關系。這些錯誤圖片中的一些更有問題?;旧隙际前咨瑤в幸恍┪谋?,說他們不再可用,而不是服務和 HTTP 錯誤?,F(xiàn)在,我們有幾個選擇。我們可以忽略它們,或者修復它。嘿,這是一個沒有手表的圖像,所以什么是對的呢?當然,你可以采取這種觀點,但如果你為正片使用這種拉取方式的話,這肯定是一個問題。你可以手動刪除它們...或者我們可以使用我們新的圖像分析知識,來檢測這些愚蠢的圖像,并將其刪除!
我繼續(xù)生成了一個新的目錄,稱之為“uglies(丑陋)”。在那個目錄中,我點擊并拖動了所有丑陋的圖像版本(只是其中之一)。在底片中我只發(fā)現(xiàn)了一個主犯,所以我只有一個。讓我們編寫一個腳本來查找這個圖像的所有實例并刪除它。
www.youtube.com/embed/t0HOV…
def find_uglies(): match = False for file_type in ['neg']: for img in os.listdir(file_type): for ugly in os.listdir('uglies'): try: current_image_path = str(file_type)+'/'+str(img) ugly = cv2.imread('uglies/'+str(ugly)) question = cv2.imread(current_image_path) if ugly.shape == question.shape and not(np.bitwise_xor(ugly,question).any()): print('That is one ugly pic! Deleting!') print(current_image_path) os.remove(current_image_path) except Exception as e: print(str(e))
現(xiàn)在我們只有底片,但是我留下了空間讓你輕易在那里添加'pos'(正片)。 你可以運行它來測試,但我不介意先抓住更多的底片。 讓我們再次運行圖片提取器,僅僅使用這個 url://image-net.org/api/text/imagenet.synset.geturls?wnid=n07942152。 最后一張圖像是#952,所以讓我們以 953 開始pic_num,并更改網(wǎng)址。
現(xiàn)在我們有超過2000張照片。 最后一步是,我們需要為這些底片圖像創(chuàng)建描述符文件。 我們將再次使用一些代碼!
def create_pos_n_neg(): for file_type in ['neg']: for img in os.listdir(file_type): if file_type == 'pos': line = file_type+'/'+img+' 1 0 0 50 50\n' with open('info.dat','a') as f: f.write(line) elif file_type == 'neg': line = file_type+'/'+img+'\n' with open('bg.txt','a') as f: f.write(line)
運行它,你有了個bg.txt文件。 現(xiàn)在,我知道有些人的互聯(lián)網(wǎng)連接可能不是最好的,所以我做個好人,在這里上傳底片圖片和描述文件。 你應該通過這些步驟。 如果你對本教程感到困擾,則需要知道如何執(zhí)行這部分。 好吧,所以我們決定我們將一個圖像用于正片前景圖像。 因此,我們需要執(zhí)行create_samples。 這意味著,我們需要將我們的neg目錄和bg.txt文件移動到我們的服務器。 如果你在服務器上運行所有這些代碼,不要擔心。
www.youtube.com/embed/eay7C…
如果你是一個術士,并已經(jīng)想出了如何在 Windows 上運行create_samples等,恭喜! 回到服務器的領地,我的文件現(xiàn)在是這樣的:
你可能沒有info目錄,所以繼續(xù)并mkdir info。 這是我們放置所有正片圖像的地方。
我們現(xiàn)在準備根據(jù)watch5050.jpg圖像創(chuàng)建一些正片樣本。 為此,請在工作區(qū)中通過終端運行以下命令:
opencv_createsamples -img watch5050.jpg -bg bg.txt -info info/info.lst -pngoutput info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
這樣做是基于我們指定的img創(chuàng)建樣本,bg是背景信息,我們將輸出info.list(很像bg.txt文件)的信息,然后-pngoutput就是我們想要放置新生成的圖像的任何地方。 最后,我們有一些可選的參數(shù),使我們的原始圖像更加動態(tài)一些,然后用= num來表示我們想要創(chuàng)建的樣本數(shù)量。 太棒了,讓我們來運行它。 現(xiàn)在你的info目錄應該有約 2,000 個圖像,還有一個名為info.lst的文件。 這個文件基本上是你的“正片”文件。 打開它,并且看看它怎么樣:
首先你有文件名,之后是圖像中有多少對象,其次是它們的所有位置。 我們只有一個,所以它是圖像中對象矩形的x,y,寬度和高度。 這是一個圖像:
很難看到它,但如果你很難看到,手表就是這個圖像。 圖像中最左側人物的左下方。 因此,這是一個“正片”圖像,從另外一個“底片”圖像創(chuàng)建,底片圖像也將用于訓練。 現(xiàn)在我們有了正片圖像,現(xiàn)在我們需要創(chuàng)建矢量文件,這基本上是一個地方,我們將所有正片圖像拼接起來。我們會再次為此使用opencv_createsamples!
opencv_createsamples -info info/info.lst -num 1950 -w 20 -h 20 -vec positives.vec
這是我們的矢量文件。 在這里,我們只是讓它知道信息文件的位置,我們想要在文件中包含多少圖像,在這個矢量文件中圖像應該是什么尺寸,然后才能輸出結果。 如果你愿意的話,你可以做得更大一些,20×20可能足夠好了,你做的越大,訓練時間就會越長。 繼續(xù),我們現(xiàn)在只需要訓練我們的層疊。
首先,我們要把輸出放在某個地方,所以讓我們創(chuàng)建一個新的數(shù)據(jù)目錄:
mkdir data,你的工作空間應該如下所示:
現(xiàn)在讓我們運行訓練命令:
opencv_traincascade -data data -vec positives.vec -bg bg.txt -numPos 1800 -numNeg 900 -numStages 10 -w 20 -h 20
在這里,我們表明了,我們想要數(shù)據(jù)去的地方,矢量文件的位置,背景文件的位置,要使用多少個正片圖像和底片圖像,多少個階段以及寬度和高度。請注意,我們使用的numPos比我們少得多。這是為了給階段騰出空間。
有更多的選擇,但這些就夠了。這里主要是正片和底片的數(shù)量。一般認為,對于大多數(shù)實踐,你需要 2:1 比例的正片和底片圖像。有些情況可能會有所不同,但這是人們似乎遵循的一般規(guī)則。接下來,我們擁有階段。我們選擇了 10 個。你至少要 10-20 個,越多需要的時間越長,而且是指數(shù)級的。第一階段通常很快,第五階段要慢得多,第五十個階段永遠不會做完!所以,我們現(xiàn)在執(zhí)行 10 個階段。這里不錯的事情是你可以訓練 10 個階段,稍后再回來,把數(shù)字改成 20,然后在你離開的地方繼續(xù)。同樣的,你也可以放一些像 100 個階段的東西,上床睡覺,早上醒來,停下來,看看你有多遠,然后用這些階段“訓練”,你會立即得到一個層疊文件。你可能從最后一句話中得出,這個命令的結果確實很棒,是個不錯的層疊文件。我們希望能檢測到我的手表,或者你決定檢測的任何東西。我所知道的是,在輸出這一段的時候,我還沒有完成第一階段的工作。如果你真的想要在一夜之間運行命令,但不想讓終端打開,你可以使用nohup:
這使命令即使在關閉終端之后也能繼續(xù)運行。 你可以使用更多,但你可能會或可能不會用完你的 2GB RAM。
www.youtube.com/embed/-Mhy-…
在我的 2GB DO 服務器上,10 個階段花了不到 2 個小時的時間。 所以,要么有一個cascade.xml文件,要么停止腳本運行。 如果你停止運行,你應該在你的data目錄下有一堆stageX.xml文件。 打開它,看看你有多少個階段,然后你可以使用這些階段,再次運行opencv_traincascade,你會立即得到一個cascade.xml文件。 這里,我只想說出它是什么,以及有多少個階段。 對我來說,我做了 10 個階段,所以我將它重命名為watchcascade10stage.xml。 這就是我們所需的,所以現(xiàn)在將新的層次文件傳回主計算機,放在工作目錄中,讓我們試試看!
import numpy as npimport cv2face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')#this is the cascade we just made. Call what you wantwatch_cascade = cv2.CascadeClassifier('watchcascade10stage.xml')cap = cv2.VideoCapture(0)while 1: ret, img = cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) # add this # image, reject levels level weights. watches = watch_cascade.detectMultiScale(gray, 50, 50) # add this for (x,y,w,h) in watches: cv2.rectangle(img,(x,y),(x+w,y+h),(255,255,0),2) for (x,y,w,h) in faces: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) cv2.imshow('img',img) k = cv2.waitKey(30) & 0xff if k == 27: breakcap.release()cv2.destroyAllWindows()
你可能注意到,手表的方框很小。 它似乎并沒有達到整個手表。 回想一下我們的訓練規(guī)模是20x20。 因此,我們最多有個20x20的方框。 你可以做100x100,但是,要小心,這將需要很長時間來訓練。 因此,我們不繪制方框,而是,為什么不在手表上寫字或什么東西? 這樣做相對簡單。 我們不在手表上執(zhí)行cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2),我們可以執(zhí)行:
很酷! 所以你可能沒有使用我的手表,你是怎么做的? 如果遇到麻煩,請嘗試使用與我完全相同的所有內容。 檢測圖像,而不是檢測攝像頭,這里是一個:
在圖像上運行檢測會給你:
我不了解你,但一旦我最終使其工作,我非常興奮!最讓我印象深刻的是,跟蹤對象所需的數(shù)據(jù)大小。Haar Cascades 往往是 100-2000 KB 的大小。大于等于 2,000 KB 的 Haar Cascades 應該非常準確??紤]你的情況,你可能會遇到約 5000 個一般物體??紤] Haar Cascades 平均可能是約 500 KB。我們需要:0.5 MB * 5,000 = 2,500 MB或 2.5 GB。你需要 2.5 GB 的內存來識別 5000 個對象,并且可能是你在一天中遇到的最多對象。這讓我著迷??紤]到我們可以訪問所有的 image-net,并可以立即拉取很大范圍的對象的一般圖像??紤] image-net 上的大多數(shù)圖像,基本上都是 100% 的“跟蹤”對象,因此,你可以通過手動標注位置,并僅使用 0,0 和圖像的全部大小來得到正確的結果。這里你可以做的事情有巨大可能...好吧,那就是現(xiàn)在。我將來可能會用 OpenCV 做一些字符識別。如果你有任何其他要求,請發(fā)送電子郵件,在社區(qū)中提出建議,或張貼在視頻上。
享受你的新力量。好好利用它們。
聯(lián)系客服