一種基於opencv的分辨檢測圓形,三角形,矩形的思路

2020-10-23 11:00:24

一種基於opencv的分辨圓形,三角形,矩形的思路

上篇文章給大家講了基於openmv的思路,這篇文章大致講講如何用opencv來做。
我事先查了一下,我這個方法不知道有多少人早就用過了。(可能是因為當時我也是瘋狂查出來的,已經記憶模糊了)他們講的比我詳細多了,我就簡單說說思路。

我的思路是:色塊識別+輪廓提取+角點檢測

1. 色塊識別:

色塊識別是很基礎的一個操作了,也比較簡單。直接看程式碼:

    lower_red_1 = np.array([0, 80, 128]) #先找出HSV色彩空間紅綠藍三種顏色的大致範圍。紅色有兩個是因為hsv空間中,色相h最上面和最下面都是紅色。可以看下面這張圖你就懂了。
    upper_red_1 = np.array([6, 255, 255])
    lower_red_2 = np.array([170, 110, 128])
    upper_red_2 = np.array([180, 255, 255])
    lower_green = np.array([35, 80, 80])
    upper_green = np.array([77, 255, 255])
    lower_blue = np.array([90, 110, 110])
    upper_blue = np.array([124, 255, 255])

    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) #frame就是輸入的影象
    
    red_mask_1 = cv.inRange(hsv,lower_red_1,upper_red_1) #將影象二值化,在lower和upper之間的顏色變為白色,其他全為黑色
    red_mask_2 = cv.inRange(hsv,lower_red_2,upper_red_2)
    red_mask = cv.bitwise_or(red_mask_1, red_mask_2) #兩種紅色統一
    green_mask = cv.inRange(hsv, lower_green, upper_green)
    blue_mask = cv.inRange(hsv, lower_blue, upper_blue)  

HSV色彩空間圖

上面這一段不懂的話可以看看這篇文章,這位博主講得很好(侵刪)

    red_res = cv.bitwise_or(frame, frame, mask = red_mask) #或運算,將彩色影象中紅色部分選中,忽略其餘顏色
    green_res = cv.bitwise_and(frame, frame, mask = green_mask)
    blue_res = cv.bitwise_and(frame, frame, mask = blue_mask)

    red_gray = cv.cvtColor(red_res, cv.COLOR_BGR2GRAY) #轉灰度圖
    green_gray = cv.cvtColor(green_res, cv.COLOR_BGR2GRAY)
    blue_gray = cv.cvtColor(blue_res, cv.COLOR_BGR2GRAY)

    final_gray = cv.bitwise_or(red_res, green_res) #將選出的紅色,藍色,綠色都整合起來
    final_gray = cv.bitwise_or(final_gray, blue_res)
    final_gray = cv.cvtColor(final_gray,cv.COLOR_BGR2GRAY) #得到最終的灰度圖。就是下一步輪廓提取的輸入

2. 輪廓提取
輪廓提取的話,opencv有自己的API,好用的很。

    __, contours,hierarchy = cv.findContours(final_gray, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
#這個地方要注意一下。這個函數根據版本不同,返回的值可能有兩個或者三個。如果opencv版本比較新,就只有後面兩個返回值。如果比較舊(我程式碼是在樹莓派上跑的,所以比較舊),就有三個返回值。不過我們只用到countours這個返回值就行。
    for cnt in range(len(contours)): #對檢測到的每個輪廓遍歷
        p = cv.arcLength(contours[cnt],True) #p是Perimeter周長的意思,當時偷懶了
        area = cv.contourArea(contours[cnt]) #area是該輪廓的畫素面積

3. 角點檢測
原理我也沒啥好說的,畢竟這也不是計算機視覺原理專欄,呼叫api就完事了。

    for cnt in range(len(contours)):
        p = cv.arcLength(contours[cnt],True)
        area = cv.contourArea(contours[cnt])

        if area > 2500:
            mm = cv.moments(contours[cnt]) #計算影象輪廓中的中心矩,原理見程式碼後連結
            if mm['m00'] != 0: #如果算出了中心距(感覺這裡應該用try,except,當時寫的不嚴謹)
                cx = int(mm['m10'] / mm['m00']) #歸一化計算得出輪廓中心的橫縱座標
                cy = int(mm['m01'] / mm['m00'])
            else:
                continue
            epsilon = 0.04 * cv.arcLength(contours[cnt], True) #多邊形擬合的距離引數,下一個函數要用到。原理見程式碼後連結
            approx = cv.approxPolyDP(contours[cnt], epsilon, True)  #輪廓近似。將圓潤曲線折線化,以此得到該影象的角點座標。
            corners = len(approx) #得到角點數量
            if corners == 3: #三個角點的就是三角形
                shapes['triangle'] = shapes['triangle'] + 1
                shapeLenth = p/3 #得到三角形邊長
            elif corners == 4: #四個角點就是矩形
                shapes['rectangle'] = shapes['rectangle'] + 1
                shapeLenth = p/4 #得到正方形邊長
            else: #圓有好多角點
                shapes['circles'] = shapes['circles'] + 1
                pi = 3.1415926
                rad = p/(2*pi) #得到圓周長
                

解釋中心距(侵刪)概率論的知識了
解釋多邊形擬合(侵刪)
解釋角點檢測原理(侵刪)這兩篇文章都講的很清楚,一看就明白了,知道原理 然後拿來用就行。

  
  效果圖不小心被刪了,也懶得再去找樹莓派重跑了。有需要的可以自己拿去跑了試試,根據角點檢測的原理,五邊形,六邊形,七邊形等多邊形都可以識別出來,但是可能會和圓搞混,調一調epsilon 這個引數就行。
  
        有問題可以評論區交流