OpenCV-Python實(shí)現(xiàn)輪廓擬合
前言
什么是輪廓?
輪廓可以簡單認(rèn)為成將連續(xù)的點(diǎn)(連著邊界)連在一起的曲線,具有相同 的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。
- 為了更加準(zhǔn)確,要使用二值化圖像。在尋找輪廓之前,要進(jìn)行閾值化處理 或者 Canny 邊界檢測。
- 查找輪廓的函數(shù)會修改原始圖像。如果你在找到輪廓之后還想使用原始圖 像的話,你應(yīng)該將原始圖像存儲到其他變量中。
- 在 OpenCV 中,查找輪廓就像在黑色背景中超白色物體。你應(yīng)該記住, 要找的物體應(yīng)該是白色而背景應(yīng)該是黑色。
在計(jì)算輪廓時(shí),可能并不需要實(shí)際的輪廓,而僅需要一個(gè)接近于輪廓的近似多邊形。比如矩形其實(shí)都是差不多的輪廓,都是長寬不相等且平行的四邊形,那么只要提供一個(gè)近似的輪廓,我們就可以區(qū)分形狀。
在OpenCV中,它給我們提供了cv2.boundingRect()函數(shù)來繪制輪廓的矩形邊界,其完整定義如下:
def boundingRect(array):
array:前面已經(jīng)介紹過,array是一個(gè)灰度圖像,或者輪廓。
該函數(shù)返回3個(gè)值時(shí),是矩形邊界的左上角頂點(diǎn)的坐標(biāo)值以及矩形邊界的寬與高。返回4個(gè)值時(shí),是矩形左上角頂點(diǎn)的x坐標(biāo),y坐標(biāo),以及寬高。
繪制橢圓的矩形邊界
現(xiàn)在,我們還是使用前面的一張橢圓圖形,如下圖所示:
得到圖形之后,我們使用上面的函數(shù),計(jì)算該圖像輪廓的4值,代碼如下:
import cv2 img = cv2.imread("26_1.jpg") # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) x, y, w, h = cv2.boundingRect(contours[0]) print(x, y, w, h)
運(yùn)行之后,控制臺輸出如下內(nèi)容:
這里我們得到了橢圓的矩形左上角坐標(biāo)為(53,120),其寬高分別為272與84。
既然我們已經(jīng)得到了其矩形邊界的坐標(biāo)以及寬高,那么我們可以開始繪制其矩形邊界。前面提取輪廓繪制用的是cv2.drawContours()函數(shù),這里同樣也是。
代碼如下:
import cv2 import numpy as np img = cv2.imread("26_1.jpg") cv2.imshow("img1",img) # 轉(zhuǎn)換為灰度圖像 gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) x, y, w, h = cv2.boundingRect(contours[0]) rect=np.array([[[x,y],[x+w,y],[x+w,y+h],[x,y+h]]])#1 cv2.drawContours(img,[rect],-1,(255,255,255),2)#1 cv2.imshow("img2",img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,其橢圓的矩形邊界就被我們標(biāo)記出來了,效果如下:
當(dāng)然,這里我們還可以使用另一個(gè)函數(shù)cv2.rectangle()來繪制矩形邊界,值需要更換上面代碼中注釋1的兩個(gè)代碼,具體如下所示:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 255,255),2)
最小包圍矩形框
在OpenCV中,它還提供了cv2.minAreaRect()來繪制最小包圍矩形框,其完整定義如下:
def minAreaRect(points):
其中points參數(shù)是輪廓,返回值為矩形特征信息,包括矩形的中心(x,y),寬高,以及旋轉(zhuǎn)角度。
特別注意,minAreaRect函數(shù)的返回值并不能直接代入drawContours()函數(shù)中。因此,我們必須將其轉(zhuǎn)換為符合要求的結(jié)構(gòu)才能接著操作。通過cv2.boxPoint()函數(shù)就可以轉(zhuǎn)換為符合drawContours()的結(jié)構(gòu)參數(shù)。
還是上面那張圖,不過我們用旋轉(zhuǎn)后的橢圓原圖,代碼如下:
import cv2 import numpy as np img = cv2.imread("26_4.jpg") cv2.imshow("img1",img) # 轉(zhuǎn)換為灰度圖像 gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) rect= cv2.minAreaRect(contours[0]) print(rect) points=cv2.boxPoints(rect) print(points) points=np.int0(points) print(points) cv2.drawContours(img,[points],0,(255,255,255),2) cv2.imshow("img2",img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,圖像效果以及控制臺的輸出信息如下:
這里我們可以清楚的看到minAreaRect()函數(shù)返回值的轉(zhuǎn)換過程。先通過boxPoints()函數(shù)轉(zhuǎn)換為drawContours()函數(shù)能接受的參數(shù)格式,然后通過取整轉(zhuǎn)換為具體的像素坐標(biāo)值。
最小包圍圓形框
既然有最小包圍矩形框,那么一定就有最小包圍圓形框。在OpenCV中,它給我們提供cv2.minEnclosingCircle()函數(shù)來繪制最小包圍圓形框。
函數(shù)的完整定義如下:
def minEnclosingCircle(points):
這里的參數(shù)與上面的points參數(shù)一致,但是其返回值并不相同,畢竟繪制圓形肯定與繪制矩形的參數(shù)肯定不一樣。
它有兩個(gè)返回值,一個(gè)是圓形的中心坐標(biāo)(x,y),一個(gè)是圓形的半徑r。下面,我們直接來繪制上面橢圓的最小包圍圓形框。具體代碼如下所示:
import cv2 import numpy as np img = cv2.imread("26_4.jpg") cv2.imshow("img1", img) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) (x, y), r = cv2.minEnclosingCircle(contours[0]) center = (int(x), int(y)) r = int(r) cv2.circle(img, center, r, (255, 255, 255), 2) cv2.imshow("img2", img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,效果如下所示:
最優(yōu)擬合橢圓
在OpenCV中,它給我們提供了cv2.fitEllipse()函數(shù)繪制最優(yōu)擬合橢圓。其完整的定義如下:
def fitEllipse(points):
其中points參數(shù)與前文一致,而它的返回值是RotatedRect類型,這是因?yàn)樵摵瘮?shù)返回的是擬合橢圓的外接矩形,包括矩形的質(zhì)心,寬高,旋轉(zhuǎn)角度等信息,這些信息正好與橢圓的中心點(diǎn),軸長度,旋轉(zhuǎn)角度一致。
下面,我們來使用該函數(shù)繪制最優(yōu)擬合橢圓,這里我們選取如上圖所示的一張矩形圖。具體代碼如下:
import cv2 img = cv2.imread("27.jpg") cv2.imshow("img1", img) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ellipse = cv2.fitEllipse(contours[0]) cv2.ellipse(img, ellipse, (0, 0, 255), 3) cv2.imshow("img2", img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,效果如下所示:
最優(yōu)擬合直線
在OpenCV中,它還提供了cv2.fitLine()函數(shù)繪制最優(yōu)擬合直線,其完整定義如下:
def fitEllipse(points):
points:與前文一樣,是輪廓
distType:距離類型。擬合直線時(shí),要使輸入點(diǎn)到擬合直線的距離之和最小。詳細(xì)參數(shù)定義參考開發(fā)文檔,這里不在贅述。
param:距離參數(shù),與所選距離類型有關(guān)。當(dāng)該參數(shù)為0時(shí),自動選擇最優(yōu)值。
reps:用于表示擬合直線所需要的徑向精度,通常該值被設(shè)定為0.01
aeps:用于表示擬合直線所需要的角度精度,通常該值被設(shè)定為0.01
對于二維直線,返回值line為4維,前兩維代表擬合出的直線的方向,后兩位代表直線上的一點(diǎn)。
下面,我們來直接使用代碼繪制最優(yōu)擬合直線。
import cv2 img = cv2.imread("27.jpg") cv2.imshow("img1", img) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ellipse = cv2.fitEllipse(contours[0]) cv2.ellipse(img, ellipse, (0, 0, 255), 3) cv2.imshow("img2", img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,效果如下所示:
對于繪制直線來說,我們需要獲取繪制直線的起點(diǎn)以及終點(diǎn),這里lefty為起點(diǎn),righty為終點(diǎn)。
最小外包三角形
在OpenCV,它還提供了cv2.minEnclosingTriangle()函數(shù)來繪制最小外包三角形。其完整定義如下:
def minEnclosingTriangle(points, triangle=None):
其中points與前文類似,其返回值triangle為外包三角形的三個(gè)頂點(diǎn)集。
下面,我們直接構(gòu)建最小外包三角形,具體代碼如下:
import cv2 img = cv2.imread("27.jpg") cv2.imshow("img1", img) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) area, trg1 = cv2.minEnclosingTriangle(contours[0]) print(area) print(trg1) for i in range(0, 3): cv2.line(img, tuple(trg1[i][0]), tuple(trg1[(i + 1) % 3][0]), (0, 255, 0), 2) cv2.imshow("img2", img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,效果如下:
需要注意的是,在cv2中沒有直接繪制三角形的函數(shù),所以我們通過繪制三條直線,繪制三角形,minEnclosingTriangle()函數(shù)第一個(gè)返回值為三角形面積,第二返回值是三點(diǎn)坐標(biāo)。
逼近多邊形
在OpenCV中,它還提供了cv2.approxPolyDP()函數(shù)構(gòu)建指定邊數(shù)的逼近多邊形。其完整定義如下:
def approxPolyDP(curve, epsilon, closed, approxCurve=None):
curve:輪廓
epsilon:精度,原始輪廓的邊界點(diǎn)與逼近多邊形邊界之間的最大距離
closed:布爾類型。為True時(shí),表示逼近多邊形是封閉的。為False時(shí),biao表示畢竟多邊形是不封閉的。
approxCurve為該函數(shù)的返回值,是逼近多邊形的點(diǎn)集。。
下面,我們來實(shí)現(xiàn)各類逼近多邊形的繪制,代碼如下:
import cv2 img = cv2.imread("24.jpg") list=[0.1,0.09,0.055,0.05,0.02] cv2.imshow("img", img) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) for i, val in enumerate(list): epsilon = val * cv2.arcLength(contours[0], True) approx = cv2.approxPolyDP(contours[0], epsilon, True) cv2.drawContours(img, [approx], 0, (0, 255, 0), 2) cv2.imshow("img"+str(i), img) cv2.waitKey() cv2.destroyAllWindows()
運(yùn)行之后,效果如下:
cv2.approxPolyDP()函數(shù)采用的是Douglas-Peucker算法,該算法的原理是首先從輪廓中找到距離最遠(yuǎn)的兩個(gè)點(diǎn),并將兩個(gè)點(diǎn)相連。接下來,在輪廓上找到一個(gè)離當(dāng)前直線最遠(yuǎn)的點(diǎn),并將該點(diǎn)與原有直線連成一個(gè)封閉的多邊形,此時(shí)得到一個(gè)三角形。以此類推四邊形,五邊形,六邊形等。當(dāng)前多邊形的距離都小于函數(shù)cv2.approxPolyDP()的參數(shù)epsilon的值時(shí),就停止迭代。
到此這篇關(guān)于OpenCV-Python實(shí)現(xiàn)輪廓擬合的文章就介紹到這了,更多相關(guān)OpenCV 輪廓擬合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文教會你用python連接并簡單操作SQLserver數(shù)據(jù)庫
最近要將數(shù)據(jù)寫到數(shù)據(jù)庫里,學(xué)習(xí)了一下如何用Python來操作SQLServer數(shù)據(jù)庫,下面這篇文章主要給大家介紹了關(guān)于用python連接并簡單操作SQLserver數(shù)據(jù)庫的相關(guān)資料,需要的朋友可以參考下2022-09-09Python lambda表達(dá)式用法實(shí)例分析
這篇文章主要介紹了Python lambda表達(dá)式用法,結(jié)合實(shí)例形式分析了lambda表達(dá)式的具體功能、應(yīng)用場景及相關(guān)使用技巧,需要的朋友可以參考下2018-12-12利用Python實(shí)現(xiàn)從PDF到CSV的轉(zhuǎn)換
將PDF轉(zhuǎn)換為CSV極大地提升了數(shù)據(jù)的實(shí)用價(jià)值,Python作為一種強(qiáng)大的編程語言,能夠高效完成這一轉(zhuǎn)換任務(wù),本文將介紹如何利用Python實(shí)現(xiàn)從PDF到CSV的轉(zhuǎn)換,需要的朋友可以參考下2024-07-07Django動態(tài)展示Pyecharts圖表數(shù)據(jù)的幾種方法
本文主要介紹了Django動態(tài)展示Pyecharts圖表數(shù)據(jù)的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08編譯 pycaffe時(shí)報(bào)錯(cuò):fatal error: numpy/arrayobject.h沒有那個(gè)文件或目錄
這篇文章主要介紹了編譯 pycaffe時(shí)報(bào)錯(cuò):fatal error: numpy/arrayobject.h沒有那個(gè)文件或目錄,需要的朋友可以參考下2020-11-11Python時(shí)間序列處理之ARIMA模型的使用講解
今天小編就為大家分享一篇關(guān)于Python時(shí)間序列處理之ARIMA模型的使用講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04Python正則表達(dá)式如何進(jìn)行字符串替換實(shí)例
Python正則表達(dá)式在使用中會經(jīng)常應(yīng)用到字符串替換的代碼。這篇文章主要介紹了Python正則表達(dá)式如何進(jìn)行字符串替換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12Django 解決由save方法引發(fā)的錯(cuò)誤
這篇文章主要介紹了Django 解決由save方法引發(fā)的錯(cuò)誤,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05