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

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

学习参考资料:

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

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


这部分主要讲两个函数

  1. cv2.VideoCapture 创建视频对象,负责获取视频内容
  2. cv2.VideoWriter() 创建视频写入对象,用来录制/保存视频

其他

  • cv2.cvtColor() 将 BGR 彩色图片转为 灰度图片
  • imwrite() 保存图片
  • cv2.VideoCapture().isOpened() 检测视频读取状态是否正常
  • cv2.VideoCapture().read() 获取视频某一帧,返回两个值,第一个是布尔值,表示“这一帧”是否获取正确;第二个值是这一帧的内容,是一个矩阵。

接下来看例子

cv2.VideoCapture()

  1. 如果传的参数是数字,表示使用摄像头。如 cv2.VideoCapture(0),表示使用计算机第一个摄像头。
  2. 如果传的是一个视频的地址,则表示读取本地视频。这里我只用第二种,给一个视频的绝对路径并读取它

比如:我希望读取一个视频,截取每一帧图像并将其转为灰度图像,并播放

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Ⅱ (我不知道这里说的对不对)

如果我想截取某个时间点的图片。
  1. 首先我们得知道,视频中的每一帧对应多少时间
    • 通过使用的函数capture.get(propId),可以获取视频(/摄像头的一些属性),如 分辨率、亮度、对比度等,我们想知道的帧速度也在这里
    • 传入的参数是数字关键词都可以。propId是从0~18的数字,代表不同的属性,完整的属性列表可以参考: VideoCaptureProperties
    • 如果想修改视频的属性,可以用 capture.set(propId,value)来修改属性值

github 大佬的经验之谈:某些摄像头设定分辨率等参数时会无效,因为它 有固定的分辨率大小支持,一般可在摄像头的资料页中找到。

import cv2 

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 就结束了)
        pass
    elif time1 * fps < timer and timer < time2 * fps:  ## 大于 5 秒,小于 10 秒时,开始截视频
        outfile.write(frame) ## outfile 就是创建VideoWriter对象
    else: ## 大于 10 秒的时候,停止
        print('finished')
        break  ## 退出

capture.release()  ## 清内存,好习惯

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

最后说说改变一些参数的结果

  1. 帧速,这个顾名思义,帧速越大,视频播放的速度越快,比如原本1秒是24帧,如果你改成240帧,相当于你1秒的时间播放了原本10秒时间的内容,将帧速改小同理。
  2. 分辨率, 别想了,只要改动分辨率,就会保存失败(空白文件),这也很容易懂,不可能让你那么容易就提高视频的清晰度嘛,(降低也不行 doge)

推荐阅读更多精彩内容