【图像识别】摄像头捕捉运动到静止视频帧(免费源码分享)
1. 本文摘要
本文实现了一个OpenCV和PyQT5 结合的摄像头视频捕捉和运动检测线程,实现了一款界面软件用于功能演示。主要使用帧差法实现,摄像头捕捉运动到静止的图片,捕捉到的图片可用于其他,例如:用于目标检测、识别等,主要流程如下:
- 初始化摄像头。
- 实时读取帧并进行区域检测。
- 通过帧差法判断运动。
- 使用信号机制将结果图像发送到主界面。
本系统所涉及的源码已打包上传,可放心获取,免费,无套路!!获取方式,点击原文链接,根据提示获取即可。
原文链接:【图像识别】摄像头捕捉运动到静止视频帧(免费源码分享)
2. 主要流程介绍
对摄像头区域做出限定,主要是我的使用流域是目标检测领域,摄像头视域边缘容易误触发识别
3. 线程处理代码及逻辑
这个类实现了一个使用 OpenCV 和 PyQt5 结合的摄像头视频捕捉和运动检测线程,主要逻辑分为:
- 初始化摄像头。
- 实时读取帧并进行区域检测。
- 通过帧差法判断运动。
- 使用信号机制将结果图像发送到主界面。
3.1 导入库和模块
import cv2
from PyQt5.QtCore import QThread, pyqtSignal, QDateTime
from PyQt5.QtGui import QImage
import time
- cv2:用于处理摄像头视频流和图像操作的 OpenCV 库。
- QThread:PyQt5 中的线程类,用于在单独的线程中运行摄像头捕捉逻辑。
- pyqtSignal:PyQt5 中的信号机制,用于线程之间的通信。定义信号后,可以在程序的其他部分接收信号并处理相关逻辑。
- QDateTime:PyQt5 中的时间处理类,用于获取当前时间。
- QImage:PyQt5 中的图像类,用于将 OpenCV 图像转换为 QImage 后显示。
- time:标准库模块,用于控制循环内的延迟。
3.2 CameraThread 类初始化
class CameraThread(QThread):
sig_area_detect_result = pyqtSignal(QImage)
sig_update_frame = pyqtSignal(QImage)
def __init__(self):
super(CameraThread, self).__init__()
self.running = True
self.first_frame = None
self.contours_total_count = 0
self.tmp_moment_1 = 0
self.tmp_moment_2 = 0
self.tmp_moment_3 = 0
self.width_percent = 0.25
self.height_percent = 0.2
QThread:继承 QThread 类,实现线程逻辑。摄像头操作在一个独立线程中进行,防止阻塞主 GUI 线程。
信号定义:
- sig_area_detect_result:用于发射检测结果图像的信号。
- sig_update_frame:用于发射每一帧图像更新信号。
类属性: - self.running:控制线程运行的布尔值。
- self.first_frame:保存初始帧,用于后续的帧差运算。
- self.contours_total_count:记录运动物体轮廓的总数。
- self.tmp_moment_1, tmp_moment_2, tmp_moment_3:用于记录时间戳,控制不同时间段的处理。
- self.width_percent, self.height_percent:用于定义裁剪图像的比例,缩小检测区域。
3.3 run() 方法 - 线程主逻辑
def run(self):
cap = cv2.VideoCapture(0) # 打开摄像头
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1600)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1200)
cv2.VideoCapture(0):打开摄像头,并将其分配给 cap 对象。
set():设置摄像头的分辨率为 1600x1200。
while self.running:
ret, frame = cap.read()
if ret:
contours_count = self.area_detect(frame)
if contours_count > 0:
# 运动中,总数++
self.contours_total_count += 1
self.tmp_moment_1 = QDateTime.currentMSecsSinceEpoch()
if self.contours_total_count >= 3:
self.tmp_moment_2 = QDateTime.currentMSecsSinceEpoch()
else:
# 静止状态
current_time = QDateTime.currentMSecsSinceEpoch()
ntmp_moment = current_time - self.tmp_moment_2
if 210 < ntmp_moment < 1000:
if current_time - self.tmp_moment_3 > 2000:
h, w, c = frame.shape
b = c * w
r_img = QImage(frame.data, w, h, b, QImage.Format_BGR888)
self.sig_area_detect_result.emit(r_img)
self.tmp_moment_3 = current_time
if current_time - self.tmp_moment_1 > 300:
self.contours_total_count = 0
# 将图像传递到主界面显示
height, width, channel = frame.shape
bytes_per_line = channel * width
q_img = QImage(frame.data, width, height, bytes_per_line, QImage.Format_BGR888)
self.sig_update_frame.emit(q_img)
time.sleep(0.01)
cap.read():从摄像头读取当前帧图像。ret 为布尔值,表示读取是否成功,frame 是读取的图像数据。
area_detect():调用 area_detect() 方法进行运动检测,返回轮廓计数 contours_count。
运动检测逻辑:
- 如果有运动检测(contours_count > 0),更新 self.contours_total_count 和时间戳。
- 如果超过 3 个连续帧有运动,则记录时间戳 tmp_moment_2。
- 如果静止(contours_count == 0),计算静止时间差,并在静止时间合适时(210ms 到 1000ms)发送检测结果图像信号 sig_area_detect_result。
frame.shape:获取图像的高度、宽度和通道数。
QImage():将 frame 转换为 QImage,方便在 PyQt5 界面中显示。
sig_update_frame.emit():发射更新图像信号,供主界面使用。
time.sleep(0.01):线程暂停 10 毫秒,避免过高的 CPU 占用。
cap.release():释放摄像头资源。
3.4 area_detect() 方法 - 运动检测
def area_detect(self, frame):
# 左右各裁剪四分之一, 高裁剪五分之一,存入裁剪图像
height, width = frame.shape[:2]
cropped_frame = frame[int(height*self.height_percent):height,
int(width*self.width_percent):int(width*(1-self.width_percent))]
# 调整大小,灰度化和高斯模糊
resized_frame = cv2.resize(cropped_frame, (width // 3, height // 3))
gray_frame = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY)
blurred_frame = cv2.GaussianBlur(gray_frame, (21, 21), 0)
if self.first_frame is None:
self.first_frame = blurred_frame
return 0
# 计算当前帧与初始帧的差异
frame_delta = cv2.absdiff(self.first_frame, blurred_frame)
_, thresh = cv2.threshold(frame_delta, 50, 255, cv2.THRESH_BINARY)
thresh = cv2.dilate(thresh, None, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
self.first_frame = blurred_frame
return len(contours)
frame.shape:获取图像的高度和宽度。
裁剪图像:通过 self.height_percent 和 self.width_percent 计算裁剪区域,仅检测特定区域的运动。
灰度处理与模糊:裁剪图像后,缩小尺寸,转换为灰度图,并应用高斯模糊以减少噪声。
first_frame:如果是第一次运行,记录当前帧作为初始帧,用于后续检测。
帧差法:计算当前帧与初始帧的差异 frame_delta,并通过阈值分割获取二值化图像。
轮廓检测:使用 cv2.findContours() 查找轮廓,返回轮廓数量。
4. 主界面逻辑
4.1 打开摄像头启动线程绑定信号槽
def btnOpenCamera(self):
if self.bOpenCamera is True:
self.LogShow('摄像头已启动!')
return
self.LogShow('线程正在启动摄像头, 请等待画面......')
# 创建摄像头线程
self.camera_thread = CameraThread()
self.camera_thread.sig_update_frame.connect(self.update_image)
self.camera_thread.sig_area_detect_result.connect(self.handle_detect_result)
self.camera_thread.start()
self.bOpenCamera = True
4.2 获取视频帧刷新界面及刷新检测结果帧
def update_image(self, image):
pixmap = QPixmap.fromImage(image)
self.ui.ImageShow.setPixmap(pixmap)
def handle_detect_result(self, image):
pixmap = QPixmap.fromImage(image)
self.ui.labelImgResult.setPixmap(pixmap)
self.nCount += 1
self.LogShow(f'检测摄像头画面由运动到静止 {self.nCount}')
往期文章回顾
原文地址:【图像识别】摄像头捕捉运动到静止视频帧(免费源码分享)
结束语
文中源码文件【获取方式】:点击原文根据提示获取,免费,无套路,关注即可!!!