最近 AI 因为 ChatGPT 大热,正好找个由头玩一下,家里为了看娃装了个摄像头,一周拍的录像大概 20 多 G,可以拿来练练手。之前都是老婆人肉一个个看的,我这次先筛选出有动静的片段,后续老婆再看

最初的想法,是使用 haarcascades 分类器的,毕竟是老牌分类器,又是 opencv 自带的,公司用了一段时间也还行。但是 full_body 和 frontalface 都试过了,家里光线和环境比较复杂,误报很多。无奈换成了有动静才保留对应视频,这里参考了 利用 OpenCV 过滤监控视频,定期删除静态视频 这个方法比我之前逐帧分析高效太多了,是跳着看的,中间也能利用 GPU,就是不确定是高斯模糊用上了 GPU,还是图像对比 findContours 用上了

最终代码大致如下:

#encoding:utf8

import cv2
import numpy as np
import glob
import os
import shutil
import imutils

main_dir = " 目录 "
video_files = glob.glob(main_dir + '/**/*.mp4', recursive=True)

# 检测视频是否有活动画面
def check_for_movement(video_path):
    camera = cv2.VideoCapture(video_path)
    total = camera.get(cv2.CAP_PROP_FRAME_COUNT)  # 总帧数
    fps = camera.get(cv2.CAP_PROP_FPS)  # 帧率
    # print(int(total / fps))   # 视频时间
    step = (total - 100) / 9  # 间隔时间

    # 初始化视频流的第一帧
    lastFrame = None
    occupied = False

    try:
        # 遍历视频的每一帧
        for i in range(100, int(total), int(step)):
            # 获取当前帧并初始化
            camera.set(cv2.CAP_PROP_POS_FRAMES, i)
            camera.grab()  # 解码并返回捕获的视频帧
            (grabbed, frame) = camera.read()

            # 如果不能抓取到一帧,说明我们到了视频的结尾
            if not grabbed:
                break

            # 调整该帧的大小,转换为灰阶图像并且对其进行高斯模糊
            frame = imutils.resize(frame, width=500)
            camera_area = frame.shape[0] * frame.shape[1]
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            gray = cv2.GaussianBlur(gray, (21, 21), 0)

            # 如果第一帧是 None,对其进行初始化
            if lastFrame is None:
                lastFrame = gray
                continue

            # 计算当前帧和第一帧的不同
            frameDelta = cv2.absdiff(lastFrame, gray)
            thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]

            # 扩展阀值图像填充孔洞,然后找到阀值图像上的轮廓
            thresh = cv2.dilate(thresh, None, iterations=2)
            (cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
                cv2.CHAIN_APPROX_SIMPLE)

            # 将当前帧置为上一帧
            lastFrame = gray

            # 遍历轮廓
            for c in cnts:
                # 如果变化太小,忽略
                ca = cv2.contourArea(c)
                if  ca < 500 or ca > camera_area/2:
                    continue
                occupied = True
                break
            else:
                continue
            break
    except Exception as e:
        print(e)
        print("err on:", video_path)
    finally:
        # 清理摄像机资源
        camera.release()
        
    return occupied


def main():
    total = len(video_files)
    count = 0
    for filename in video_files:
        count = count + 1
        print(count, "/", total, "percent:{:.2%}".format(count/total))
        if check_for_movement(filename):
            basename = os.path.basename(filename)
            shutil.copy(filename, "output/"+basename)
        

    
if __name__ == "__main__":
    main()

写完后发现,有人写了个 Python 包做这个事情,叫 DVR-Scan,不用自己写代码了。。。回头试试用一下。话说 ChatGPT 3.5 真的比较弱,这个话题反复问也没给到很好的解决方案。。。