天天看點

【小工具系列】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. 依賴

    numpy

    cv2

    :(當然首先得有 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.imread

cv2.imwrite

是因為這倆沒辦法進行中文路徑