用Python提取视频中的图片

小编自己码的通用型函数,支持各种常用视频格式,可满足常用需求,亲测效果和速度都不错。

想获取本文数据和完整代码的下载链接,请关注微信公众号"R语言和Python学堂",并回复发文日期"20181103"。

最近在帮着处理实验上的一些视频,我们知道视频是由一帧一帧图片叠加而制成的。其中第一步就是要把视频中的图片提取出来,怎么做呢?

有人会说,网上应该有很多相关小软件可以做这个事情。仔细一想,大多数软件应该不能满足以后的需求,比如:批量处理视频,提取视频某段时间内的图片,每隔一段时间提取一张图片。。。

Google了一下,发现Python在处理视频方面表现非常优秀,多数都是基于OpenCV库的。为了解决我的需求,因此决定自己写个基于Python+OpenCV的通用函数,来解决以后在提取图片过程中的各种需求。

这个函数就是本文要介绍的video2frames()函数,功能就是从视频中提取图片,名称“video2frames”是我自己取的,还比较形象。现将它分享给大家,感兴趣的小伙伴们可以参考一下,完整代码附在文末。

1. 主要功能

这个函数有以下主要功能:

  • 提取特定时间点图片,比如:提取视频第3秒, 第5秒,第9秒图片

  • 设定提取的起始时刻,比如:从视频的第10秒开始提取

  • 设定提取的终止时刻,比如:100秒后的视频不提取图片

  • 设定每隔多少秒提取一张图片,比如:每隔2秒从视频中提取一张图片

2. 函数参数

video2frames()函数的原型为:

video2frames(pathIn='', 
             pathOut='', 
             only_output_video_info = False, 
             extract_time_points = None, 
             initial_extract_time = 0,
             end_extract_time = None,
             extract_time_interval = -1, 
             output_prefix = 'frame',
             jpg_quality = 100,
             isColor = True)

各参数的意义:

  • pathIn:视频的路径,比如:F:\python_tutorials\test.mp4

  • pathOut:设定提取的图片保存在哪个文件夹下,比如:F:\python_tutorials\frames\。如果该文件夹不存在,函数将自动创建它

  • only_output_video_info:如果为True,只输出视频信息(长度、帧数和帧率),不提取图片

  • extract_time_points:提取的时间点,单位为秒,为元组数据,比如,(2, 3, 5)表示只提取视频第2秒, 第3秒,第5秒图片

  • initial_extract_time:提取的起始时刻,单位为秒,默认为0(即从视频最开始提取)

  • end_extract_time:提取的终止时刻,单位为秒,默认为None(即视频终点)

  • extract_time_interval:提取的时间间隔,单位为秒,默认为-1(即输出时间范围内的所有帧)

  • output_prefix:图片的前缀名,默认为frame,那么图片的名称将为frame_000001.jpgframe_000002.jpgframe_000003.jpg......

  • jpg_quality:设置图片质量,范围为0100,默认为100(质量最佳)

  • isColor:如果为False,输出的将是黑白图片

目前只支持输出jpg格式图片

3. 例子

下面来测试一下这个函数的功能:

  • 设置only_output_video_infoTrue,将只输出视频信息,不提取图片
>>> pathIn = 'test.mp4'
>>> video2frames(pathIn, only_output_video_info=True)
only output the video information (without extract frames)::::::
Duration of the video: 5.28 seconds
Number of frames: 132
Frames per second (FPS): 25.0

可以看到,视频test.mp4的长度为5.28秒,共132帧,帧率为25.0

  • 提取所有图片,并保存到指定文件夹下
>>> pathIn = 'test.mp4'
>>> pathOut = './frames1/'
>>> video2frames(pathIn, pathOut)
Converting a video into frames......
Write a new frame: True, 1/132
Write a new frame: True, 2/132
..............................
Write a new frame: True, 131/132
Write a new frame: True, 132/132

可以看到,视频的132帧图片全部提取到frames1文件夹下

  • 设置extract_time_points参数,提取特定时间点的图片
>>> pathIn = 'test.mp4'
>>> pathOut = './frames2'
>>> video2frames(pathIn, pathOut, extract_time_points=(1, 2, 5))
Write a new frame: True, 1th
Write a new frame: True, 2th
Write a new frame: True, 3th

可以看到,只提取了第1秒,第2秒和第5秒图片

  • 每隔一段时间提取图片,并设置初始时刻和终止时刻
>>> pathIn = 'test.mp4'
>>> pathOut = './frames3'
>>> video2frames(pathIn, pathOut,
                 initial_extract_time=1,
                 end_extract_time=3,
                 extract_time_interval = 0.5) 
Converting a video into frames......
Write a new frame: True, 1th
Write a new frame: True, 2th
Write a new frame: True, 3th
Write a new frame: True, 4th
Write a new frame: True, 5th

可以看到,1到3秒内的视频每隔0.5秒提取图片,共5张图片(分别为1s, 1.5s, 2s, 2.5s, 3s时刻的图片)

  • 设置jpg_quality参数,改变输出图片的质量
>>> pathOut = './frames4'
>>> pathIn = 'test.mp4'
>>> video2frames(pathIn, pathOut, extract_time_points=(0.3, 2), jpg_quality=50)
Write a new frame: True, 1th
Write a new frame: True, 2th
  • 设置isColor参数为False,提取的照片将是黑白色
>>> pathOut = './frames5'
>>> pathIn = 'test.mp4'
>>> video2frames(pathIn, pathOut, extract_time_points=(0.3, 2), isColor=False)
Write a new frame: True, 1th
Write a new frame: True, 2th

video2frames()函数的功能测试到此结束。

4. 完整代码

函数为通用型的,因此代码较长,可能还存在可以优化的地方,仅供参考。

完整代码如下:

# -*- coding: utf-8 -*-
import os
import cv2    ##加载OpenCV模块

def video2frames(pathIn='', 
                 pathOut='', 
                 only_output_video_info = False, 
                 extract_time_points = None, 
                 initial_extract_time = 0,
                 end_extract_time = None,
                 extract_time_interval = -1, 
                 output_prefix = 'frame',
                 jpg_quality = 100,
                 isColor = True):
    '''
    pathIn:视频的路径,比如:F:\python_tutorials\test.mp4
    pathOut:设定提取的图片保存在哪个文件夹下,比如:F:\python_tutorials\frames1\。如果该文件夹不存在,函数将自动创建它
    only_output_video_info:如果为True,只输出视频信息(长度、帧数和帧率),不提取图片
    extract_time_points:提取的时间点,单位为秒,为元组数据,比如,(2, 3, 5)表示只提取视频第2秒, 第3秒,第5秒图片
    initial_extract_time:提取的起始时刻,单位为秒,默认为0(即从视频最开始提取)
    end_extract_time:提取的终止时刻,单位为秒,默认为None(即视频终点)
    extract_time_interval:提取的时间间隔,单位为秒,默认为-1(即输出时间范围内的所有帧)
    output_prefix:图片的前缀名,默认为frame,图片的名称将为frame_000001.jpg、frame_000002.jpg、frame_000003.jpg......
    jpg_quality:设置图片质量,范围为0到100,默认为100(质量最佳)
    isColor:如果为False,输出的将是黑白图片
    '''
    
    cap = cv2.VideoCapture(pathIn)  ##打开视频文件
    n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  ##视频的帧数
    fps = cap.get(cv2.CAP_PROP_FPS)  ##视频的帧率
    dur = n_frames/fps  ##视频的时间
    
    ##如果only_output_video_info=True, 只输出视频信息,不提取图片
    if only_output_video_info:
        print('only output the video information (without extract frames)::::::')
        print("Duration of the video: {} seconds".format(dur))
        print("Number of frames: {}".format(n_frames))
        print("Frames per second (FPS): {}".format(fps)) 
    
    ##提取特定时间点图片
    elif extract_time_points is not None:
        if max(extract_time_points) > dur:   ##判断时间点是否符合要求
            raise NameError('the max time point is larger than the video duration....')
        try:
            os.mkdir(pathOut)
        except OSError:
            pass
        success = True
        count = 0
        while success and count < len(extract_time_points):
            cap.set(cv2.CAP_PROP_POS_MSEC, (1000*extract_time_points[count])) 
            success,image = cap.read()
            if success:
                if not isColor:
                    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  ##转化为黑白图片
                print('Write a new frame: {}, {}th'.format(success, count+1))
                cv2.imwrite(os.path.join(pathOut, "{}_{:06d}.jpg".format(output_prefix, count+1)), image, [int(cv2.IMWRITE_JPEG_QUALITY), jpg_quality])     # save frame as JPEG file
                count = count + 1

    else:
        ##判断起始时间、终止时间参数是否符合要求
        if initial_extract_time > dur:
            raise NameError('initial extract time is larger than the video duration....')
        if end_extract_time is not None:
            if end_extract_time > dur:
                raise NameError('end extract time is larger than the video duration....')
            if initial_extract_time > end_extract_time:
                raise NameError('end extract time is less than the initial extract time....')
        
        ##时间范围内的每帧图片都输出
        if extract_time_interval == -1:
            if initial_extract_time > 0:
                cap.set(cv2.CAP_PROP_POS_MSEC, (1000*initial_extract_time)) 
            try:
                os.mkdir(pathOut)
            except OSError:
                pass
            print('Converting a video into frames......')
            if end_extract_time is not None:
                N = (end_extract_time - initial_extract_time)*fps + 1
                success = True
                count = 0
                while success and count < N:
                    success,image = cap.read()
                    if success:
                        if not isColor:
                            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
                        print('Write a new frame: {}, {}/{}'.format(success, count+1, n_frames))
                        cv2.imwrite(os.path.join(pathOut, "{}_{:06d}.jpg".format(output_prefix, count+1)), image, [int(cv2.IMWRITE_JPEG_QUALITY), jpg_quality])     # save frame as JPEG file
                        count =  count + 1
            else:
                success = True
                count = 0
                while success:
                    success,image = cap.read()
                    if success:
                        if not isColor:
                            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
                        print('Write a new frame: {}, {}/{}'.format(success, count+1, n_frames))
                        cv2.imwrite(os.path.join(pathOut, "{}_{:06d}.jpg".format(output_prefix, count+1)), image, [int(cv2.IMWRITE_JPEG_QUALITY), jpg_quality])     # save frame as JPEG file
                        count =  count + 1

        ##判断提取时间间隔设置是否符合要求    
        elif extract_time_interval > 0 and extract_time_interval < 1/fps:
            raise NameError('extract_time_interval is less than the frame time interval....')
        elif extract_time_interval > (n_frames/fps):
            raise NameError('extract_time_interval is larger than the duration of the video....')
        
        ##时间范围内每隔一段时间输出一张图片
        else:
            try:
                os.mkdir(pathOut)
            except OSError:
                pass
            print('Converting a video into frames......')
            if end_extract_time is not None:
                N = (end_extract_time - initial_extract_time)/extract_time_interval + 1
                success = True
                count = 0
                while success and count < N:
                    cap.set(cv2.CAP_PROP_POS_MSEC, (1000*initial_extract_time+count*1000*extract_time_interval)) 
                    success,image = cap.read()
                    if success:
                        if not isColor:
                            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
                        print('Write a new frame: {}, {}th'.format(success, count+1))
                        cv2.imwrite(os.path.join(pathOut, "{}_{:06d}.jpg".format(output_prefix, count+1)), image, [int(cv2.IMWRITE_JPEG_QUALITY), jpg_quality])     # save frame as JPEG file
                        count = count + 1
            else:
                success = True
                count = 0
                while success:
                    cap.set(cv2.CAP_PROP_POS_MSEC, (1000*initial_extract_time+count*1000*extract_time_interval)) 
                    success,image = cap.read()
                    if success:
                        if not isColor:
                            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
                        print('Write a new frame: {}, {}th'.format(success, count+1))
                        cv2.imwrite(os.path.join(pathOut, "{}_{:06d}.jpg".format(output_prefix, count+1)), image, [int(cv2.IMWRITE_JPEG_QUALITY), jpg_quality])     # save frame as JPEG file
                        count = count + 1



##### 测试
pathIn = 'test.mp4'
video2frames(pathIn, only_output_video_info = True)

pathOut = './frames1/'
video2frames(pathIn, pathOut)

pathOut = './frames2'
video2frames(pathIn, pathOut, extract_time_points=(1, 2, 5))

pathOut = './frames3'
video2frames(pathIn, pathOut,
             initial_extract_time=1,
             end_extract_time=3,
             extract_time_interval = 0.5)   

pathOut = './frames4/'
video2frames(pathIn, pathOut, extract_time_points=(0.3, 2), isColor = False)


pathOut = './frames5/'
video2frames(pathIn, pathOut, extract_time_points=(0.3, 2), jpg_quality=50)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本公众号。


感谢您的阅读!想了解更多有关Python技巧,请关注我的微信公众号“R语言和Python学堂”,我将定期更新相关文章。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容