OpenCV对指定目录下所有视频批量生成九宫格缩略图

最近接到一个客户的订单,需要批量对电影片段截图,为了方便,于是我撸了一下午代码,写了这么一个辅助工具出来,希望对各位有相似需求的朋友们提供帮助。程序是用Python实现的。

大致思路:

1. 使用os.walk遍历当前目录(包括子目录)下所有文件,获得文件路径列表,再对列表中的文件分别处理。这里为了方便显示进度,我使用了tqdm来枚举列表。

2. 使用OpenCV的VideoCapture对视频等距截取9张图片。对视频截图的操作就像读文件时的操作,是存在一个视频帧的“指针”的,一帧图片截完了以后需要根据步长(ceil(总帧数/8)-1,减1是为了防止某些视频最后一帧为空而导致截图失败退出)更新指针。

3. 重点来了:将截取到的9张图片,每3张一组,使用np.concatenate横向拼接得到img0, img1, img2,最后将这三组图片用同样的方法纵向拼接得到img9,即为不带任何信息的纯九宫格图片。

关于numpy中矩阵拼接函数concatenate的用法,可以参考这篇博文:np.concatenate() 使用说明_Tchunren的博客-CSDN博客_np.concatenate([xi, x],axis=1

4. 使用cv2.copyMakeBorder在上述的img9的最上方画高为200的灰色边框,用于容纳视频的基本信息文字。然后获取视频的基本信息,包括文件大小、分辨率、时长、帧率等。

提示:opencv中获取分辨率使用cap.get(cv2.CAP_PROP_FRAME_HEIGHT),和cap.get(cv2.CAP_PROP_FRAME_WIDTH),分别取得帧的高和宽;

获取视频帧率使用cap.get(cv2.CAP_PROP_FPS);

获取视频时长,先使用cap.get(cv2.CAP_PROP_FRAME_COUNT)获取总帧数,再除以上述的帧率即可。

最后使用cv2.putText将上面的信息文字写入图片。

关于opencv中copyMakeBorder函数的用法,可以参考这篇博文:

OpenCV-Python: cv2.copyMakeBorder()函数详解_我是大黄同学呀的博客-CSDN博客_copymakeborder函数详解

关于opencv中putText函数的用法,可以参考这篇博文:

opencv中puttext()函数用法总结(03)_洛克家族的博客-CSDN博客_opencv puttext

最后附上效果图:

代码:

import cv2
import numpy as np
import os
from tqdm import tqdm

BLACK = [0, 0, 0]
GRAY = [135, 139, 142]

# Turn the large image into a small one, without changing the aspect ratio, and add black borders to make up for the
# shortage.
def my_resize(image, height, width):
    h, w, c = image.shape
    top, bottom, left, right = (0, 0, 0, 0)
    # assert h >= height and w >= width, 'ratio should less than 1'
    long_side = max(h, w)
    if h >= w:
        ratio = float(height) / long_side
    elif h < w:
        ratio = float(width) / long_side

    # resize the long side and add black border to the both size of the short side
    resi = cv2.resize(image, (0, 0), fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    res_height, res_width, res_c = resi.shape

    if h >= w:
        if res_width < width:
            dw = width - res_width
            left = dw // 2
            right = dw - left

    elif h < w:
        if res_height < height:
            dh = height - res_height
            top = dh // 2
            bottom = dh - top

    dst = cv2.copyMakeBorder(resi, top, bottom, left, right, cv2.BORDER_CONSTANT, value=BLACK)
    return dst

def get_hms(seconds):
    m, s = divmod(seconds, 60)
    h, m = divmod(m, 60)
    str = "%02d:%02d:%02d" % (h, m, s)
    return str

def escribir_info(img, fname, infos = ()):
    addtext = img.copy()
    msg0 = 'File Name: ' + fname
    msg1 = 'File Size: ' + str(infos[0]) + ' bytes (' + str(round(infos[0] / 1024.0 / 1024.0, 2)) + ' MB)'
    msg2 = 'Resolution: ' + str(int(infos[2])) + 'x' + str(int(infos[1]))
    msg3 = 'FPS: ' + str(round(infos[3], 2))
    seconds = round(infos[4] / infos[3], 3)
    msg4 = 'Duration: ' + get_hms(seconds) + ' (' + str(seconds) + ' s)'
    cv2.putText(addtext, msg0, (20, 30), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255), 2)
    cv2.putText(addtext, msg1, (20, 70), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255), 2)
    cv2.putText(addtext, msg2, (20, 110), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255), 2)
    cv2.putText(addtext, msg3, (20, 150), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255), 2)
    cv2.putText(addtext, msg4, (20, 190), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255), 2)
    return addtext

def gen_captura(img_path, path_dir):
    cap = cv2.VideoCapture(img_path)
    frame_cnt = cap.get(cv2.CAP_PROP_FRAME_COUNT)
    frame_itv = int(frame_cnt // 8) - 1  # 9 pictures
    s_frame = 1
    flag = 1
    imgs = []

    while cap.isOpened():
        cap.set(cv2.CAP_PROP_POS_MSEC, flag)
        cap.set(cv2.CAP_PROP_POS_FRAMES, s_frame)
        ret, img = cap.read()
        try:
            dst = my_resize(img, 360, 640)
        except Exception as e:
            print('Error while capturing thumbnails: ' + str(e) + '\nNow we will use a black image to fill')
            dst = np.zeros((360, 640, 3), np.uint8)
            dst[:] = [0, 0, 0]
        finally:
            imgs.append(dst)
        flag += 1
        s_frame += frame_itv
        if s_frame >= frame_cnt:
            break

    # Stitching images into a nine-box grid
    img0 = np.concatenate(imgs[:3], 1)
    img1 = np.concatenate(imgs[3:6], 1)
    img2 = np.concatenate(imgs[6:], 1)
    img9 = np.concatenate([img0, img1, img2], 0)
    res = cv2.copyMakeBorder(img9, 200, 0, 0, 0, cv2.BORDER_CONSTANT, value=GRAY)
    pint = os.path.basename(img_path)
    res = escribir_info(res, pint, (os.path.getsize(img_path),
                                    cap.get(cv2.CAP_PROP_FRAME_HEIGHT), cap.get(cv2.CAP_PROP_FRAME_WIDTH),
                                    cap.get(cv2.CAP_PROP_FPS), cap.get(cv2.CAP_PROP_FRAME_COUNT)))

    cv2.imwrite(os.path.join(path_dir, os.path.splitext(pint)[0] + '_scr.jpg'), res)

    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    path_dir = input('Please input directory path that contains video:')
    if not os.path.exists(path_dir):
        print('Directory does not exist!')
        exit(0)
    proc_list = []
    for r, d, files in os.walk(path_dir):
        for file in files:
            if file.endswith('mp4') or file.endswith('mkv') :
                padre = os.path.join(r, file)
                padre = padre.replace('\\', '/')
                proc_list.append((padre, r))
    for i, tp in tqdm(enumerate(proc_list)):
        print('Now processing video: ' + tp[0])
        gen_captura(tp[0], tp[1])

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值