ロボットとコンピュータビジョンの戦闘シリーズ (4): フレーム間差分に基づく動体検出とカメラ運動状態推定

ロボットとコンピューター ビジョンの戦闘シリーズの第 4 回目となる今回は、前後の画像フレームの違いに基づいて移動物体を検出し、カメラの動きの状態を推定する方法を紹介します。これは、自動運転、ドローン、ビデオ監視などのシナリオで使用でき、画像から動的情報を抽出し、カメラが静止しているか、パンしているか、回転しているかを判断するためのシンプルで効果的な方法です。
動体検知例

基本的な考え方と核となるステップ

動体検出とカメラの動き状態推定の基本的な考え方は、2 つの画像フレーム間のオプティカル フローを使用して各ピクセルの動きベクトルを計算し、動きベクトルのサイズと方向に応じて静止領域を分割することです。ダイナミック レンジ、およびカメラ モーション モード。具体的には、次のような手順があります。

  1. オプティカルフローを計算します。オプティカル フローは、画像内の各ピクセルの時間変位を指します。これは、Lucas-Kanade 法や Horn-Schunck 法などのいくつかの古典的なアルゴリズムで解決できます。ここでは、OpenCV が提供する関数 cv2.calcOpticalFlowFarneback() を使用して、密なオプティカル フローを計算し、各ピクセルの水平方向と垂直方向の動き量を取得します。
  2. 動きベクトルを計算します。動きベクトルは各ピクセルの動きの水平成分と垂直成分を指し、式 v = (u^2 + v 2) 0.5で計算できます。ここで、u とv はそれぞれ水平と垂直の動きです。動きベクトルは各ピクセルの動きの速度を反映しており、大きいほど速くなります。
  3. 静的領域と動的領域を分割します。静的領域とは、背景や地面などの画像のあまり変化しない部分を指し、動的領域とは、歩行者や車両などの画像の大きく変化する部分を指します。閾値 T を使用して、各ピクセルが静的領域に属するか動的領域に属するかを判断できます。つまり、v < T の場合は静的領域であり、そうでない場合は動的領域です。しきい値 T は実際の状況に応じて調整でき、通常は 5 や 10 などの小さい値になります。
  4. カメラの動きの状態を推定します。カメラの動きの状態とは、カメラが空間内でどのように動くかを指し、静的、平行移動、または回転の 3 つのタイプに分類できます。カメラの動作状態を判断するには、次の方法を使用できます。
  • すべてのピクセルが静的領域に属している場合、カメラは静的状態にあります。
  • すべてのピクセルが動的領域に属している場合、カメラは平行移動状態にあります。
  • 一部のピクセルが静的領域に属し、一部のピクセルが動的領域に属する場合、カメラは回転状態にあります。

長所と短所

この手法の利点は、実装がシンプルで簡単で、複雑なモデルやトレーニング データを必要としないことですが、欠点は、オプティカル フローの計算に高い精度が必要で、ノイズ、オクルージョン、ノイズなどの影響を受けやすいことです。イルミネーションが変わります。

実際のコード

動く物体の位置や輪郭を検出

# python opencv实现基于前后图像帧之间的差异进行运动物体检测
# 首先,导入所需的模块,包括opencv和numpy
import cv2
import numpy as np

# 然后,创建一个视频捕获对象,参数为0表示使用默认的摄像头,也可以用视频文件的路径替换
camera = cv2.VideoCapture(0)

# 检查摄像头是否成功打开,如果失败则退出程序
if not camera.isOpened():
    print("摄像头未打开")
    exit()

# 获取视频的宽度和高度,并打印出来
width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
print("视频宽度:", width)
print("视频高度:", height)

# 创建一个结构元素,用于形态学操作,这里使用椭圆形状,大小为9*4
es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 4))

# 定义一个变量,用于存储背景图像,初始为None
background = None

# 循环读取视频帧
while True:
    # 从摄像头获取一帧图像,返回值有两个,第一个是布尔值,表示是否成功读取,第二个是图像矩阵
    ret, frame = camera.read()

    # 如果读取失败,则退出循环
    if not ret:
        break

    # 对图像进行预处理,包括转换为灰度图,高斯滤波去噪,以及缩放到合适的大小
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (21, 21), 0)
    gray = cv2.resize(gray, (width // 4, height // 4))

    # 如果背景图像为空,则将当前帧作为背景图像
    if background is None:
        background = gray
        continue

    # 计算当前帧与背景图像之间的差异,并进行阈值化处理,得到一幅二值图像
    diff = cv2.absdiff(background, gray)
    diff = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)[1]

    # 对二值图像进行膨胀操作,填补空洞和缺陷,并找出其中的轮廓
    diff = cv2.dilate(diff, es, iterations=2)
    contours, hierarchy = cv2.findContours(diff.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 遍历轮廓列表,根据轮廓的面积大小进行筛选,过滤掉太小的区域
    for c in contours:
        if cv2.contourArea(c) < 1500:
            continue

        # 计算轮廓的外接矩形,并在原始帧上绘制绿色的边框
        x, y, w, h = cv2.boundingRect(c)
        x *= 4 # 还原缩放后的坐标
        y *= 4
        w *= 4
        h *= 4
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

    # 显示原始帧和差分图像
    cv2.imshow("frame", frame)
    cv2.imshow("diff", diff)

前後の画像フレームの差分に基づくカメラの動き状態の推定を実現するPython opencvのコード例

カメラの動きの状態の推定はコンピュータ ビジョンにおける重要な問題であり、ビデオ安定化、3D 再構成、拡張現実などのアプリケーションで使用できます。一般的に使用される方法は、オプティカル フローとも呼ばれる、画像フレームの前後の差に基づいてカメラの平行移動と回転を計算することです。この記事では、Python と opencv を使用してこのメ​​ソッドを実装する方法とコード例を紹介します。

まず、ビデオから 2 つのフレームを読み取り、グレースケールに変換する必要があります。次に、opencv が提供する関数 cv2.calcOpticalFlowFarneback() を使用して、画像の 2 つのフレーム間の密なオプティカル フローを計算します。この関数は画像と同じサイズの配列を返し、各要素は画像の 2 つのフレーム間の対応するピクセルの変位ベクトルを表します。cv2.cartToPolar() 関数を使用してこれらのベクトルを極座標に変換し、各ピクセルの変位の大きさと方向を取得できます。

次に、オプティカル フローの大きさと方向に基づいてカメラの並進と回転を推定する必要があります。簡単な方法は、すべてのピクセルの変位の大きさと方向を平均して、全体的な変位ベクトルと回転角度を取得することです。ただし、この方法はノイズや外れ値の影響を受ける可能性があり、推定値が不正確になる可能性があります。したがって、より堅牢なアプローチである RANSAC (Random Sampling Consensus) アルゴリズムを採用できます。RANSAC アルゴリズムの基本的な考え方は、すべてのデータ ポイントのごく一部を内部点としてランダムに選択し、これらの内部点に基づいてモデルを近似し、モデルとすべてのデータ ポイント間の誤差を計算することです。誤差がしきい値より小さい場合、モデルは妥当であるとみなされ、誤差がしきい値より小さいデータ ポイントがインライア セットに追加されます。このプロセスを何度も繰り返し、最終的に誤差が最も小さいモデルが最終結果として選択されます。

この例では、RANSAC アルゴリズムを使用して、2 次元平面上のカメラの平行移動と回転を表すアフィン変換行列を当てはめます。2 つのピクセルとそれに対応するオプティカル フロー ベクトルをランダムにサンプリングし、これら 2 つの点のペアに基づいてアフィン変換行列を計算し、この行列を使用してすべてのピクセルの位置を変換します。次に、変換された位置と元の位置の間のユークリッド距離を計算し、その距離がしきい値 (たとえば、5 ピクセル) 未満の点をインライアとして使用します。このプロセスを 100 回繰り返し、内部点の数が最も多いアフィン変換行列を最終結果として選択します。最後に、アフィン変換行列から平行移動ベクトルと回転角度を抽出し、画面に出力します。

完全なコード例は次のとおりです。

python
import cv2
import numpy as np

# 读取视频文件
cap = cv2.VideoCapture("video.mp4")

# 读取第一帧图像
ret, frame1 = cap.read()
# 转换为灰度图
gray1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)

# 循环读取后续帧图像
while True:
# 读取下一帧图像
ret, frame2 = cap.read()
if not ret:
break
# 转换为灰度图
gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)

# 循环读取后续帧图像
while True:
# 读取下一帧图像
ret, frame2 = cap.read()
if not ret:
break
# 转换为灰度图
gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)

# 检测角点特征,并提取描述符
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(gray1, None)
kp2, des2 = orb.detectAndCompute(gray2, None)

# 匹配前后帧的特征点,并筛选出良好的匹配对
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
good_matches = matches[:20]

# 使用随机抽样一致(RANSAC)算法,估计前后帧之间的单应矩阵(homography matrix)
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC)

# 从单应矩阵中分解出相机的旋转矩阵和平移向量
K = np.array([[800.0, 0.0, 320.0], [0.0, 800.0, 240.0], [0.0, 0.0, 1.0]]) # 相机内参矩阵,根据实际情况修改
R, t = cv2.recoverPose(H, src_pts[mask.ravel() == 1], dst_pts[mask.ravel() == 1], K)

# 根据旋转矩阵和平移向量,判断相机是向前、向后、向左、向右

opencv Python

回転行列と並進ベクトルに従って、カメラが前方、後方、左方、右方のいずれを向いているかを決定します

この記事では、Python ライブラリ opencv を使用して、回転行列と平行移動ベクトルに基づいてカメラが前方、後方、左方、右方に移動しているかを判断する方法を紹介します。これは、コンピュータ ビジョンやロボット工学におけるカメラの姿勢推定に使用できる便利な手法です。

まず、回転行列と平行移動ベクトルとは何かを理解する必要があります。回転行列は、ある座標系の別の座標系に対する回転を表す 3x3 行列です。平行移動ベクトルは、ある座標系の別の座標系に対する平行移動を表す 3x1 ベクトルです。たとえば、2 つのカメラ座標系 C1 と C2 がある場合、回転行列 R と並進ベクトル t は、C1 に対する C2 の変換を表すことができます。

C2 = R * C1 + t

ここで、* は行列の乗算を示します。これは、C1 内の点 P の座標がわかっている場合、次の式で C2 内のその座標を計算できることを意味します。

P' = R * P + t

ここで、P' は C2 の P の座標です。逆に、C2 の P' の座標がわかっている場合は、次の式で C1 の座標を計算できます。

P = R^(-1) * (P' - t)

ここで、R^(-1) は R の逆行列です。なお、ここでは、2つのカメラ座標系の原点はカメラの光学中心にあり、2つのカメラ座標系の軸は右手回りであると仮定している。

では、Rとtに基づいてカメラが前後左右に動いているかどうかをどのように判断するのでしょうか? 簡単なアプローチは、C1 でのカメラの位置が O(0, 0, 0) であると仮定し、C2 でのカメラの位置は O'(t_x, t_y, t_z) であると仮定します。ここで、t_x、t_y、t_z は 3 つのコンポーネントの t です。 。次に、C2 の O' の位置に応じてカメラの移動方向を判断できます。

  • t_z > 0 の場合、カメラは前方に移動しています。
  • t_z < 0 の場合、カメラは後方に移動しています。
  • t_x > 0 の場合、カメラは右に移動しています。
  • t_x < 0 の場合、カメラは左に移動しています。
  • t_y > 0 の場合、カメラは上に移動しています。
  • t_y < 0 の場合、カメラは下に移動しています。

もちろん、この方法では大まかな方向しか得られず、カメラの軌跡を正確に記述することはできません。カメラの運動軌跡をより正確に記述したい場合は、並進方向に対する回転行列 R の影響を考慮する必要があります。たとえば、R が y 軸を中心に反時計回りに 90 度回転する変換を表す場合、元の x 軸は z 軸になり、元の z 軸は -x 軸になります。このとき、t_xとt_zからカメラが前後左右に動いているかを直接判断することはできません。

次のチュートリアルでは、ロボット工学とコンピューター ビジョンを一緒に練習できます。私をフォローするか、私のコラムを購読してください。

おすすめ

転載: blog.csdn.net/weixin_42499608/article/details/129978346