1 引言
上一篇使用傳統方法進行石頭檢測的文章,受到了大家一致的好評.
嗯嗯,應該是一致的好評!
今天我們來研究一個新的方向,如何使用傳統方法來進行圓的檢測.

如上圖所示,存在多個形狀各異的物體,這里原型物體只有兩個左下角的白色藥片,
這里我們需要用影像處理的方法來檢測出兩個圓形物體,并計算圓形物體相應的面積.
2 解決方案
2.1 二值化
傳統影像分割一般采用閾值分割的方法來區分前景和背景,
這里我們采用OTSU的方法來進行二值化操作,方法如下:
image_file = './images/pillsetc.png'
# Step 1: Read image
bgr_image = cv2.imread(image_file,cv2.IMREAD_COLOR)
# Step 2: Threshold the image
gray_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2GRAY)
threshold, thresh_image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print('Graylevel threshold computed with otsu: ', threshold)
運行后結果如下:

上圖中,左側為原圖,右側為二值化后的結果圖.
2.2 影像去燥
觀察上圖二值化后的影像,存在一些零零散散的噪聲,這里我們來想辦法去除這些噪聲.
這里推薦使用remove_small_objects這個函式,該函式可以直接去掉零散的小連通區域,我們代碼實作如下:
from skimage import morphology
rmnoise_image = morphology.remove_small_objects(thresh_image.astype(bool), min_size=30)
上述代碼,直接將面積小于30的小連通區域和孤點噪聲直接剔除,運行結果如下:

上圖中,左側為去燥前的效果圖,右側為去燥后的效果圖,可以看出很多零散的噪聲和小的連通區域均被濾除.
2.3 消除空洞
觀察上述去燥后的影像,注意查看下圖綠色接頭(紅色框內)的二值影像,我們發現存在空洞,這里可以采用先膨脹在腐蝕的操作(閉運算),來消除空洞,代碼如下:
fillcap_image = morphology.binary_closing(rmnoise_image, selem=morphology.disk(2))
運行效果如下:

上圖中左側為閉運算操作前的結果圖,右側為使用閉運算后的效果圖,可以看出矩形框內的綠色轉接頭已經消除中間空洞.
2.4 填充
觀察下圖左側影像.可以看出黃色框內的橢圓物品中間為空心的,這里需要對其進行填充,填充代碼如下:
from scipy.ndimage.morphology import binary_fill_holes
clean_image = binary_fill_holes(fillcap_image)
運行效果如下:

上圖中左側為填充前的結果圖,右側為填充后的效果圖,可以看出矩形框內的橢圓物品由空心經填充變為了實心.
2.5 查找輪廓
我們觀察上圖,可以看出所有物品均被我們處理成一個個獨立的封閉區域,這時我們可以采用opencv下的findContours函式來尋找對應區域的外輪廓.代碼如下:
contours, _ = cv2.findContours(clean_image.astype(np.uint8), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
h,w = clean_image.shape
obj_image = np.zeros(shape=(h,w,3),dtype=np.uint8)
color_list=[(94,17,43),(128,31,113),(121,54,180),(118,173,253),(190,251,250),(93,96,239)]
for ind, cnt in enumerate(contours):
cv2.drawContours(obj_image, [cnt], 0,color_list[ind], -1)
運行效果如下:

上圖中左側為輸入原圖,右側為我們尋找出的外輪廓,并對其進行輪廓填充后的結果圖.
2.6 圓的判斷
經過上述操作,我們已經可以得到每個物品的外輪廓,接著我們回到我們原來的問題,怎么來判斷那個物體是圓形呢?
首先我們來回顧一下圓的性質:
- 圓的面積 A r e a = π R 2 Area= \pi R^2 Area=πR2
- 圓的周長 P e r i m e t e r = 2 ? π ? R Perimeter=2*\pi*R Perimeter=2?π?R
接著我們定義 α \alpha α,由面積和周長組成,計算公式如下:
α = 4 ? π ? A r e a P e r i m e t e r 2 \alpha=\frac{4*\pi*Area}{Perimeter^2} α=Perimeter24?π?Area?
對于圓形物體,我們將圓的面積和周長計算公式代入,可以知道
α
=
1
\alpha=1
α=1.
對于正方形物體來說,我們將正方形的面積和周長計算公式代入,
α
=
4
?
π
?
A
r
e
a
P
e
r
i
m
e
t
e
r
2
=
4
?
π
?
a
2
(
4
?
a
)
2
=
π
4
<
1
\alpha=\frac{4*\pi*Area}{Perimeter^2}=\frac{4*\pi*a^2}{(4*a)^2}=\frac{\pi}{4}<1
α=Perimeter24?π?Area?=(4?a)24?π?a2?=4π?<1
總上,我們可以得到以下結論:
如果一個物體的輪廓所計算出的
α
\alpha
α值越接近于1,那么該輪廓越趨向于圓形物體輪廓.
2.7 計算面積周長
經過2.6節的簡單推導,我們已經知道如何判斷一個輪廓是否趨向于圓形,基于此我們撰寫代碼如下:
for ind, contour in enumerate(contours):
perimeter = cv2.arcLength(contour, True)
area = cv2.contourArea(contour)
alpha = 4*np.pi*area/(perimeter**2)
print('{0:>6} {1:>9,.0f} {2:>9,.2f} {3:>9,.3f}'.format(ind, area, perimeter, alpha))
# Compute moments in order to find the centroid of objects
moments = cv2.moments(contour)
cx = int(moments['m10'] / moments['m00'])
cy = int(moments['m01'] / moments['m00'])
cv2.putText(obj_image,str(ind),(cx,cy),cv2.FONT_HERSHEY_SIMPLEX,0.5,color=(0,128,0),thickness=2)
# Plot a red circle on the centroid of the round objects
if alpha > round_thresh:
cv2.circle(obj_image,(cx,cy),2,color=(0,0,255),thickness=2)
運行后的結果如下:
Object Area Perimeter Roundness
0 882 110.57 0.907
1 876 110.57 0.900
2 16,928 543.05 0.721
3 3,026 261.12 0.558
4 8,074 338.88 0.884
5 4,908 264.01 0.885
可視化結果如下:

上圖中,左側為輸入原圖,右側為我們圓檢測后的結果圖,圖中紅色圓點代表我們識別出的圓形物體的質心(也就是圓心).
同時根據定量結果輸出我們可以得到以下結論:
- 0號物體和1號物體為檢測出的圓形物體,紅色小點為對應的圓心
- 0號物體的周長為110.57,面積為882
- 1號物體的周長為110.57,面積為876
3 總結
本文介紹了使用傳統影像處理方法進行圓形物體檢測的基本方法,并根據圓的性質給出了如何根據輪廓的面積和周長來判斷輪廓是否為圓的輪廓的方法.
您學廢了嗎?
關注公眾號《AI演算法之道》,獲取更多AI演算法資訊,

注: 關注公眾號,后臺回復 圓 , 可獲取完整代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/339014.html
標籤:AI
