opencv 实践入门2 - 如何利用 VideoCapture截取视频中的图像

我只是一名搬砖工,无意侵犯各位大佬知识产权。
最近因项目组需要,开始学 图像处理
所以记录一下这个过程以及经验,希望能帮到大家

学习参考资料:

  • 《图像处理的数学修养》
  • github 上的 OpenCV-Python-Tutorial

    若想系统地学习,麻烦移步到 github或买书啃一下

    这部分主要讲两个函数

  • cv2.VideoCapture 创建视频对象,负责获取视频内容
  • cv2.VideoWriter() 创建视频写入对象,用来录制/保存视频
  • cv2.cvtColor() 将 BGR 彩色图片转为 灰度图片
  • imwrite() 保存图片
  • cv2.VideoCapture().isOpened() 检测视频读取状态是否正常
  • cv2.VideoCapture().read() 获取视频某一帧,返回两个值,第一个是布尔值,表示“这一帧”是否获取正确;第二个值是这一帧的内容,是一个矩阵。
  • 接下来看例子

    cv2.VideoCapture()

  • 如果传的参数是数字,表示使用摄像头。如 cv2.VideoCapture(0),表示使用计算机第一个摄像头。
  • 如果传的是一个视频的地址,则表示读取本地视频。这里我只用第二种,给一个视频的绝对路径并读取它
  • 比如:我希望读取一个视频,截取每一帧图像并将其转为灰度图像,并播放

    import cv2 
    capture = cv2.VideoCapture('/Users/0.mp4') ##假设我有个视频放在(/Users/ 目录下,我是 mac 玩家,win 玩家自行修改)
    while(capture.isOpened()):  ## 检测视频打开是否有问题
        ret,frame = capture.read()  ## 逐帧读取视频
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) ## 灰度转换
        cv2.imshow('frame', gray)  ## (播放)看图
        if cv2.waitKey(30) == ord('q'): ## 控制退出,选中窗口按'q' (大写不行)
            break
    capture.release()  ## 清内存,好习惯
    cv2.destroyAllWindows()  ## 后面这两句不写,看图的窗口就关不了
    cv2.waitKey(1)
    ### 为了上面好看点,我把注释写在下面了
    ### capture.isOpened()返回的是布尔值,如果成功读取这一帧,则为 True; 反之为 False
    ### ret 其实就是capture.isOpened(), 而 frame 这是获取的这一帧图像的内容,是矩阵格式
    ###  cv2.cvtColor()  将 BGR 彩色图片转为 灰度图片
    ### cv2.imshow() 第一个参数是打开播放图像窗口的名字,自己随便命名;第二个参数是要看的图像名称,就是 gray
    ### cv2.waitKey(),它的参数表示暂停时间,所以这个值越大,视频播放速度越慢,反之,播放速度越快,通常设置为25或30。 ord 是转码,因为 opencv 内核是 C++,用的是 ASCⅡ (我不知道这里说的对不对)
    
    如果我想截取某个时间点的图片。
  • 首先我们得知道,视频中的每一帧对应多少时间
  • 通过使用的函数capture.get(propId),可以获取视频(/摄像头的一些属性),如 分辨率、亮度、对比度等,我们想知道的帧速度也在这里
  • 传入的参数是数字关键词都可以。propId是从0~18的数字,代表不同的属性,完整的属性列表可以参考: VideoCaptureProperties
  • 如果想修改视频的属性,可以用 capture.set(propId,value)来修改属性值
  • capture = cv2.VideoCapture('/Users/0.mp4') timepoint = 10 ###(单位:秒) 想截取视频中 10s 那一帧 timer = 0 ### 用来计算帧数, fps = round(capture.get(5)) ### 1 秒有多少帧, 使用的参数是5,表示帧速 (要用怎么自己查表) ### 一般来说,1 秒 约等于 24 帧,我这个视频是 23.976023976023978, 所以直接用round(), 四舍五入,将其约等于 24. while(capture.isOpened()): ## 检测视频打开是否有问题 timer += 1 ## 开始计算过去了多少帧 ret,frame = capture.read() ## 逐帧读取视频 if timer == timepoint * fps: ## 10秒的时候,开始截图 cv2.imwrite('/Users/10s.jpg',frame) ## 存图,第一个参数递质,第二个要存的图是哪个 break ## 退出 capture.release() ## 清内存,好习惯
    如果我看视频看到某一帧想截图 (这个更简答了是不是,和第一个很像)

    我们来个暗号,比如 按下键盘 ‘s’ 就截图

    import cv2 
    capture = cv2.VideoCapture('/Users/0.mp4') 
    while(capture.isOpened()):  ## 检测视频打开是否有问题
        ret,frame = capture.read()  ## 逐帧读取视频
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) ## 灰度转换
        cv2.imshow('frame', gray)  ## (播放)看图
        if cv2.waitKey(30) == ord('s'): ## 控制退出,选中窗口按'q' (大写不行)
            cv2.imwrite('/Users/10s.jpg',frame) ### 其实就多了这个
            break
    capture.release()  ## 清内存,好习惯
    cv2.destroyAllWindows()  ## 后面这两句不写,看图的窗口就关不了
    cv2.waitKey(1)
    

    如果我觉得截图不过瘾,想截一段动画(gif) 或 截一段视频

    这次要加上存视频的函数 cv2.VideoWriter()
    思路还是一样,假设我们想截 第 5 秒到第 10 秒的视频

    cv2.VideoWriter()
    先说说这个函数,它需要四个参数
    cv2.VideoWriter(filename, fourcc, fps, resolution)

  • 第一个参数 是文件名,如‘output.avi’ (绝对路径,你保存文件的位置)
  • 第二个参数 是编码方式,这里选的是 FourCC码 (我也没搞懂,反正先用着吧)
  • 第三个参数是帧率 FPS (又叫帧速),一般和原视频一样,24,有兴趣的可以试试更大或更小的数字,发现新世界
  • 第四个是分辨率 (每英寸像素Pixel per inch, ppi)
  • 480P: 720×480 (宽 × 高
  • 720p: 1280×720
  • 1080p: 1920×1080
  • 可以试试原画是 480P 转 1080P 输出会变什么样子 (查原视频分辨率用 .get(3) 宽,.get(4) 高
  • 还有一个FourCC是用来指定视频编码方式的四字节码,所有的编码可参考 Video Codecs 。如MJPG编码可以这样写: cv2.VideoWriter_fourcc(*’MJPG’)或cv2.VideoWriter_fourcc(‘M’,’J’,’P’,’G’)
    (这个我也不懂,先用着吧)

    那开始干吧

    import cv2 
    capture = cv2.VideoCapture('/Users/test.mp4')
    time1 = 5   ###(单位:秒) 截取开始时间 第5秒
    time2 = 10  ###(单位:秒) 截取结束时间 第10秒
    timer = 0   ### 用来计算帧数,
    fps = round(capture.get(5))  ### 获取 1 秒有多少帧
    ### 定义编码方式并创建VideoWriter对象
    fourcc = cv2.VideoWriter_fourcc(*'MJPG')
    outfile = cv2.VideoWriter('/Users/test_5s.avi',fourcc, 24, (640, 360))  ###我这边测试视频 帧频是24,分辨率是640×360, 可以分别用.get(5), .get(3)和.get(4)获取, 当然你也可以改动一下试试看什么效果
    while(capture.isOpened()):  ## 检测视频打开是否有问题
        timer += 1  ## 开始计算过去了多少帧
        ret,frame = capture.read()  ## 逐帧读取视频
        if time1 * fps >= timer: ## 小于 5 秒时,跳过 (为什么要? 因为没有这个会直接跳到 else,break 就结束了)
        elif time1 * fps < timer and timer < time2 * fps:  ## 大于 5 秒,小于 10 秒时,开始截视频
            outfile.write(frame) ## outfile 就是创建VideoWriter对象
        else: ## 大于 10 秒的时候,停止
            print('finished')
            break  ## 退出
    capture.release()  ## 清内存,好习惯
    

    OK 啦,是不是很简单,快来试试吧