發現你的身形——OpenCV圖像輪廓

寫在最前

我的意思不是說你長得很胖,emmmm,而是你的輪廓很大。
——五星上將詹姆斯下士如是說

妹子 胸大
果然有圖沒圖,理解是不一樣的,這就體現了計算機視覺的重要性,2333
上一節最後,我們說過這一次我們就將會講解真正的OpenCV圖像輪廓有關知識。輪廓發現的具體實現有多種方式,不過其實其使用在OpenCV中的使用並不困難,不過想用好還需要多點基礎知識。所以這裏我們會首先講一講OpenCV中的輪廓發現算法,然後再講一講其他可以用於輪廓發現的特殊方法。這裏我們主要使用了兩種來自於opencv官方的圖片(Note:上面那張不是),第一張是彩色快樂魚,第二張是水果分屍圖不對,應該是果繽紛纔對。\(^o^)/~

魚

輪廓發現算法

opencv中的輪廓發現算法來自於1985的一篇論文《TopologicalStructural Analysis of Digitized Binary Images by Border Following》,這篇論文主要講了兩種可有效利用於輪廓發現的邊界跟蹤算法,其中第一種是區分二值圖像邊界之間的保衛關係(surroundness relations)第二種是第一種算法的變形,只跟蹤最外層輪廓。對於具體內容這裏不再過多描述,你可以自行查閱。至於具體的使用,我們直接使用OpenCV中的函數findContours即可。

由於opencv中的輪廓發現借鑑於論文《 Analysis of Digitized Binary Images by Border Following》,所以其只能接受二值化之後的圖像,也就是說我們需要先對原始圖像進行二值化處理,這裏需要使用函數cvtColor將圖片進行類型轉化,具體代碼如下:

# 灰度圖像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化
ret, binary = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY)

其中COLOR_BGR2GRAY的作用一如其名,就是將BGR圖像轉化爲GRAY(灰度圖,又名遺照)

灰度圖 魚

之後我們就可以使用 cv2.threshold方法進行二值化處理,就這樣一條彩色的快樂魚就變成了一張黑白的快樂魚遺照了。其中要注意的是,閾值是由我們自己選定的(這裏是175),我們也可以通過修改閾值來獲取新的二值化參數。

魚 二值化

最終,我們就可以將二值化之後的圖像傳進輪廓發現算法,進行輪廓發現並繪製。代碼與結果如下:

# 輪廓發現
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 輪廓繪製
cv2.drawContours(img, contours_new, -1, (0, 0, 255), 3)

魚 輪廓圖

現在快樂魚就被慘遭“分屍”了,嘴巴上多了一條大口子。而說回正事,上面的代碼中drawContours函數就是繪製函數,第一個參數是需要繪製的原圖像,第二個參數是之前我們使用算法發現的輪廓,第三個參數則表示繪製的輪廓索引,這裏使用-1表示繪製所有輪廓,第四個參數則是繪製輪廓時線條的顏色,這裏我們選擇紅色0。第五個參數則表示線條粗細度,如果是負值則在輪廓內部繪製。除此之外還有一個隱身的六娃lineType表示線條類型,因爲有默認數值LINE_8,所以這裏沒有設置。

不過除了這些基本的操作外,我們還可以有很多其他的操作,比如藉助之前我們學過的濾波函數對圖像進行處理,去除那些意義不大的小色塊,或者使用邊緣檢測算法如Canny先一步獲取圖形邊緣(代替直接二值化哪一步),然後再進行輪廓發現,這些操作避免發現錯誤輪廓,同時我們也可以使用判斷方法選擇一定周長或者面積的輪廓。前者直接使用cv.blur(src_gray, (3,3)),以及函數canny_output = cv.Canny(gray, threshold, threshold * 2)即可,後者則需要幾個opencv的函數進行配合,具體代碼如下:

# Detect edges using Canny
threshold = 100
# Detect edges using Canny
canny_output = cv2.Canny(gray, threshold, threshold * 2)
for i in range(len(contours)):
    # 計算輪廓所包含的面積
    area = cv2.contourArea(contours[i])
    # 計算輪廓的周長
    perimeter = cv2.arcLength(contours[i], True)
    
    if perimeter >= 10 and area >= 20:
        print("第{0}個輪廓的面積爲{1},周長爲{2}".format(i+1,area,perimeter))
        contours_new.append(contours[i])

邊緣檢測 魚

魚 繪製輪廓

第一段代碼的作用是使用Canny進行邊緣檢測(展示的圖片也經過了均值模糊),然後得到一張只有邊緣數據的圖,這樣就避免了之前直接二值化產生的像素閾值產生的本來不存在的誤差,而第二行代碼則是利用計算輪廓面積和計算輪廓周長的函數進行輪廓篩選。其實這裏我們也可以選擇不進行篩選直接進行獲取,但是在一般情況下

均值模糊,邊緣檢測,周長,面積等都是十分有用的操作,至於怎麼用則需要根據實際情況進行調整。

邊緣檢測

這裏還提到了邊緣檢測,邊緣檢測顧名思義就是檢測圖片的邊緣。在一些特殊情況下其實邊緣檢測比直接輪廓獲取有用,不過也有些時候圖像分割的一些技術更有用一些。這幾項技術的特點都是作用於圖像中的區域對其進行處理。只不過在沒有深度學習的時代,沒有像目標檢測,或者現代的更直接的自動進行圖像分割的技術,只能使用各類算法進行手動操作,從不同的圖片中手動設置不同的方法獲取我們想要的信息。

Canny就是一種比較常見的邊緣檢測方法,下面就是一個例子。我們將會在下一屆仔細的講講的邊緣檢測等有關圖像處理技術。

水果,OpenCV
水果 邊緣檢測 OpenCV

寫在最後

大概寫到這個時候感覺確實可以出個專欄了,所以正式規劃了《漫談計算機視覺》,頻率大概會一週一更,大概會從OpenCV將其,然後一直串到這兩年各個計算機視覺領域比較經典深度神經網絡。同時代碼會開源,之後代碼和有關數據前往FontTian的Github下載即可,包括之前的內容都會穿上去,項目位置在這:https://github.com/FontTian/Gossipage_About_CV。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章