2015/08/10


因為想在四軸飛行器上整合影像辨識做些好玩的事, 所以萊恩大兵動念想自學 OpenCV. 胡亂摸索一陣之後, 覺得 OpenCV + Python 的學習資源較豐富, 就往這方向投入了.

Github: PyCV-time 的範例, 有教怎麼辨識多邊形 (polygon), 其至是箭形 (arrow). 邊看程式, 邊查 API 說明, 倒也學到不少知識. 大致整理一下萊恩大兵的理解.


要辨識多邊形, 首先, 要能找出圖像的 Contour (輪廓).

[1] 什麼是 Contour (輪廓)?

擷取 OpenCV Python Tutorials 裡面的定義: Contours can be explained simply as a curve joining all the continuous points (along the boundary), having same color or intensity.

從上述的定義可理解, 一般在對圖像取 contour 前, 都會先轉黑白, 做 threshold, canny 等 edge detection 處理, 能提高 contour 的辨識效果.

[2] Contour 的資料結構長什麼呢?

OpenCV 有個叫做 FindContour 的指令, 能找出圖像的 contour(s). 

寫隻小程式來實驗:


原圖:



程式:


import numpy as np
import cv2
 
im = cv2.imread('test3.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print "there are " + str(len(contours)) + " contours"

cnt = contours[0]
print "there are " + str(len(cnt)) + " points in contours[0]"
print cnt

cnt = contours[1]
print "there are " + str(len(cnt)) + " points in contours[1]"
print cnt

cv2.imshow('im', im)
cv2.waitKey(0)
cv2.destroyAllWindows()

執行結果:




從程式的執行結果可以看出: contour 的資料結構, 是有 n 組點座標串列 (list) 組成的大串列. (n 代表掃瞄到 contour(s) 的個數, 點座標串列則是某個 contour 的點數.)

[3] 對 contour 做多邊形逼近 (approxPolyDP)

對 contour 做多邊形逼近的目的, 可以想成是用粗一點的線來描邊, 來忽略掉細微的毛邊或雜點. 再用隻程式來實驗:

程式:


import numpy as np
import cv2
 
im = cv2.imread('test3.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print "there are " + str(len(contours)) + " contours"

cnt = contours[0]
print "there are " + str(len(cnt)) + " points in contours[0]"
approx = cv2.approxPolyDP(cnt,30,True)
print "after approx, there are " + str(len(approx)) + " points"
print approx
cv2.drawContours(im,[approx],0,(255,0,0),-1)


cnt = contours[1]
print "there are " + str(len(cnt)) + " points in contours[1]"
approx = cv2.approxPolyDP(cnt,30,True)
print "after approx, there are " + str(len(approx)) + " points"
print approx
cv2.drawContours(im,[approx],0,(0,255,0),-1)

cv2.imshow('im', im)
cv2.waitKey(0)
cv2.destroyAllWindows()


執行結果:




從程式的執行結果可以看出: 第一個圖像為星形, 從 FindContour 找出來的 contour 卻有 351 個點, 在 approxPolyDP 處理過後, 才變成只有 10 個點. 若 approxPolyDP 的參數取的好 (用來逼近的線不會過粗, 也不會太細), 則根據輸出的座標點數, 就比較能判斷圖像的形狀. 藉由 FindContour 與 approxPolyDP 的處理, 就足夠寫隻辨識多邊形的小程式了.

[4] 什麼是 Convex Hull (凸多邊形框)?

看 OpenCV Python Tutorials 裡面的圖 (手) 即可明瞭, Convex Hull 概念上是取一個物件的凸多邊形框.



OpenCV 有個叫做 convexHull 的指令, 能找出圖像的 convex hull.

程式:


import numpy as np
import cv2
 
im = cv2.imread('test3.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print "there are " + str(len(contours)) + " contours"

cnt = contours[0]
print "there are " + str(len(cnt)) + " points in contours[0]"
hull = cv2.convexHull(cnt)
print "after convexHull, there are " + str(len(hull)) + " points"
cv2.drawContours(im,[hull],0,(255,0,0),-1)

cv2.imshow('im', im)
cv2.waitKey(0)
cv2.destroyAllWindows()

執行結果:




這個 Convex Hull 有什麼用呢? 往下看.


[5] 什麼是 Convexity Defects (凸多邊形缺䧟?)?

有了 Convex Hull 後, 它和原圖像的 deviation (偏差), 也就是那些凹䧟的部份, 就叫做 Convexity Defects (凸多邊形缺䧟?).

OpenCV 有個叫做 convexityDefect 的指令, 能找出圖像的 Convexity Defects.

看一下 Convexity Defects 的資料結構, 裡面有三個點座標與長度值.


struct CvConvexityDefect
{
   CvPoint* start; // point of the contour where the defect begins
   CvPoint* end; // point of the contour where the defect ends
   CvPoint* depth_point; // the farthest from the convex hull point within the defect
   float depth; // distance between the farthest point and the convex hull
};


程式:


import numpy as np
import cv2
 
im = cv2.imread('test3.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print "there are " + str(len(contours)) + " contours"

cnt = contours[0]
print "there are " + str(len(cnt)) + " points in contours[0]"
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
print "there are " + str(len(defects)) + " defects in contours[0]"

for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(im,start,end,[0,255,0],2)
    cv2.circle(im,far,5,[0,0,255],-1)

cv2.imshow('im', im)
cv2.waitKey(0)
cv2.destroyAllWindows()



執行結果:





[6] 辨識箭形

有了 Convex Hull 與 Convexity Defects 的概念後, 就能來看 Github: PyCV-time 的辨識箭形程式碼.

def isArrow(heptagon):
    hull = cv2.convexHull(heptagon, returnPoints = False)

    if len(hull) > 2:
        defects = cv2.convexityDefects(heptagon, hull)
        if defects is None or len(defects) != 2: 
            return False
      
        farpoints = [d[0][2] for d in defects]    
        if not np.abs(farpoints[0] - farpoints[1]) in [3, 4]:
            return False

        for defect in defects:
            s, e, f, d = defect[0]
            #    print defects
            #    s, e, f, d = defect[0]
            ps = heptagon[s, 0]
            pe = heptagon[e, 0]
            pd = heptagon[f, 0]
            if angle(ps, pd, pe) < 120:
                return True    

        return False


[7] 確保多邊形品質的程式技巧

看 Github: PyCV-time 的辨識五邊形/平行四邊形程式碼, 萊恩大兵學到幾招:

(i) 用多邊形的邊長或對角線長度的比值, 來篩選適當形狀的多邊形.


        edges = [LA.norm(a-b) for a, b in zip(simp_ctr, np.roll(simp_ctr, 2))]
        ratios = [(1 - e/edges[0])** 2 for e in edges]


(ii) 用 2-norm 來計算兩個點之間的距離

關於 Norm 的概念, 請參考:
OpenCV線性代數-cvNorm向量長度,距離計算
[線性系統] 拡談各種基本範數 (Norm)


(iii) Python 對串列的處理, 包括: zip(), roll(), shape(), arrange(), tuple() 等


[8] 參考資料
OpenCV Python Tutorials, Contours - 1 : Getting Started
OpenCV Python Tutorials, Contours - 2 : Brotherhood
OpenCV Python Tutorials, Contours - 3 : Extraction
OpenCV Python Tutorials, Contours - 4 : Ultimate

(2015/8/11, 更新) 這篇文章在臉書上的一些討論.
  • 黃無名 讚喔~久沒看到你分享四軸,以為你沒玩了呢!
  • 曾建評 最近是沒怎麼玩四軸沒錯. 先繞去做別的東西, 未來會再合一起
    昨天 23:33 ·  · 1
  • 施翰誠 曾大,可以加個好友一起增進嗎~
    23小時 · 收回讚 · 2
  • 王廣安 可以看看新的opencv3 新增了很多辨識的東西
    23小時 · 收回讚 · 9
  • Cruise Chen 多謝分享
    23小時 ·  · 1
  • Neil Lin 用cortex m4小顆的 把部分不要的linux移掉後把open cv移上去~可以更快
    23小時 ·  · 1
  • 龍青 有點不太了解OPENCV,是不是OPENCV一定要裝在個人電腦上,由WEBCAM作影像輸入,再由COM Port和Arduino通訊,作應用。這樣的架構,如果作成飛行器,會不會很麻煩?
  • Jungfu Chen Neil 哥說的沒錯,cortex m4雙核芯搭free rtos
    14小時 ·  · 1
  • 王廣安 openCV 也只是幫你包裝好的C++,你可以用openCV快速的達到你要的功能,再把裡面包好的C++ 放進去你的控制器就好了。
  • 龍青 openCV的底層,沒限用哪種硬體架構嗎? M4有浮點運算器,應該跑起來比較順。樹莓派,甚至伽利略,都可以用嗎?
  • 王廣安 我指的是先了解"影像處理"的原理,再去看openCV裡面的函示,自然在任何處理器都可以自己寫,至於openCV底層架構我就不太清楚了,我只知道有很多版本,可以支援android windows mac linux 等等
  • 龍青 你提的都是作業系統....看來OpenCV,一定要綁在作業系統內了...
  • 王廣安 應該是吧,所以才要了解"影像處理"的原理阿
    13小時 ·  · 2
  • 龍青 因為我一直用傳統的單晶片寫法在想這個事,因為一般來說,比較少用作業系統,甚至用組合語言來寫。我再去了解一下OpenCV的架構好了。
  • 王廣安 樹梅派其實有opencv!
    13小時 ·  · 1


[萊恩大兵的其它文章]

自製大四軸

自製大四軸, 實作分享@華山文創園區
自製大四軸, (1) 零組件篇, 遙控器 (Drone, Quadcopter, Futaba, Maker, Arduino, Animatronic Eye)
自製大四軸, (2) 零組件篇, 飛控板 (Drone, Quadcopter, MultiWii, Arduino, Futaba, Maker)
自製大四軸, (3) 零組件篇, 自行雷切木質機架 (Drone, Quadcopter, Maker, Laser Cut)
自製大四軸, (4) 零組件篇, 馬達與電變調整 (Drone, Quadcopter, Maker, Electric Speed Control, Motor)
自製大四軸, (5) 組裝篇, 四軸飛行器成形 (Drone, Quadcopter, MultiWii, Arduino, Maker, Electric Speed Control, Motor)
自製大四軸, (6) 調整篇, 飛行前兩三事 (Drone, Quadcopter, Maker, Futaba, Arduino, MultiWii)
自製大四軸, (7) 充電篇, iMax B6 充電器操作記要 (Charger, Battery)
自製大四軸, (8) 問題篇, 機架損壞維修 (Drone, Quadcopter, Laser Cut)
自製大四軸, (9) 改良篇, 方便拆卸的木質機架 (Drone, Quadcopter, Maker, Laser Cut)
自製大四軸, (10) 外飛篇, 新手的青蛙跳與遛狗 (Drone, Quadcopter, Maker, MultiWii)

自動報球速的棒球



CC2540 Bluetooth Low Energy
筆記, CC2540 Bluetooth Low Energy, (1) 開發環境 架設 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (2) 跑第一個範例程式 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (3) SimpleBLEPeripheral 簡單介紹 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (4) 在智慧手機上執行範例程式 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (5) 偵測與發送 iBeacon 訊號 (Bluetooth, CC2540, iBeacon)
實作, iBeacon 發訊器 x 防丟器 (Bluetooth, CC2540, iBeacon)
實作, iBeacon 尋寶遊戲 (Bluetooth, CC2540, iBeacon, iOS app)
實作, BLE + iOS app, 遙控燈泡君 (Bluetooth, CC2540, iOS app)
做實驗, 用 iBeacon 做自動控制的可行性 (Bluetooth, iBeacon, CC2540, Automation, URL Scheme, iOS app)

藍色小鋪一起來做

藍色小鋪一起來做, (1) 用 beacon 控制開關的枱燈
藍色小鋪一起來做, (2) 講解 BLE CC2540 UART 通訊範例程式 (Bluetooth, CC2540, UART)
藍色小鋪一起來做, (3) 藍牙枱燈專案實作 (上) (Bluetooth, CC2540)
藍色小鋪一起來做, (4) 藍牙枱燈專案實作 (下) (Bluetooth, CC2540)
藍色小鋪一起來做, (5) iBeacon scanner 專案示範與解說 (Bluetooth, CC2540, iBeacon)
藍色小鋪一起來做, (6) 完成, 用 iBeacon 控制開關的枱燈 (Bluetooth, CC2540, iBeacon)
藍色小鋪, 初嚐樹莓派 (Raspberry Pi)
藍色小鋪, iBeacon 應用, 自動記錄到訪時間 (iBeacon, Geohopper, Zapier)
藍色小鋪, 菲力普的 Docker 應用分享 (Docker)
藍色小鋪, PTT地震文團隊分享三連發, (1) Maker 的 IOT 遊樂場 (PTT, Hackathon, Python, Xively, Internet of Things)
藍色小鋪, PTT地震文團隊分享三連發, (2) mbed 新手分享 (mbed, MPU6050, Hackathon, Internet of Things)
藍色小鋪, 空中提升軌道車, 作品進化分享 (Pneumatic Tube System, Force of Friction, Mini 4 WD)
藍色小鋪, 數字管時鐘, 作品進化分享 (Nixie Tube, VFD)
藍色小鋪, 回憶之光, 3D 列印的經驗分享 (3D printing)

OpenCV 學習路徑

記錄, OpenCV 學習路徑, (1) 環境安裝與第一個範例 (OpenCV, Python)

小惡魔 無線溫度感測器

108 大眼仔
Plot Clock

體驗, 原住民互動故事書@宜蘭大同鄉泰雅生活館
體驗, 蛋生音互動裝置@兒童美術館 (Arduino, 3D Printing, HC-SR04, Interactive)



實作, 電容感應音樂樹

0 意見:

張貼留言