• 【小工具系列】Python + OpenCV 图片序列转换成视频


    图片序列转换成视频

    最近一直在找一个工具,能够将一堆图片转化成视频。网上找了一些小软件,还有 win10 的照片自带的视频制作功能,都不是很满意。
    又不想下载那些专业的视频剪辑软件大材小用。

    然后找到了ffmpeg这个非常出名非常常用的多媒体编解码库,看了下文档试着用了下,它确实可以完成上述功能,但是命令行对输入图片的命名有规定(文件名必须开头相同、且包含连续编号),并且windows下的编译版不支持Pattern type 'glob'
    先尝试了用python写了批量重命名然后调用ffmpeg的命令,然后发现还需要相同的分辨率才行。。。

    最后决定参照网上的其他使用OpenCV(OpenCV在视频处理功能底层也是用的ffmpeg)的博客自己写一个。

    这里先把完整代码和使用说明贴出来:

    import os, sys
    import cv2
    import numpy as np
    import argparse
    
    imgs_path = 'C:\\'
    
    target_size = (1280, 720)
    target_fps = 1.0
    # 输出文件名
    target_video = 'out.mp4'
    # 是否保存 resize 的中间图像
    saveResizeFlag = False
    img_types = ('.bmp', '.dib', '.png', '.jpg', '.jpeg', '.pbm', '.pgm', '.ppm', '.tif', '.tiff')
    
    # 不存在则创建目录
    def mkdir(path):
        if not os.path.exists(path):
            os.mkdir(path)
    
    # 将图片等比例缩放,不足则填充黑边
    def resizeAndPadding(img):
        size = img.shape
        h, w = size[0], size[1]
        target_h, target_w = target_size[1], target_size[0]
    
        # 确定缩放的尺寸
        scale_h, scale_w= float(h / target_h), float(w / target_w)
        scale = max(scale_h, scale_w)
        new_w, new_h = int(w / scale), int(h / scale)
    
        # 缩放后其中一条边和目标尺寸一致
        resize_img = cv2.resize(img, (new_w, new_h))
    
        # 图像上、下、左、右边界分别需要扩充的像素数目
        top = int((target_h - new_h) / 2)
        bottom = target_h - new_h - top
        left = int((target_w - new_w) / 2)
        right = target_w - new_w - left
        # 填充至 target_w * target_h
        pad_img = cv2.copyMakeBorder(resize_img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=[0,0,0]) 
    
        return pad_img
     
     
    def imgs2video():
        output_path = imgs_path + 'out\\'
        mkdir(output_path)
        target = output_path + target_video
        fourcc = cv2.VideoWriter_fourcc(*"mp4v")
        vw = cv2.VideoWriter(target, fourcc, target_fps, target_size)
    
        images = os.listdir(imgs_path)
        count = 0
        for image in images:
            if not (image.lower().endswith(img_types)):
                continue
            try:
                print(image)
                # cv2.waitKey(100)
                # frame = cv2.imread(imgs_path + image)
                # imread 不能读中文路径,unicode也不行
                frame = cv2.imdecode(np.fromfile(imgs_path + image, dtype=np.uint8), cv2.IMREAD_COLOR) #, cv2.IMREAD_UNCHANGED
                
                pad_frame = resizeAndPadding(frame)
                # print(pad_frame.shape)
    
                if saveResizeFlag:
                    # 保存缩放填充后的图片
                    resize_path = imgs_path + 'resize\\'
                    mkdir(resize_path)
                    resize_name = resize_path + 'resize_' + image
                    # cv2.imwrite(resize_name, pad_frame)
                    # imwrite 不能读中文路径,unicode也不行
                    cv2.imencode(os.path.splitext(image)[-1], pad_frame)[1].tofile(resize_name)
                
                # 写入视频
                vw.write(pad_frame)
                count += 1
            except Exception as exc:
                print(image, exc)
        vw.release()
        print('\r\nConvert Success! Total ' + str(count) + ' images be combined into the video at: ' + target + '\r\n')
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(description="Function: convert images to video")
        parser.add_argument('--input', '-i', required = True)
        parser.add_argument('--output', '-o', default='out.mp4')
        parser.add_argument('--fps', '-f', type=float, default = 1.0)
        parser.add_argument('--resolution', '-r', type=int, nargs = 2, default = [1280, 720])
        parser.add_argument('--save', '-s', action='store_true')
        args = parser.parse_args()
        if args.input:
            if not os.path.isdir(args.input):
                print("input is not a directory")
                sys.exit(0)
            imgs_path = args.input
            if not imgs_path.endswith(('\\', '/')):
                imgs_path += os.path.sep
            print('input path: ' + imgs_path)
        if args.output:
            target_video = args.output
            print('output file: ' + target_video)
        if args.fps:
            if not args.fps > 0:
                print('fps should be greater than zero')
                sys.exit(0)
            target_fps = args.fps
            print('output file fps: ' + str(target_fps))
        if args.resolution:
            if not args.resolution[0] > 0 and args.resolution[1] > 0:
                print('resolution should be greater than zero')
                sys.exit(0)
            target_size = (args.resolution[0], args.resolution[1])
            print('output file resolution: ' + str(target_size))
        if args.save:
            saveResizeFlag = True
        imgs2video()
    

    使用方法:

    1. 依赖numpycv2:(当然首先得有 Python 环境)
    pip install numpy
    pip install opencv-python
    
    1. 下载本代码到本地;
    2. 在命令行运行本代码,并指定参数:
    python imgs2video.py -i D:\images\
    python imgs2video.py -i D:\images\ -o test.mp4 -f 0.5 -r 1920 1080 -s
    
    • 参数介绍:
    --input, -i: 输入图片的路径,必须参数;
    --output, -o: 输出视频的名字,默认 out.mp4;
    --fps, -f: 指定的帧率,类型浮点数,默认 1.0;
    --resolution, -r: 指定视频的分辨率,类型两个整数,默认 1280 720;
    --save, -s: 是否保存图片转化分辨率之后的中间结果,默认不保存。
    

    说明

    代码看起来有点长,其实核心功能在函数imgs2video中:创建一个cv2.VideoWriter用于写视频文件,cv2.imdecode读图片,然后缩放,然后写入视频。可选项:cv2.imencode将缩放后的图片保存下来。之所以不用cv2.imreadcv2.imwrite是因为这俩没办法处理中文路径

    函数resizeAndPadding用于将图片等比例缩放至指定分辨率,并往周围填充黑边(想用其他颜色在代码里改一改value=[0,0,0]这里的 RGB 值就行了)。这个函数本身也可以实现某些需求了。

    main里面主要是处理命令行的一些参数,指定输入文件、输出文件、分辨率、帧率等

    输出文件类型目前用的mp4,可以指定其他类型试试。我自己试过avi但是在帧率低的时候表现不太正常。指定其他类型可能需要把fourcc = cv2.VideoWriter_fourcc(*"mp4v")这里的"mp4v"改成其他的,具体怎么改可以搜一下,这里就不一一列举了。(这里有一个奇妙 BUG,写"MP4V"反正会报出错误,虽然能正常生成视频。在StackOverFlow上看见有人说改成"mp4v"就正常了,试了下还真的是。。。。)

    其他功能的说明在上文使用方法里都有写。

    后记

    折腾了一天写了个用处不大的小工具。如果此代码对你有帮助可以在该github仓库点个Star

  • 相关阅读:
    从零入门 Serverless | Serverless Kubernetes 应用部署及扩缩容
    从单体迈向 Serverless 的避坑指南
    从零入门 Serverless | 教你使用 IDE/Maven 快速部署 Serverless 应用
    开发函数计算的正确姿势——OCR 服务
    从零入门 Serverless | 函数计算的开发与配置
    全部满分!阿里云函数计算通过可信云21项测试
    登录接口+三方登录 微博
    注册接口文档
    异步发送短信验证与 注册接口完善
    图片验证码接口
  • 原文地址:https://www.cnblogs.com/recycer/p/12925767.html
Copyright © 2020-2023  润新知