Jetson 15 OpenCV 运动检测

来自Waveshare Wiki
跳转至: 导航搜索

OpenCV 运动检测

本章教程使用 OpenCV 来检测画面中的变化,你可以为变化多少设置一个阈值,更改阈值,可以更改运动检测的敏感度。

本章节需要前置章节的基础。

准备工作

由于产品开机默认会自动运行主程序,主程序会占用摄像头资源,这种情况下是不能使用本教程的,需要结束主程序或禁止主程序自动运行后再重新启动机器人。

这里需要注意的是,由于机器人主程序中使用了多线程且由 crontab 配置开机自动运行,所以常规的 sudo killall python 的方法通常是不起作用的,所以我们这里介绍禁用主程序自动运行的方法。

如果你已经禁用了机器人主程序的开机自动运行,则不需要执行下面的结束主程序章节。

结束主程序

1. 点击上方本页面选项卡旁边的 “+”号,会打开一个新的名为 Launcher 的选项卡。

2. 点击 Other 内的 Terminal,打开终端窗口。

3. 在终端窗口内输入 bash 后按回车。

4. 现在你可以使用 Bash Shell 来控制机器人了。

5. 输入命令: sudo killall -9 python


例程

以下代码块可以直接运行:

1. 选中下面的代码块

2. 按 Shift + Enter 运行代码块

3. 观看实时视频窗口

4. 按 STOP 关闭实时视频,释放摄像头资源

如果运行时不能看到摄像头实时画面

  • 需要点击上方的 Kernel - Shut down all kernels
  • 关闭本章节选项卡,再次打开
  • 点击 STOP 释放摄像头资源后重新运行代码块
  • 重启设备

注意事项

如果使用CSI摄像头则需要注释`frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)`这一句。

本章节的特性

你需要更改一些参数来调增 OpenCV 对画面中变化检测的阈值(灵敏度)`threshold`,这个阈值越低,OpenCV 对画面的变化越敏感。

运行

运行代码块是,你可以看到摄像头的实时画面,可以在画面前挥手,本例程会自动将出现变化的部分使用绿色的方框圈起来。

import cv2
import numpy as np
from IPython.display import display, Image
import ipywidgets as widgets
import threading

threshold = 2000  # 动态检测面积阈值


# ============================
# 创建停止按钮
# ============================
stopButton = widgets.ToggleButton(
    value=False,
    description='Stop',
    disabled=False,
    button_style='danger',
    tooltip='Description',
    icon='square'
)


# ============================
# 视频流 + 动态检测
# ============================
def view(button):

    # 打开 USB 摄像头
    camera = cv2.VideoCapture(-1)
    camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    display_handle = display(None, display_id=True)

    avg = None  # 用于背景平均

    while True:
        ret, frame = camera.read()
        if not ret:
            continue

        # BGR → 灰度 → 高斯模糊
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        gray = cv2.GaussianBlur(gray, (21, 21), 0)

        # 初始化背景
        if avg is None:
            avg = gray.copy().astype("float")
            continue

        # 更新背景
        cv2.accumulateWeighted(gray, avg, 0.5)

        # 当前帧 vs 背景
        frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))

        # 阈值化 + 膨胀
        thresh = cv2.threshold(frameDelta, 5, 255, cv2.THRESH_BINARY)[1]
        thresh = cv2.dilate(thresh, None, iterations=2)

        # 不用 imutils,直接用 OpenCV
        contours, _ = cv2.findContours(
            thresh.copy(),
            cv2.RETR_EXTERNAL,
            cv2.CHAIN_APPROX_SIMPLE
        )

        # 画矩形框
        for c in contours:
            if cv2.contourArea(c) < threshold:
                continue

            (mov_x, mov_y, mov_w, mov_h) = cv2.boundingRect(c)
            cv2.rectangle(frame,
                          (mov_x, mov_y),
                          (mov_x + mov_w, mov_y + mov_h),
                          (128, 255, 0), 1)

        # 显示画面
        _, jpg = cv2.imencode('.jpeg', frame)
        display_handle.update(Image(data=jpg.tobytes()))

        # 点击停止按钮
        if stopButton.value:
            camera.release()
            display_handle.update(None)
            return


# ============================
# 显示按钮 + 启动线程
# ============================
display(stopButton)
thread = threading.Thread(target=view, args=(stopButton,))
thread.start()