运动检测-基于边缘提取与帧间作差-opencv-python

Github:https://github.com/asd123pwj/motion-detect

介绍

  • 这篇是上一篇文章提到的效果更差的:运动检测-基于帧间比较减少灯光影响-opencv-python
  • 这份代码的优势主要在于光照影响很小,如果与前文的帧间比较方式结合起来,我感觉应该是能完全不受光照影响。
  • 但缺点很大,就是白色不敏感,边缘模糊时完全检测不到。
  • 所以就简要介绍下原理和效果,代码就不美化了。

原理

  • 读取视频帧,并转为灰度图。
  • 高斯滤波,取出噪声。
  • Canny边缘提取。
  • GMM模型分离运动前景。
  • 与前一帧作差。
  • 使用绘制区域、腐蚀、膨胀、开运算、闭运算等形态学处理方式消除噪声,连接相邻区域。
  • 绘制区域。

效果

  • 优点
    • 光照影响极小。
    • 能美观标注运动整体,但标注的运动区域容易抖动。
    • 对小运动敏感。
  • 缺点
    • 对大面积白色运动物体敏感度差。
    • 对边缘模糊的运动物体难以标注。
    • 光照会带来少量噪声。

改进方向

代码

import cv2 as cv
import numpy as np

def GMM_process(frame, gray_frame, mog, es):
    gray_frame = mog.apply(gray_frame)

    #   膨胀、腐蚀处理,将相近的运动区块连接
    # gray_frame = cv.erode(gray_frame, es, iterations=1)
    # gray_frame = cv.dilate(gray_frame, es, iterations=1)

    for i in range(20):
        gray_frame = cv.dilate(gray_frame, es, iterations=2)
        gray_frame = cv.erode(gray_frame, es, iterations=1)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(gray_frame, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 300:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(gray_frame, (x, y), (x + w, y + h), (255, 255, 255), -1)

    gray_frame = cv.dilate(gray_frame, es, iterations=10)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(gray_frame, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 200:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
    return frame

def get_edge(gray_frame, es, GB_thres, Canny_thres_1, Canny_thres_2):
    gray_frame = cv.GaussianBlur(gray_frame, (GB_thres, GB_thres), 0)
    # gray_frame = cv.bilateralFilter(gray_frame, 10, 175, 5)
    #   差别图像二值化
    # gray_frame = cv.threshold(gray_frame, 150, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)[1]
    edge_frame = cv.Canny(gray_frame, Canny_thres_1, Canny_thres_2, True)
    # gray_frame = cv.GaussianBlur(gray_frame, (GB_thres, GB_thres), 0)
    return edge_frame

def get_inter_diff(frame, present_frame, last_frame, es):
    inter_diff = cv.absdiff(last_frame, present_frame)

    #   膨胀、腐蚀处理,将相近的运动区块连接
    inter_diff = cv.erode(inter_diff, es, iterations=1)
    inter_diff = cv.dilate(inter_diff, es, iterations=1)

    for i in range(5):
        inter_diff = cv.dilate(inter_diff, es, iterations=2)
        inter_diff = cv.erode(inter_diff, es, iterations=1)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(inter_diff, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 300:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(inter_diff, (x, y), (x + w, y + h), (255, 255, 255), -1)

    inter_diff = cv.dilate(inter_diff, es, iterations=5)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(inter_diff, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 400:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(inter_diff, (x, y), (x + w, y + h), (255, 255, 255), -1)

    inter_diff = cv.dilate(inter_diff, es, iterations=10)
    inter_diff = cv.erode(inter_diff, es, iterations=4)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(inter_diff, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 800:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(inter_diff, (x, y), (x + w, y + h), (255, 255, 255), -1)

    frame_origin = cv.absdiff(last_frame, last_frame)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(inter_diff, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 800:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(frame_origin, (x, y), (x + w, y + h), (255, 255, 255), -1)

    es2 = cv.getStructuringElement(cv.MORPH_CROSS, (3, 8))
    frame_origin = cv.dilate(frame_origin, es2, iterations=12)
    es2 = cv.getStructuringElement(cv.MORPH_CROSS, (8, 3))
    frame_origin = cv.dilate(frame_origin, es2, iterations=7)
    es2 = cv.getStructuringElement(cv.MORPH_CROSS, (5, 7))
    frame_origin = cv.erode(frame_origin, es2, iterations=18)
    # frame_origin = cv.erode(frame_origin, es, iterations=4)
    # inter_diff = cv.absdiff(frame_origin, frame)

    #   运动轮廓提取
    contours, hierarchy = cv.findContours(frame_origin, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    #   轮廓画于图片
    for c in contours:
        #   去除小面积变化噪声
        if cv.contourArea(c) < 200:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)

    return frame

def main():
    #   视频读取
    cap = cv.VideoCapture('testVideo.mp4')
    #   视频fps读取
    fps = cap.get(cv.CAP_PROP_FPS)
    print(fps)
    #   视频大小读取
    size = (int(cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT)))
    #   视频保存编码
    fourcc_mp4 = cv.VideoWriter_fourcc(*'mp4v')
    fourcc_avi = cv.VideoWriter_fourcc(*'XVID')
    #   保存视频
    out_detect = cv.VideoWriter('output_detect.mp4', fourcc_mp4, fps, size, True)

    #   形态学模板创建
    # es = cv.getStructuringElement(cv.MORPH_ELLIPSE, (12, 12))
    es = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3))
    print(es)

    GB_thres = 7
    medianBlur_Threshold = 3
    Canny_thres_1 = 20
    Canny_thres_2 = 60

    inter_diff = None

    mog = cv.createBackgroundSubtractorMOG2()
    last_frame = None
    times = 1
    show = 0
    while cap.isOpened():
        #   处理第times帧
        print(times)
        times += 1
        #   读入视频帧
        ret, frame = cap.read()
        #   视频结束,跳出循环
        if frame is None:
            break
        #   转灰度图
        gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
        #   GMM识别
        # GMM_frame = GMM_process(frame, gray_frame.copy(), mog, es)
        #   GMM识别区内不同
        # intra_diff = cv.absdiff(GMM_frame, gray_frame)
        # intra_edge = get_edge(intra_diff, es, GB_thres, Canny_thres_1, Canny_thres_2)

        if last_frame is not None:
            present_edge = get_edge(gray_frame, es, GB_thres, Canny_thres_1, Canny_thres_2)
            last_edge = get_edge(last_frame, es, GB_thres, Canny_thres_1, Canny_thres_2)
            present_GMM = mog.apply(present_edge)
            last_GMM = mog.apply(last_edge)
            inter_diff = get_inter_diff(frame.copy(), present_GMM, last_GMM, es)
            # inter_diff = cv.absdiff(frame, inter_diff)
            # inter_gray_frame = cv.cvtColor(inter_diff, cv.COLOR_BGR2GRAY)
            # GMM_frame = GMM_process(frame, inter_gray_frame.copy(), mog, es)
            # GMM_frame = GMM_process(frame, inter_diff.copy(), mog, es)
        last_frame = gray_frame

        #   图像实时显示
        if show:
            # cv.namedWindow(frame, cv.WINDOW_NORMAL)
            # cv.imshow(frame, frame)
            # cv.namedWindow(gray_frame, cv.WINDOW_NORMAL)
            # cv.imshow(gray_frame, gray_frame)
            # cv.namedWindow(edge, cv.WINDOW_NORMAL)
            # cv.imshow(edge, edge)

            if inter_diff is not None:
                cv.namedWindow(diff, cv.WINDOW_NORMAL)
                cv.imshow(diff, inter_diff)

            #   按键退出
            if cv.waitKey(1) == ord('q'):
                break

        #   处理结果写入
        out_detect.write(inter_diff)
    #   资源释放
    cap.release()
    cv.destroyAllWindows()
    out_detect.release()

if __name__ == '__main__':
    main()

You may also like...

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注