Python OpenCV 365 天學習計劃,與橡皮擦一起進入影像領域吧,本篇博客是這個系列的第 48 篇,
該系列文章導航參考:https://blog.csdn.net/hihell/category_10688961.html
Python OpenCV
- 學在前面
- 輪廓檢測與輪廓特征
- cv2.findContours 函式
- 回傳值 contours
- 輪廓特征
- 矩
- 輪廓面積
- 輪廓周長
- 外接矩形
- 其余補充學習
- 橡皮擦的小節
學在前面
影像金字塔學習的時候,就要想著有個金字塔在你眼前,這個金字塔最底部是你的原影像(源影像),
關于影像金字塔的基本知識,可以翻閱咱們之前的博客 Python OpenCV 之影像金字塔,高斯金字塔與拉普拉斯金字塔 學習,
學習高斯金字塔首先接觸的概念是,向下采樣方法,注意在金字塔,向下是縮小圖片的含義,越靠近金字塔頂部,影像越小,相應的向上采樣法,是方法影像,
好了,影像金字塔一點點的補充已經完畢,
輪廓檢測與輪廓特征
輪廓檢測的基礎學習,請參照 Python OpenCV 基于影像邊緣提取的輪廓發現函式 這篇博客,今天要補充的內容是在進行影像輪廓檢測的時候,cv2.findContours 函式中輪廓檢索模式引數,一般情況下建議使用 RETR_TREE 也就是檢測所有輪廓,
查看一下測驗代碼吧,
import cv2 as cv
src = cv.imread("./t223.jpg")
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
gaussian = cv.GaussianBlur(gray, (3, 3), 0)
edges = cv.Canny(gaussian, 70, 210)
# 尋找輪廓
contours, hierarchy = cv.findContours(
edges, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 繪制輪廓
cv.drawContours(src, contours, -1, (0, 0, 255), 1)
cv.imshow('src', src)
cv.waitKey(0)
尋找邊緣使用的是 Canny 函式,找到所有邊緣之后,通過 cv2.drawContours 函式繪制輪廓,

在學習 cv2.drawContours 函式的時候需要注意,有的博客中會提示使用該函式在原圖繪制輪廓之后,會將原圖進行覆寫,但是在橡皮擦使用的這個 opencv 版本中,并未出現上述情況,可能是不同版本導致的,注意下即可,
cv2.findContours 函式
該函式有兩個回傳值,contours 與 hierarchy,其中一個是輪廓本身,另一個就是每條輪廓對應的屬性,
整體測驗代碼與影像使用下述內容,
import cv2 as cv
src = cv.imread("./t22331.jpg")
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
gaussian = cv.GaussianBlur(gray, (3, 3), 0)
edges = cv.Canny(gaussian, 50, 150)
# 尋找輪廓
contours, hierarchy = cv.findContours(
edges, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 繪制輪廓
cv.drawContours(src, contours, -1, (0, 0, 255), 2)
cv.imshow('src', src)
cv.waitKey(0)

回傳值 contours
通過下述代碼,先確定一下該引數的基本資料,
# 輪廓詳情
print(type(contours))
print(type(contours[0]))
print(len(contours))
得到的結果是:
<class 'list'>
<class 'numpy.ndarray'>
46
contours 引數是 list 型別、其中每一項都是 numpy.ndarray 型別,串列的長度等于輪廓數量,
合計獲取到 46 個輪廓,可以根據索引去繪制制定輪廓,例如:
# 繪制輪廓
cv.drawContours(src, contours, 1, (0, 0, 255), 2)
cv.drawContours(src, contours, 45, (0, 0, 255), 2)
回傳值 hierarchy 暫時略過,因為和接下來的內容關聯性不強,
輪廓特征
這部分內容又叫做物件測量,在Python OpenCV 物件檢測,影像處理取經之旅第 37 篇 進行了最基礎的學習,本篇繼續對其進行擴展,
咱們的首要目標是學會通過呼叫方法檢測輪廓的不同特征,例如面積、周長、質心、邊界框,
這部分在學習的時候,會用到大量的常見場景和數學知識,由于咱們現在還沒有辦法將這些內容直接應用到真實的案例中,所以數學相關知識與應用場景后置,先學習應用層,了解不同函式實作的結果即可,
接下來拋出不同的概念吧,
矩
在這個階段,只需要知道矩指的是影像的矩,它可以幫助我們計算影像的質心,面積等內容,如果你想提前學習一下數學相關的內容,我也幫你把目前橡皮擦能找到的幾篇不錯的博客貼了出來,你可以先學習一下,后面我們也會迭代學習到,
- 影像的矩特征
- 矩、中心矩、質心、patch 方向
- 影像中矩的概念
大佬們還是咱們努力學習的方向呀,相信不久就能再見面了,
以上內容你可以直接略過,進入正題
先在工具中輸入如下代碼,獲取運行結果,方便后面的學習:
import cv2 as cv
src = cv.imread("./t22331.jpg")
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
gaussian = cv.GaussianBlur(gray, (3, 3), 0)
edges = cv.Canny(gaussian, 50, 150)
# 尋找輪廓
contours, hierarchy = cv.findContours(
edges, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 繪制輪廓
cv.drawContours(src, contours, 1, (0, 0, 255), 2)
cv.drawContours(src, contours, 45, (0, 0, 255), 2)
# 選中的第一個輪廓
cnt = contours[0]
# 通過 moments 函式計算的所有矩值的字典
M = cv.moments(cnt)
print(M)
運行之后展示的內容如下:
{'m00': 51.0, 'm10': 12277.833333333332, 'm01': 13028.166666666666, 'm20': 2956012.333333333, 'm11': 3136429.25, 'm02': 3328298.333333333, 'm30': 711743823.85, 'm21': 755128136.4666667, 'm12': 801262936.2, 'm03': 850328986.1500001, 'mu20': 224.26742919441313, 'mu11': 4.5642701531760395, 'mu02': 197.80991285433993, 'mu30': 23.924903512001038, 'mu21': 30.072836493171053, 'mu12': -27.494554918412177, 'mu03': -25.694735527038574, 'nu20': 0.0862235406360681, 'nu11': 0.0017548135921476508, 'nu02': 0.0760514851419992, 'nu30': 0.0012880263706323274, 'nu21': 0.0016190078435839353, 'nu12': -0.0014802029093220657, 'nu03': -0.0013833074364813173}
注意看都是以 m.. 或者 nu.. 開始的各種資料,
輪廓面積
輪廓的面積可以使用函式 cv2.contourArea() 計算得到,也可以使用矩(0 階矩), 即剛才結果中的 M["m00"] 獲取,
# 輪廓面積
area = cv.contourArea(cnt)
print(area)
# 0 階矩
print(M['m00'])
輪廓周長
也稱為弧長,使用函式 cv.arcLength() 計算得到,該函式的第二引數可以用來指定物件的形狀是閉合的(True),還是打開的(一條曲線),如果曲線閉合,那以上 2 種方法計算結果一致,如果是開曲線,則兩者計算結果不同,其中閉合的方法,會在最后將起始點和終止點連一起的長度加進去,
# 輪廓周長
perimeter = cv.arcLength(cnt,True)
print(perimeter)
后面的內容因為涉及到不同的輪廓,為了檢測出希望操作的輪廓,我遍歷了所有輪廓,找到了周長合適的那個圓形,
for index in range(len(contours)):
print("索引是:",index)
cnt = contours[index]
# 通過 moments 函式計算的所有矩值的字典
M = cv.moments(cnt)
# print(M)
# 輪廓面積
area = cv.contourArea(cnt)
print(area)
# 0 階矩
print(M['m00'])
# 輪廓周長
perimeter = cv.arcLength(cnt,True)
print(perimeter)
cv.imshow("image",src)
cv.waitKey()

外接矩形
通過下述代碼獲取上圖黃色圓形的外界矩形,外接矩形也叫做邊界矩形或者包圍盒,
它需要找到圖形物件最高點、最低點、最左點、最右點,畫出一個矩形邊界,
使用函式 cv2.boundingRect 計算之后,將結果進行回傳,其中(x,y)為矩形左上角的坐標,(w,h)是矩形的寬和高,
# 外接矩形
x, y, w, h = cv.boundingRect(cnt)
cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv.imshow("src",src)
cv.waitKey()
還可以繪制圓形的最小外接矩形,也叫做旋轉邊界包圍盒,例如下述代碼,回傳 Box2D 結構,包含左上角坐標(x,y),矩形寬,高 (w,h),以及旋轉角度,
# 最小外接矩形
rect = cv.minAreaRect(cnt)
box = np.int0(cv.boxPoints(rect))
cv.drawContours(src, [box], 0, (255, 0, 0), 2)
cv.imshow("src",src)
整體運行結果,略微存在差異,

其余補充學習
有了上述的基本知識概念之后,剩下的就非常容易學習了
最小外接圓
代碼如下,這里我切換了一張圖片,畢竟用圓形做外接圓不太合適,
(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(src,center,radius,(0,255,0),2)
cv.imshow("src",src)
cv.waitKey()

橢圓擬合
ellipse = cv.fitEllipse(cnt)
cv.ellipse(src,ellipse,(0,255,0),2)
擬合一條線
# 擬合一條直線
rows, cols = src.shape[:2]
[vx, vy, x, y] = cv.fitLine(cnt, cv.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv.line(src, (cols-1, righty), (0, lefty), (0, 255, 0), 2)

以上所有方法的前提都是找到輪廓,如果沒有找到輪廓,所有函式都不會有結果展示,
輪廓近似與凸包相關知識點,依舊后置,這些知識的學習沒有應用場景,很容易被遺忘,
相關資料提前學習,可以檢索 cv2.approxPolyDP 與 cv2.convexHull 函式,
輪廓性質可以由輪廓特征計算得出,包括但不限于,寬高比、輪廓面積與邊界矩形面積的比、 輪廓面積與凸包面積的比、獲取與輪廓面積相等的圓形直徑、方向、輪廓的掩膜與像素點、最大值和最小值及它們的位置、平均顏色及平均灰度、極點、凸缺陷、形狀匹配
橡皮擦的小節
希望今天的 1 個小時你有所識訓,我們下篇博客見~
相關閱讀
技術專欄
- Python 爬蟲 100 例教程,超棒的爬蟲教程,立即訂閱吧
- Python 爬蟲小課,精彩 9 講
今天是持續寫作的第 90 / 100 天,
如果你想跟博主建立親密關系,可以關注同名公眾號 夢想橡皮擦,近距離接觸一個逗趣的互聯網高級網蟲,
博主 ID:夢想橡皮擦,希望大家點贊、評論、收藏,
CSDN認證博客專家
高級產品經理
互聯網從業者
業余編程愛好者
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263453.html
標籤:python
上一篇:Python演算法的分享(一)
