2016年11月25日

python+OpenCV做車輛追蹤

在我過去學習的軟體中,要做物體追蹤可以用Imagej或是Tracker,例如
以ImageJ分析影片中物體的座標及路徑
用tracker分析植物生長動態
用tracker追蹤綠豆向光性
以ImageJ分析影片中物體的座標及路徑

我覺得imagej在處理影片是比較麻煩一點,因為還得先把影片轉換呈圖片,再匯入成TIFF檔之後,才能進行處理。而Tracker因為本來就是針對影片分析而發展的軟體,所以使用上自然是方便許多,不過要分析物體移動上,還是得用半手動地指定位置去做追蹤。

這樣有點難滿足我某部份的分析需求,比方說我如果想要分析即時的影像,那就不能用Tracker來做了。於是我想那就直接寫程式來分析吧,經過幾天摸索學習後,我使用python加上 OpenCV來實做。

使用的環境是Ubuntu 16.04+python 2.7+ OpenCV3.0,這安裝的門檻算有點高,就是要一直輸入文字去安裝,還好有教學文件,照著貼上去執行就可以。(不過編譯的時候,有一點小問題,後來把出現的訊息拿去拜google,跟著改一個小地方也就解決了)
安裝的教學文件連結
Ubuntu 16.04: How to install OpenCV

那個編譯錯誤的訊息是顯示 fatal error: hdf5.h: No such file or directory
解決方式就是如這篇,把下面這兩行,加到 modules/python/common.cmake底下就可以
ind_package(HDF5)
include_directories(${HDF5_INCLUDE_DIRS})

OpenCV是非常完整豐富的函式庫,只要知道要用什麼函式,一行一行堆進去,就可以作到需要的效果,以下就是分析的影片。

最上面是原始檔,第二個是把移動物體的輪廓抓出來,第三個是用這個輪廓把車輛的影像抓出,第四個則是在原圖上加上那個輪廓框線。學習的參考文件,完全就是從這個網站來的
OpenCV-Python Tutorials





完整程式碼在最底下,如果我要做即時分析,也只要修改這行
cap = cv2.VideoCapture('output.mp4')
改成
cap = cv2.VideoCapture(0)
就可以拿來分析webcam的影像了

不過目前卡關了,因為還在摸索怎麼去把不同frame裡的車運算得知那是「同一部車」。


想要學習這樣的分析,原因之一是想到生物分析也會用到這樣的技術,過去看到一篇研究,有人拍攝螞蟻的動態,然後分析出有一半的螞蟻其實都是站著不動的,並非窩裡每一隻都會跑來跑去工作的。像這樣的研究,人工快轉去分析也是可以啦,但是如果程式可以代勞,那就可以得知很多不同的訊息了。


===================
#!/usr/bin/env python
import numpy as np
import cv2


cap = cv2.VideoCapture('output.mp4')
fgbg = cv2.createBackgroundSubtractorMOG2()
kernel = np.ones((11,11),np.uint8)

while(cap.isOpened()):
    ret, frame = cap.read()
    road=frame[475:644,250:1200]


    fgmask = fgbg.apply(road)     #backgroun
    fgmask = cv2.GaussianBlur(fgmask,(5,5),0) #GaussianBlue
    ret,fgmask = cv2.threshold(fgmask,150,255,cv2.THRESH_BINARY)   #Threshold
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)  #Morphological transformations-open
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel) #Morphological transformations-close


    car = cv2.bitwise_and(road,road,mask = fgmask)  #get car real images

    fgmask_inv = cv2.bitwise_not(fgmask)  #inverse car mask
    white=road.copy()
    white[:,:]=255  #make white road background
    road_withoutCar = cv2.bitwise_and(white,white,mask = fgmask_inv)  #road -car_mask
    whiteroad_car = cv2.add(road_withoutCar,car)  #road + car

    image, contours, hierarchy = cv2.findContours(fgmask.copy(),cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) #contour

    #print  len(contours)
    car_contour1=road.copy()
    car_contour1 = cv2.drawContours(car_contour1, contours, -1, (0,255,0), 3)  #draw car contour

    car_contour2=road.copy()
    car_contour3=road.copy()
    car_contour4=road.copy()
    for i in range(len(contours)):
        cnt = contours[i]
        M = cv2.moments(cnt)
        area = cv2.contourArea(cnt)
     
        epsilon = 0.1*cv2.arcLength(cnt,True)
        approx = cv2.approxPolyDP(cnt,epsilon,True)
        car_contour2 = cv2.drawContours(car_contour2, [approx], -1, (0,255,0), 3)  #draw car contour    

        hull = cv2.convexHull(cnt)
        car_contour3 = cv2.drawContours(car_contour3, [hull], -1, (0,255,0), 3)  #draw car contour    

        x,y,w,h = cv2.boundingRect(cnt)
        car_contour4 = cv2.rectangle(car_contour4,(x,y),(x+w,y+h),(0,255,0),2)



    #cv2.imshow('road',road)
    cv2.imshow('fgmask',fgmask)
    cv2.imshow('car',car)
    #cv2.imshow('road_white',whiteroad_car)
    #cv2.imshow('car_contour1',car_contour1)
    #cv2.imshow('car_contour2',car_contour2)
    #cv2.imshow('car_contour3',car_contour3)
    cv2.imshow('car_contour4',car_contour4)
    #cv2.imshow('carBoundary',carBoundary)


    k = cv2.waitKey(20) & 0xff
    if k == 27:
        break
cap.release()
cv2.destroyAllWindows()