Создаем thumbnails для видео с python и opencv

  • Tutorial


Порой, разбирая завалы больших и малых видеофайлов в папке(папках) нет времени заглядывать в содержимое каждого файла. Тут на ум приходят так называемые thumbnails, которые позволяют в виде нарезки фрагментов из видео, создать представление о содержимом.

Создадим небольшую программу, которая создаст thumbnails для каждого из файлов в текущей папке windows, и добавит timeline к вырезанным файлам.

Стандартный импорт модулей в начале программы на python:

import numpy as np
import cv2
import os

Укажем, в какай папке искать файлы и добавим сообщение для пользователя:

file=file
print('Подождите...')
path=r'E:\1'
os.chdir(path)

Здесь программа обрабатывает все файлы на диске E в папке 1.

Далее вступает в бой opencv, нарезает кадры и timeline к ним:

vidcap = cv2.VideoCapture(path+'\\'+file)
    fps = vidcap.get(cv2.CAP_PROP_FPS)
    #print(fps)
    n=12
    total_frames = vidcap.get(cv2.CAP_PROP_FRAME_COUNT)
    time_line = total_frames / fps

    frames_step = total_frames//n
    time_line_step=time_line//n
    #print(int(time_line_step))
    a=[]
    b=[]

n — количество файлов в нарезке, 12 штук.

Так как нарезка timeline в секундах, чтобы она корректно отображалась на кадрах,
добавим функцию, приводящую к формату времени 00:00:00:

def sec_to_time(t):
        h=str(t//3600)
        m=(t//60)%60
        s=t%60
        if m<10:
            m='0'+str(m)
        else:
            m=str(m)
        if s<10:
            s='0'+str(s)
        else:
            s=str(s)    
        #print(h+':'+m+':'+s)
        t=h+':'+m+':'+s
        return t

Теперь получаем картинки, уменьшаем их размер на 50% и сохраняем их на диск, как промежуточные файлы:

for i in range(n):        
        vidcap.set(1,i*frames_step)
        success,image = vidcap.read()
        #уменьшаем картинку 
        scale_percent = 50
        width = int(image.shape[1] * scale_percent / 100)
        height = int(image.shape[0] * scale_percent / 100)
        image=cv2.resize(image, (width, height))

        # вставка текста красного цвета c time_line
        font = cv2.FONT_HERSHEY_COMPLEX    
        t=int(time_line_step)*i    
        image=cv2.putText(image, sec_to_time(t), (100, 30), font, 0.5, color=(0, 0, 255), thickness=0)   
        cv2.imwrite('image'+str(i)+'.jpg',image)
        a.append('image'+str(i)+'.jpg')
    vidcap.release()

Склеиваем полученные файлы, используя opencv, между собой по горизонтали, соблюдая очередность:

def glue (img1,img2,img3,x):
        i1 = cv2.imread(img1)
        i2 = cv2.imread(img2)
        i3 = cv2.imread(img3)    
        vis = np.concatenate((i1, i2, i3), axis=1)
        cv2.imwrite('out'+str(x)+'.png', vis)
        b.append('out'+str(x)+'.png')
    x=0
    while x<len(a):    
        glue(a[x],a[x+1],a[x+2],x)
        x+=3

Получившиеся «тройки» склеиваем по вертикали:

 #склеиваем видео по вертикали
    def glue2 (img1,img2,img3,img4):
        i1 = cv2.imread(img1)
        i2 = cv2.imread(img2)
        i3 = cv2.imread(img3)
        i4 = cv2.imread(img4) 
        vis = np.concatenate((i1, i2, i3,i4), axis=0)
        cv2.imwrite(file[:-4]+'.jpeg', vis)
    glue2(b[0],b[1],b[2],b[3])

Прибираемся в папке, удаляя временные файлы:

#уборка
    c=['jpg', 'png']
    for root, dirs, files in os.walk(path):    
        for file in files:
            if file[-3:] in c:
                os.remove(file)

Проводим вышеуказанные процедуры для всех видеофайлов в папке:

video=['wmv', 'mp4', 'avi', 'mov', 'MP4', '.rm', 'mkv']
for root, dirs, files in os.walk(r'E:/1'):    
    for file in files:
        if file[-3:] in video:
            print('В обработке-'+file)
            tumbnail(file)

Код программы для тех, к коим отношусь и я, сначала скачивает код, а потом читает статью — скачать.

P.S. timeline не без греха и немного отрывается от реальной timeline видео.

Особенно это заметно на больших видеофайлах.

Средняя зарплата в IT

110 450 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 6 901 анкеты, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 8

    +2

    Не измерял, но подозреваю, что ffmpeg с фильтром tile будет производительнее

      +1

      Измерил, не производительнее :( Видимо потому что ffmpeg по природе своей декодирует все кадры, а не только нужные

        0
        Зависит от кодака и опорного фрейма.(GOP)
          0

          Не зависит, потому что ffmpeg по умолчанию в любом случае декодирует все имеющиеся кадры (о чём я подзабыл, когда писал первый комментарий)


          Можно использовать "-discard nokey" чтобы выбросить все кадры кроме опорных, но это уже немного другая история с менее предсказуемым результатом

        0
        Какой у вас результат для ffmpeg по времени?
        У меня для файла 7,9 Гб — 0:00:23.434035 (23сек).
        +1
        я таки извеняюся, но вот это
        if s<10:
            s='0'+str(s)
        else:
            s=str(s)

        нимнога дичь, патамушта существует форматирование до n-знаков любого числа
        s = f'{s:02d}'
        >>07

        ну или через .format
        прям всю строку можно сформировать одной ф-строкой
        str_m = f'{h}h:{m:02d}m:{s%60:02d}s'
        >>2h:05m:06s


          0
          str_m = f'{h}h:{m:02d}m:{s%60:02d}s'
          >>2h:05m:06s

          Разве это .format?
          Не такой синтаксис должен быть: «I have {0:f} dollars on me».format(100.113)?

          Да, f-строки (как и .format) работают немного быстрее (по моим замерам очень немного быстрее), тем не менее, это более современный синтаксис.
          Если пойти дальше, то можно ifы в lambdы упаковать и т.д.
          Спасибо, за коммент!
            +1
            Разве это .format?

            Нет, это не формат, в данном случае это ф-строка
            Вообще, имелось в виду что в форматировании строки можно дополнить вывод переменной до необходимого количества символов
            Например здесь мы дополняем до трех знаков десятичное число 1 (используется формат):
            s = 1
            "{:3}".format(s)
            >>'  1'

            Здесь перед единицей — два пробела

            А в этом случае мы дополняем до пяти знаков, используя в виде заполнителя цифру ноль (f-строка):
            f"{s:05}"
            >>'00001'


            ну т.е. не городить свой велосипед с ифами а сразу использовать встроенные возможности языка
            За статью спасибо!

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое