机器人与计算机视觉实战系列(4):基于帧间差异的运动物体检测以及相机运动状态估计

这是机器人与计算机视觉实战系列的第4篇,将介绍如何基于前后图像帧之间的差异进行运动物体检测和相机运动状态的估计。这是一种简单而有效的方法,可以用于自动驾驶、无人机、视频监控等场景中,提取出图像中的动态信息,并判断相机是否处于静止、平移或旋转状态。
运动物体检测例子

基本思想以及核心步骤

运动物体检测和相机运动状态估计的基本思想是,利用前后两帧图像之间的光流(optical flow)来计算每个像素点的运动向量,然后根据运动向量的大小和方向来划分出静态区域和动态区域,以及相机的运动模式。具体来说,有以下几个步骤:

  1. 计算光流。光流是指图像中每个像素点在时间上的位移,可以用一些经典的算法来求解,如Lucas-Kanade法、Horn-Schunck法等。这里我们使用OpenCV提供的函数cv2.calcOpticalFlowFarneback()来计算稠密光流,得到每个像素点的水平和垂直方向上的运动量。
  2. 计算运动向量。运动向量是指每个像素点的运动量在水平和垂直方向上的分量,可以用公式v = (u^2 + v2)0.5来计算,其中u和v分别是水平和垂直方向上的运动量。运动向量反映了每个像素点的运动速度,越大表示越快。
  3. 划分静态区域和动态区域。静态区域是指图像中没有发生明显变化的部分,如背景、地面等;动态区域是指图像中有明显变化的部分,如行人、车辆等。我们可以用一个阈值T来判断每个像素点是否属于静态区域或动态区域,即如果v < T,则为静态区域,否则为动态区域。阈值T可以根据实际情况调整,一般取一个较小的值,如5或10。
  4. 估计相机运动状态。相机运动状态是指相机在空间中的移动方式,可以分为三种:静止、平移或旋转。我们可以用以下方法来判断相机的运动状态:
  • 如果所有像素点都属于静态区域,则相机处于静止状态;
  • 如果所有像素点都属于动态区域,则相机处于平移状态;
  • 如果部分像素点属于静态区域,部分像素点属于动态区域,则相机处于旋转状态。

优缺点

这种方法的优点是简单易实现,不需要复杂的模型或训练数据;缺点是对光流计算的精度要求较高,容易受到噪声、遮挡、光照变化等因素的影响。

实战代码

检测运动物体的位置和轮廓

# 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的代码实例

在计算机视觉中,相机运动状态的估计是一个重要的问题,它可以用于视频稳定、三维重建、增强现实等应用。一种常用的方法是基于前后图像帧之间的差异,也称为光流,来计算相机的平移和旋转。本文将介绍如何使用python和opencv来实现这种方法,并给出一个代码实例。

首先,我们需要从视频中读取两帧图像,并将它们转换为灰度图。然后,我们使用opencv提供的函数cv2.calcOpticalFlowFarneback()来计算两帧图像之间的稠密光流。这个函数返回一个和图像大小相同的数组,每个元素表示对应像素点在两帧图像之间的位移向量。我们可以用cv2.cartToPolar()函数将这些向量转换为极坐标形式,得到每个像素点的位移幅度和方向。

接下来,我们需要根据光流的幅度和方向来估计相机的平移和旋转。一种简单的方法是对所有像素点的位移幅度和方向求平均,得到一个全局的位移向量和旋转角度。然而,这种方法可能会受到噪声和异常点的影响,导致估计不准确。因此,我们可以采用一种更稳健的方法,即RANSAC(随机抽样一致)算法。RANSAC算法的基本思想是从所有数据点中随机抽取一小部分作为内点,然后根据这些内点拟合一个模型,并计算该模型与所有数据点的误差。如果误差小于一个阈值,则认为该模型是合理的,并将误差小于阈值的数据点加入内点集合。重复这个过程多次,最终选择误差最小的模型作为最终结果。

在本例中,我们使用RANSAC算法来拟合一个仿射变换矩阵,该矩阵可以表示相机在二维平面上的平移和旋转。我们随机抽取两个像素点及其对应的光流向量,然后根据这两对点计算仿射变换矩阵,并用该矩阵变换所有像素点的位置。然后我们计算变换后的位置与原始位置之间的欧式距离,并将距离小于一个阈值(例如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

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

在本文中,我们将介绍如何使用opencv python库来根据旋转矩阵和平移向量,判断相机是向前、向后、向左、向右移动的。这是一个有用的技术,可以用于计算机视觉和机器人学中的相机位姿估计。

首先,我们需要了解什么是旋转矩阵和平移向量。旋转矩阵是一个3x3的矩阵,它表示了一个坐标系相对于另一个坐标系的旋转。平移向量是一个3x1的向量,它表示了一个坐标系相对于另一个坐标系的平移。例如,如果我们有两个相机坐标系C1和C2,那么旋转矩阵R和平移向量t可以表示C2相对于C1的变换:

C2 = R * C1 + t

其中*表示矩阵乘法。这意味着,如果我们知道了C1中的一个点P的坐标,那么我们可以通过以下公式计算出它在C2中的坐标:

P’ = R * P + t

其中P’是P在C2中的坐标。反之,如果我们知道了P’在C2中的坐标,那么我们可以通过以下公式计算出它在C1中的坐标:

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

其中R^(-1)是R的逆矩阵。注意,这里假设了两个相机坐标系的原点都在相机光心处,且两个相机坐标系的轴都是右手系。

那么,如何根据R和t来判断相机是向前、向后、向左、向右移动的呢?一种简单的方法是,假设相机在C1中的位置为O(0, 0, 0),那么它在C2中的位置为O’(t_x, t_y, t_z),其中t_x, t_y, t_z是t的三个分量。然后,我们可以根据O’在C2中的位置来判断相机的移动方向:

  • 如果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