传统运动物体检测方法的Python实现

传统运动物体检测方法的Python实现


一、目标跟踪算法综述

视觉目标(单目标)跟踪任务就是在给定某视频序列初始帧的目标大小与位置的情况下,预测后续帧中该目标的大小与位置

1. 传统方法:特征提取+滤波类搜索算法

(1) 帧差法、光流法、背景减除法
(2) 相关滤波法

2. 深度学习方法: 目标检测和相似度匹配

(1) tracking-by-detection方式:
主要针对目标检测算法和滤波类算法(多目标跟踪),yolo系列、SSD系列、anchor-free系列、two-stage系列等等,滤波类和上述传统方式相似。
(2) 基于Siamese Networks(生成式,主要针对单目标):
主要通过Siamese网络进行相似度匹配,主要操作为:首先手动选择初始图像中的目标,使用Siamese网络进行特征提取,然后以此特征为标准,遍历后面帧图像的每个位置,对每个位置进行特征提取,然后做比较,确定位置。

二、Python实现

0.引入库

代码如下:

import cv2
import numpy as np

1. 帧差法

(1)二帧法

当前帧与前一帧差分,核心代码如下:

# 当前帧
tempFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 差分帧
disFrame = cv2.absdiff(tempFrame, last_frame)
# 中值滤波去噪
median = cv2.medianBlur(disFrame, 3)
# 二值化
ret, threshold_frame = cv2.threshold(median, 20, 255, cv2.THRESH_BINARY)
# 高斯模糊
gauss_image = cv2.GaussianBlur(threshold_frame, (3, 3), 0)
# 更新,便于下一次差分
last_frame = tempFrame

(2)三帧法

对于连续的三帧,12相减,23相减,然后将两次差分的结果做与运算作为最终结果,相比于二帧法可以消除微小抖动的影响。核心代码如下:

# 当前帧
temp_frame = frame_gray
# 12差分
dis_frame1 = cv2.absdiff(frame2, frame1)
_, thresh1 = cv2.threshold(dis_frame1, 40, 255, cv2.THRESH_BINARY)  # 二值,大于40的为255,小于0
# 23差分
dis_frame2 = cv2.absdiff(temp_frame, frame2)
_, thresh2 = cv2.threshold(dis_frame2, 40, 255, cv2.THRESH_BINARY)  # 二值,大于40的为255,小于0
# 与运算
dis_frame = cv2.bitwise_and(thresh1, thresh2)  # 二值化图像
# 更新,便于下一次差分
frame1, frame2 = frame2, temp_frame

2. 背景减除法

采用OpenCV的createBackgroundSubtractorMOG2实现。
这个也是以高斯混合模型为基础的背景 / 前景分割算法。它是以2004年和2006年Z.Zivkovic的两篇文章为基础的。这个算法的一个特点是它为每
一个像素选择一个合适数目的高斯分布。这样就会对由于亮度等发生变化引起的场景变化产生更好的适应。

cv2.createBackgroundSubtractorMOG2(history, varThreshold, detectShadows)

该方法可以选择是否检测阴影。如果detectShadows = True(默认值),它就会检测并将影子标记出来,但是这样做会降低处理速度。影子会被标记为灰色。

fgbg = cv2.createBackgroundSubtractorMOG2()
fgmask = fgbg.apply(frame)

3. 光流法

所谓光流就是瞬时速率,在时间间隔很小(比如视频的连续前后两帧之间)时,也等同于目标点的位移

(1)实现流程

(1)首先获取视频或者摄像头的第一帧图像 用goodFeaturesToTrack函数获取初始化的角点
(2)然后开始无限循环获取视频图像帧 将新图像和上一帧图像放入calcOpticalFlowPyrLK函数当中,从而获取新图像的光流。

使用光流法的前提假设:
(1)相邻帧之间的亮度恒定
(2)相邻视频帧的取帧时间连续,或者,相邻帧之间物体的运动比较“微小”;
(3)保持空间一致性;即,同一子图像的像素点具有相同的运动

(2)Python代码

# 从第一帧中选择利于跟踪的特征
p0 = cv2.goodFeaturesToTrack(frame0_gray, mask=None, **feature_params)  # **param 关键字参数
mask = np.zeros_like(frame0)  
tempFrame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
p1, st, err = cv2.calcOpticalFlowPyrLK(frame0_gray, tempFrame_gray, p0, None, **lk_params)
# 选取好的跟踪点
good2track_temp = p1[st == 1]
good2track_0 = p0[st == 1]
# 更新上一帧的图像和追踪点
old_gray = tempFrame_gray.copy()
p0 = good2track_0.reshape(-1, 1, 2)

三、完整代码

运行环境:Python3.8、OpenCV-Python4.5.5.62

# _*_ coding: utf-8 _*_
# @time : 2022/1/12  16:29
# @name : traditionalMethod.py
# @author : 霜晨月~
import cv2
import numpy as np


# 1.1、帧差分法-->二帧法
def dis_2frame():
    cap = cv2.VideoCapture(0)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    last_frame = np.zeros((height, width), dtype=np.uint8)  # 前1帧
    num = 0
    if not cap.isOpened():
        print('Error opening video or file!')
    while cap.isOpened():  # 视频打开成功
        ret, frame = cap.read()
        if ret:
            # 当前帧
            tempFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            # 差分帧
            disFrame = cv2.absdiff(tempFrame, last_frame)
            # 中值滤波去噪
            median = cv2.medianBlur(disFrame, 3)
            # 二值化
            ret, threshold_frame = cv2.threshold(median, 20, 255, cv2.THRESH_BINARY)
            # 高斯模糊
            gauss_image = cv2.GaussianBlur(threshold_frame, (3, 3), 0)
            cv2.imshow('dis_medianBlur_threshold_gaussianBlur', gauss_image)
            # 更新,便于下一次差分
            last_frame = tempFrame
            num += 1
            if cv2.waitKey(20) & 0xFF == 27:
                cv2.destroyAllWindows()
                break
    cap.release()

# 1.2、帧差分法-->三帧法:12相减,23相减,与运算
def dis_3frame():
    cap = cv2.VideoCapture(0)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    frame1 = np.zeros((height, width), dtype=np.uint8)  # 前2帧 1
    frame2 = frame1  # 前1帧 2
    num = 0
    if not cap.isOpened():
        print('Error opening video or file!')

    while cap.isOpened():  # 视频打开成功
        ret, frame = cap.read()
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        if ret:
            # 当前帧
            temp_frame = frame_gray
            # 12差分
            dis_frame1 = cv2.absdiff(frame2, frame1)
            _, thresh1 = cv2.threshold(dis_frame1, 40, 255, cv2.THRESH_BINARY)  # 二值,大于40的为255,小于0
            # 23差分
            dis_frame2 = cv2.absdiff(temp_frame, frame2)
            _, thresh2 = cv2.threshold(dis_frame2, 40, 255, cv2.THRESH_BINARY)  # 二值,大于40的为255,小于0
            # 与运算
            dis_frame = cv2.bitwise_and(thresh1, thresh2)  # 二值化图像
            # 形态学去噪
            kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
            erode = cv2.erode(dis_frame, kernel)  # 腐蚀
            # dilate = cv2.dilate(erode, kernel)  # 膨胀
            # dilate = cv2.dilate(dilate, kernel)  # 膨胀

            img, contours, hei = cv2.findContours(erode.copy(), mode=cv2.RETR_EXTERNAL,
                                                  method=cv2.CHAIN_APPROX_SIMPLE)  # 寻找轮廓
            for contour in contours:
                if 100 < cv2.contourArea(contour) < 40000:
                    x, y, w, h = cv2.boundingRect(contour)  # 找方框
                    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255))
            cv2.namedWindow("binary", cv2.WINDOW_NORMAL)
            cv2.namedWindow("dilate", cv2.WINDOW_NORMAL)
            cv2.namedWindow("frame", cv2.WINDOW_NORMAL)
            cv2.imshow("binary", dis_frame)
            cv2.imshow("dilate", erode)
            cv2.imshow("frame", frame)
            # 更新,便于下一次差分
            frame1, frame2 = frame2, temp_frame
            num += 1
            if cv2.waitKey(20) & 0xFF == 27:
                cv2.destroyAllWindows()
                break
    cap.release()


# 2、背景减除法
def createBackground():
    cap = cv2.VideoCapture(0)
    # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
    # fgbg = cv2.bgsegm.createBackgroundSubtractorGMG()
    fgbg = cv2.createBackgroundSubtractorMOG2()
    while cap.isOpened():
        ret, frame = cap.read()

        fgmask = fgbg.apply(frame)
        # fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
        cv2.imshow('fgmask', fgmask)
        if cv2.waitKey(20) & 0xFF == 27:
            cv2.destroyAllWindows()
            break
    cap.release()


# 3、光流法
def lightFlow():
    cap = cv2.VideoCapture(0)
    feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)  # ShiTomasi 角点检测参数
    ret, frame0 = cap.read()
    w, h = cap.get(3), cap.get(4)
    FPS = cap.get(5)
    print('size:', w, h)
    print('FPS:', FPS)
    frame0_gray = cv2.cvtColor(frame0, cv2.COLOR_BGR2GRAY)
    p0 = cv2.goodFeaturesToTrack(frame0_gray, mask=None, **feature_params)  # **param 关键字参数

    mask = np.zeros_like(frame0)  # 创建一个蒙版用来画轨迹,i.e.和每帧图像大小相同的全0张量
    lk_params = dict(winSize=(15, 15), maxLevel=2,
                     criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))  # lucas kanade光流法参数
    color = np.random.randint(0, 255, (100, 3))  # 创建随机颜色
    while cap.isOpened():
        ret, frame = cap.read()
        tempFrame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        p1, st, err = cv2.calcOpticalFlowPyrLK(frame0_gray, tempFrame_gray, p0, None, **lk_params)
        # 选取好的跟踪点
        good2track_temp = p1[st == 1]
        good2track_0 = p0[st == 1]

        # 画出轨迹
        for i, (new, old) in enumerate(zip(good2track_temp, good2track_0)):
            a, b = new.ravel()
            c, d = old.ravel()
            mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), color[i].tolist(), 2)  # 添加了该帧光流的轨迹图
            frame = cv2.circle(frame, (int(a), int(b)), 5, color[i].tolist(), -1)
        img = cv2.add(frame, mask)  # 将该图和轨迹图合并
        cv2.imshow('frame', img)

        if cv2.waitKey(20) & 0xFF == 27:
            cv2.destroyAllWindows()
            break
        # 更新上一帧的图像和追踪点
        old_gray = tempFrame_gray.copy()
        p0 = good2track_0.reshape(-1, 1, 2)
    cap.release()


if __name__ == '__main__':
    dis_2frame()
    # dis_3frame()
    # createBackground()
    # lightFlow()
  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值