空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

物体追跡をやってみる(ピンクのボールを追いかける)

やりたいこと:ピンクボールの追跡

f:id:robonchu:20170819115402j:plain

OpenCV-Python tutorial

OpenCV-Pythonチュートリアル — OpenCV-Python Tutorials 1 documentation

OpenCVでのHSVの扱い

OpenCVでのHSV色空間lower,upperの取り扱い - Qiita

ピンクの色相の検出

lightsalmon : 255,160,167

lower_pink = np.array([168, 100, 100])

upper_pink = np.array([188, 255, 255])

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while(1):
    _, frame = cap.read()
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    lower_light_pink = np.array([168, 100, 100])
    upper_light_pink = np.array([188, 255, 255])
    mask = cv2.inRange(hsv, lower_light_pink, upper_light_pink)

    res = cv2.bitwise_and(frame,frame, mask= mask)

    cv2.imshow('frame',frame)
    cv2.imshow('mask',mask)
    cv2.imshow('res',res)
    k = cv2.waitKey(5) & 0xFF
    if k == 27:
        break

cv2.destroyAllWindows()

参考:色名称付色見本[赤、ピンク、茶系] – コピペで出来る!簡単ホームページ作成入門

【Python/OpenCV】カラートラッキング(色追跡)で移動物体の検出

最大矩形を追跡

作成したmaskの中で、一番大きいものだけを追跡する(矩形で囲む)

ROI:Region Of Interest

以下を上記のスクリプトに追加↑

    image, contours, hierarchy  = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    rects = []
    for contour in contours:
        approx = cv2.convexHull(contour)
        rect = cv2.boundingRect(approx)
        rects.append(np.array(rect))

    if len(rects) > 0:
      rect = max(rects, key=(lambda x: x[2] * x[3]))
      cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), (0, 0, 255), thickness=2)

参考: pythonで赤い物体を認識しよう - Qiita

camshiftで追跡

ヒストグラムとは

ヒストグラムとは画像中の画素値の全体的な分布を知るためのグラフやプロットとみなせます.

ヒストグラム その1: 計算して,プロットして,解析する !!! — OpenCV-Python Tutorials 1 documentation

ヒストグラム逆投影法

各画素が対象物体に属している確率を表す.入力画像と同じサイズで単相の画像を作成します.

ヒストグラム その4: ヒストグラムの逆投影法 — OpenCV-Python Tutorials 1 documentation

パラメータ

  • cv2.TERM_CRITERIA_EPS - 指定された精度(epsilon)に到達したら繰り返し計算を終了する.

  • cv2.TERM_CRITERIA_MAX_ITER - 指定された繰り返し回数(max_iter)に到達したら繰り返し計算を終了する.

かなり適当なコード(TODO:リファクタ、マウスでクリックした箇所のHSV値の利用)

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

print raw_input("please set the tracking object and input key")

# Need adjustment
lower_light_pink = np.array([168, 100, 100])
upper_light_pink = np.array([188, 255, 255])

_LOWER_COLOR = lower_light_pink
_UPPER_COLOR = upper_light_pink

print _LOWER_COLOR[0]

def find_rect_of_target_color(image):
  hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

  # Threshold the HSV image to get only pink colors
  mask = cv2.inRange(hsv, _LOWER_COLOR, _UPPER_COLOR)
  image, contours, hierarchy  = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

  rects = []
  for contour in contours:
    approx = cv2.convexHull(contour)
    rect = cv2.boundingRect(approx)
    rects.append(np.array(rect))
  return rects

def get_max_rect(rects):
    if len(rects) > 0:
      rect = max(rects, key=(lambda x: x[2] * x[3]))
      return rect
    else:
      return None

def get_track_info(rect):
    # setup initial location of window
    r, h, c, w = rect[1], rect[3], rect[0], rect[2]
    track_window = (c,r,w,h)

    # set up the ROI for tracking
    roi = frame[r:r+h, c:c+w]
    hsv_roi =  cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv_roi, _LOWER_COLOR, _UPPER_COLOR)
    
    # Need adjustment
    roi_hist = cv2.calcHist([hsv_roi],[0],mask,[int(_UPPER_COLOR[0])],[0,int(_UPPER_COLOR[0])])
    cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)

    # Setup the termination criteria, either 10 iteration or move by atleast 1 pt
    term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
    return roi_hist, track_window, term_crit

# Set Initial Value
while(1):
  # take first frame of the video
  ret,frame = cap.read()
  rects = find_rect_of_target_color(frame)
  rect = get_max_rect(rects)
  if rect is not None:
    roi_hist, track_window, term_crit = get_track_info(rect)
    break

# Start Tracking
while(1):
    ret ,frame = cap.read()

    if ret == True:
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)        
        dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)

        # apply camshift to get the new location
        # ret = ([x,y],[width,height],deg) ex: [640, 480],[10,10],30
        ret, track_window = cv2.CamShift(dst, track_window, term_crit)

        # Draw it on image
        pts = cv2.boxPoints(ret)
        pts = np.int0(pts)
        img = cv2.polylines(frame,[pts],True, 255,2)
        cv2.imshow('img',img)

        print pts
        # origin is upper left
        frame_size = frame.shape
        print "position_x_rate"
        print (pts[0][0]+pts[2][0])/2.0/frame_size[1]
        print "position_y_rate"
        print (pts[0][1]+pts[2][1])/2.0/frame_size[0]

        k = cv2.waitKey(60) & 0xff
        if k == 27:
            break
        else:
            cv2.imwrite(chr(k)+".jpg",img)
    else:
        break

cv2.destroyAllWindows()
cap.release()

参考: Meanshift と Camshift — OpenCV-Python Tutorials 1 documentation

パーティクルフィルタで追跡

import numpy as np
import cv2

# Need adjustment
lower_light_pink = np.array([168, 50, 50])
upper_light_pink = np.array([188, 255, 255])

_LOWER_COLOR = lower_light_pink
_UPPER_COLOR = upper_light_pink

def tracking():
    cap = cv2.VideoCapture(0)

    filter = ParticleFilter()
    filter.initialize()

    while True:
        ret, frame = cap.read()

        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

        # Threshold the HSV image to get only a color
        mask = cv2.inRange(hsv, _LOWER_COLOR, _UPPER_COLOR)

        # Start Tracking
        y, x = filter.filtering(mask)
        frame = cv2.circle(frame, (int(x), int(y)), 10, (255, 0, 0), -1)

        # origin is upper left
        frame_size = frame.shape
        print "position_x_rate"
        print x/frame_size[1]
        print "position_y_rate"
        print y/frame_size[0]

        for i in range(filter.SAMPLEMAX):
            frame = cv2.circle(frame, (int(filter.X[i]), int(filter.Y[i])), 2, (0, 0, 255), -1)
        cv2.imshow("frame", frame)

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

class ParticleFilter:
    def __init__(self):
        self.SAMPLEMAX = 1000
        # frame.shape
        self.height, self.width = 480, 640

    def initialize(self):
        self.Y = np.random.random(self.SAMPLEMAX) * self.height
        self.X = np.random.random(self.SAMPLEMAX) * self.width

    # Need adjustment for tracking object velocity
    def modeling(self):
        self.Y += np.random.random(self.SAMPLEMAX) * 200 - 100 # 2:1
        self.X += np.random.random(self.SAMPLEMAX) * 200 - 100

    def normalize(self, weight):
        return weight / np.sum(weight)

    def resampling(self, weight):
        index = np.arange(self.SAMPLEMAX)
        sample = []

        # choice by weight 
        for i in range(self.SAMPLEMAX):
            idx = np.random.choice(index, p=weight)
            sample.append(idx)
        return sample

    def calcLikelihood(self, image):
        # white space tracking 
        mean, std = 250.0, 10.0
        intensity = []

        for i in range(self.SAMPLEMAX):
            y, x = self.Y[i], self.X[i]
            if y >= 0 and y < self.height and x >= 0 and x < self.width:
                intensity.append(image[int(y),int(x)])
            else:
                intensity.append(-1)

        # normal distribution
        weights = 1.0 / np.sqrt(2 * np.pi * std) * np.exp(-(np.array(intensity) - mean)**2 /(2 * std**2))
        weights[intensity == -1] = 0
        weights = self.normalize(weights)
        return weights

    def filtering(self, image):
        self.modeling()
        weights = self.calcLikelihood(image)
        index = self.resampling(weights)
        self.Y = self.Y[index]
        self.X = self.X[index]
        
        # return COG
        return np.sum(self.Y) / float(len(self.Y)), np.sum(self.X) / float(len(self.X))

if __name__ == '__main__':
    tracking()

参考:

パーティクルフィルタ組んでみた(Python) - コンピュータサイエンス系勉強ノート

Numpyによる乱数生成まとめ - Qiita

python + numpyで緑色の物体を追跡する (パーティクルフィルタ) - Qiita

Particle FilterでOpencvを使った物体追跡 - のんびりしているエンジニアの日記

Python版OpenCVのパーティクルフィルタで物体追跡

satomacoto: Pythonでパーティクルフィルタを実装してみる

particlefilter/particlefilter_py.py at master · AriYu/particlefilter · GitHub

pythonでパーティクルフィルタによる物体トラッキング - kohta blog

オプティカルフロー(Lucas-Kanade法)で追跡

cv2.goodFeaturesToTrack

コーナー検出器。8bitの画像とコーナーに関する設定を行うことでコーナーが検出される。

cv2.calcOpticalFlowPyrLK

8bitの画像を前後の二枚と検出したい点の集合(feature)やwinSizeを指定すると、後の画像上の特徴点が抽出される。

cv2.cornerSubPix

コーナー検出の高精度化。

Harrisコーナー検出 — OpenCV-Python Tutorials 1 documentation

cv2.dilate

膨張処理

Python OpenCV3で画素の膨張処理(dilation)と収縮処理(erosion) (ちょっと解説も) | from umentu import stupid

cv2.connectedComponentsWithStats

重心・面積・外接矩形の座標が得らる。

Harrisコーナー検出 — OpenCV-Python Tutorials 1 documentation

参考: qiita.com

OpenCVでオプティカルフローをリアルタイムに描画する(Shi-Tomasi法、Lucas-Kanade法) - Qiita

全体参考

毎日がロボット勉強会: 10.ロボットビジョン(応用編その1) 

モーション解析と物体追跡 — opencv 2.2 (r4295) documentation

人追跡

OpenCVで簡単リアルタイム物体検出 | AI coordinator

 Nobutobook: 【Python × OpenCV】 はじめての歩行者検知

機械の目が見たセカイ -コンピュータビジョンがつくるミライ (27) 動く人・物を追跡する(1) - OpenCVによるトラッキング | テクノロジー | マイナビニュース

顔追跡

【ライブラリ】pyFaceTracker:顔パーツ追跡「FaceTracker」のpythonラッパー... | DERiVE コンピュータビジョン ブログ

OpenCVで簡単リアルタイム物体検出 | AI coordinator

マーカー

Aruco-with-Python/aruco_testproject/build at master · JanezCim/Aruco-with-Python · GitHub

Opencvを用いたマーカー検出(仮 - Qiita

arucoモジュールでマーカーを検出する · atinfinity/lab Wiki · GitHub

軽量な AR ライブラリの ArUco について調査中 - Qiita

itouh2: OpenCV 画面上の四角の3次元座標をもとめる

GitHub - DebVortex/python-ar-markers: Detection of hamming markers for OpenCV written in python

ARタグの姿勢を取得

ROSでARマーカーを追跡するパッケージを使う: マイコン漬け

ROS勉強記録: ar_pose (ARToolKitのマーカー認識 in ROS)パッケージを使ってみる

ar-markers 0.4.1 : Python Package Index