Python

Pythonでオプティカルフローのやり方・方法とマスク処理の追加【OpenCV】

Python

どうも、月見(@Suzuka14144156)です。

今回の記事では、Pythonでオプティカルフローを実装する方法・やり方を解説したいと思います。

よって、この記事では、このような悩みをお持ちの方におすすめです。

  • オプティカルフローとは
  • オプティカルフローはどのように使うの?
  • Pythonでどのように実装するの?
  • オプティカルフローで元動画の動きのある部分以外をマスキングするという方法で使うことがあるので、その方法も解説します

参考にした本

created by Rinker
¥3,630 (2025/01/22 08:00:37時点 楽天市場調べ-詳細)

オプティカルフローとは?

  • オプティカルフローとは、動画中の物体の動きをベクトルで表したもの

以下の画像は、円が画面左下から右上に動く様子を示したものです。

動線の軌跡をベクトルで表示しています。

これがオプティカルフローです。

Pythonでオプティカルフローを実装するには

OpenCVという画像処理のライブラリを使用します。

Pythonでオプティカルフローのための環境の構築

以下のコードをコマンドプロンプトで入力することで、OpenCVがインストール可能です。

pip install opencv-python

Pythonでオプティカルフローを試すための動画

以下のリンク先からダウンロード可能です。

opencv/samples/data/vtest.avi at master · opencv/opencv
Open Source Computer Vision Library. Contribute to opencv/opencv development by creating an account on GitHub.

ディレクトリ構成

pyコードとvtest.aviを同じフォルダに格納してください。

オプティカルフローのPythonのコード(1)

import numpy as np
import cv2

cap = cv2.VideoCapture('vtest.avi')

# params for ShiTomasi corner detection
feature_params = dict( maxCorners = 100,
                       qualityLevel = 0.3,
                       minDistance = 7,
                       blockSize = 7 )

# Parameters for lucas kanade optical flow
lk_params = dict( winSize  = (15,15),
                  maxLevel = 2,
                  criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

# Create some random colors
color = np.random.randint(0,255,(100,3))

# Take first frame and find corners in it
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)

# Create a mask image for drawing purposes
mask = np.zeros_like(old_frame)

while(1):
    ret,frame = cap.read()
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # calculate optical flow
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

    # Select good points
    good_new = p1[st==1]
    good_old = p0[st==1]

    # draw the tracks
    for i,(new,old) in enumerate(zip(good_new,good_old)):
        a,b = new.ravel()
        c,d = old.ravel()
        a,b,c,d =int(a),int(b),int(c),int(d)
        mask = cv2.line(mask, (a,b),(c,d), color[i].tolist(), 2)
        frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1)
    img = cv2.add(frame,mask)

    cv2.imshow('frame',img)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break

    # Now update the previous frame and previous points
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()
cap.release()

a,b,c,d =int(a),int(b),int(c),int(d)は、整数に変換しています。

Opencvのバージョンが、OpenCV(4.6.0)の場合は、整数に変換しないとエラーを返します。

チュートリアルでのコードは、この部分が抜けているのでエラーになります。

実行結果

人々の動線にラインが描かれます。

オプティカルフローのPythonのコード(2)

import cv2
import numpy as np
cap = cv2.VideoCapture("vtest.avi")

ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255

while(1):
    ret, frame2 = cap.read()
    next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)

    flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

    mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])
    hsv[...,0] = ang*180/np.pi/2
    hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)

    cv2.imshow('frame2',rgb)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break
    elif k == ord('s'):
        cv2.imwrite('opticalfb.png',frame2)
        cv2.imwrite('opticalhsv.png',rgb)
    prvs = next

cap.release()
cv2.destroyAllWindows()

実行結果

動画の動きのある部分のみを抽出することができます。

これを応用して、元画像の動きがある部分のみを抽出する方法を以下で解説します。

オプティカルフローのPythonのコード(3)

import cv2
import numpy as np

cap = cv2.VideoCapture('vtest.avi')
ret, frame1 = cap.read()

# 前のフレームとの差分をとるための変数を初期化
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv_mask = np.zeros_like(frame1)

while True:
    ret, frame2 = cap.read()
    if not ret:
        break
    
    # 現在のフレームをグレースケールに変換
    next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    
    # オプティカルフローを計算
    flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    
    # オプティカルフローの角度と大きさを計算
    mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
    
    # 色相の値を角度に、彩度の値を255に固定して、hsv画像を作成
    hsv_mask[..., 0] = ang * 180 / np.pi / 2
    hsv_mask[..., 1] = 255
    hsv_mask[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
    hsv_mask = cv2.cvtColor(hsv_mask,cv2.COLOR_HSV2BGR)
    
    # マスキング処理
    mask = cv2.inRange(hsv_mask, np.array([0, 0, 0]), np.array([30, 50, 50]))
    mask = 255 - mask
    masked_frame = cv2.bitwise_and(frame2, frame2, mask=mask)
    
    cv2.imshow('masked video', masked_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
    
    # 次のフレームの計算のために現在のフレームを保存
    prvs = next

cap.release()
cv2.destroyAllWindows()

実行結果

人が動いている部分だけを抽出する動画(画像)を生成できます。

Pythonで画像処理の勉強方法

私は、以下の本で勉強しました。

かなり詳しく書かれており、とてもオススメの本です。

created by Rinker
¥3,630 (2025/01/22 08:00:37時点 楽天市場調べ-詳細)

Udemy

Udemyはオンライン口座です。

一度購入すれば、ずっと閲覧できるのでオススメです。

【Pythonで学ぶ】OpenCVでの画像処理入門
OpenCVの導入・画像の基礎知識からエッジの検出・特徴抽出、特徴追跡など様々な画像処理を紹介。実践力強化のため、パーティクルフィルターも原理を理解した後、自力で実装します。

まとめ

今回の記事では、Pythonでオプティカルフローを実装する方法・やり方を解説しました。

今回の記事は、以上です。

コメント

タイトルとURLをコピーしました