Hlavní navigace

Použití knihovny MoviePy při úpravách a filtraci videa

Pavel Tišnovský

Ve čtvrtém článku o knihovně MoviePy si ukážeme, jakými způsoby je možné programově upravovat videa, a to včetně aplikace různých typů filtrů poskytovaných touto knihovnou.

Doba čtení: 25 minut

11. Aplikace masky na všechny snímky videa

12. Jednoduchý filtr pro inverzi všech snímků

13. Rozmazání videa v čase (motion blur)

14. Rozmazání s využitím vzdálenějších snímků

15. Jednoduchá kompozice typu „obraz v obraze“

16. Vytvoření jednoduché pipeline s uložením výsledku ve formě matice čtyř videoklipů

17. Vytvoření videa z jednotlivých snímků získaných z rastrových obrázků

18. Repositáře s demonstračními příklady a vygenerovanými videi

19. Odkazy na Internetu

1. Použití knihovny MoviePy při úpravách a filtraci videa

Na předchozí tři části [1] [2] [3] krátkého seriálu o užitečné knihovně MoviePy dnes navážeme. Řekneme si totiž, jakými různými způsoby je možné upravovat již existující videa. S videi lze provádět různé „lineární“ editační operace, například převody do odlišného formátu (s použitím jiného kodeku), modifikaci bitové rychlosti, změnu rozměrů snímků (tím pádem i rozlišení výsledného videa), aplikaci různých filtrů na snímky, aplikaci mezisnímkových filtrů (motion blur), použití masky apod. Kromě toho jsou však podporovány i různé operace nelineární, zejména pak kombinace několika videí (spojení, proložení snímků, obraz v obrazu) apod.

V závěrečné části dnešního článku si ukážeme, jakým způsobem je možné v případě potřeby vytvořit video obsahující jednoduchou animovanou vizualizaci. Pro jednoduchost se bude jednat o mapu, na které jsou v grafické podobě zvýrazněny naměřené veličiny (v našem konkrétním případě se bude jednat o naměřenou a interpolovanou půdní vlhkost, která je dnes poměrně ostře sledována). V tomto případě se tedy knihovna MoviePy použije až v závěrečné fázi celého zpracování dat – od jejich měření, přes interpretaci (vizualizace na mapě) až po výslednou animaci, která zvýrazní změnu v čase. Ovšem díky tomu, že jsou jednotlivé snímky, ze kterých se výsledné video složí, tvořeny poli (přesněji řečeno datovými strukturami typu ndarray), lze i v závěrečné fázi do map různým způsobem zasahovat a provádět v nich různé více či méně zásadní modifikace.

Pro testy filtrace videa bude použita následující volně dostupné odpočítávání získané ze starého filmu:

https://tisnik.github.io/moviepy-videos/videoprocessing_input_video.htm

2. Princip činnosti MoviePy při zpracování existujícího videa

Nejprve se budeme věnovat lineární a nelineární editaci existujícího videa, tj. souborů obsahujících video zakódované nějakým podporovaným kodekem uloženým do zvoleného kontejneru (příkladem bude kombinace Ogg/Theora). Knihovna MoviePy v tomto případě pracuje přibližně následujícím způsobem:

  1. Vstupní soubor je postupně zpracováván známým externím nástrojem pojmenovaným ffmpeg. Výstupem je postupně vytvářená sekvence jednotlivých snímků popř. i audiostopy (ovšem dnes se budeme zabývat pouze zpracováním jednotlivých snímků, i když vstupní video obsahuje i zvukovou stopu).
  2. Jednotlivé snímky jsou v operační paměti počítače reprezentovány datovou strukturou typu ndarray z knihovny Numpy. Díky tomu je možné se snímky pracovat na nejnižší úrovni modifikací jednotlivých pixelů.
  3. Na snímky nebo na jejich sekvenci lze v případě potřeby aplikovat různé filtry, které si popíšeme v dalších kapitolách.
  4. Snímky se na výstupu opět spojují do sekvence, ze které se zvoleným kodekem generuje výsledný soubor s videem. Pro tuto činnost se opět volá externí nástroj ffmpeg.

Ve skutečnosti však může být situace nepatrně složitější, protože současně je možné zpracovávat větší množství videí na vstupu – tato videa lze kombinovat, spojovat, vytvořit snímky ve snímku atd. I s touto zajímavou a užitečnou problematikou se setkáme v navazujících kapitolách.

Příkladem může být použití třídy CompositeVideoClips zajišťující kompozici více vstupních videí:

  1. Vstupní soubory jsou postupně zpracovávány externím nástrojemffmpeg, podobně jako v případě, kdy je vstupem jediné video. Výstupem je tolik sekvencí jednotlivých snímků, kolik souborů se zpracovává.
  2. Na snímky nebo na jejich sekvenci lze aplikovat různé filtry, z nichž některé si popíšeme v dalších kapitolách.
  3. Snímky z jednotlivých sekvencí jsou zkombinovány dohromady. Při této činnosti se typicky používají různé masky (bitové mapy popř. pole s hodnotami od 0.0 do 1.0). Výsledkem kombinace je jediná sekvence výsledných snímků.
  4. Snímky se na výstupu opět spojují do sekvence, ze které se zvoleným kodekem generuje výsledný soubor s videem. Pro tuto činnost se opět volá externí nástroj ffmpeg (tento krok se nijak neliší od zpracování jediného videa).

3. Načtení videa a jeho následné uložení do odlišného formátu

První příklad, který si v dnešním článku ukážeme, bude velmi jednoduchý. Popíšeme si v něm základní použití třídy VideoFileClip, která reprezentuje video načítané z externího souboru a dekódované do operační paměti paměti ve formě jednotlivých snímků. V konstruktoru této třídy je nutné specifikovat jméno souboru s videem a popř. i další nepovinné parametry, zejména pak:

Parametr Význam
has_mask určuje, zda se video načte i s maskou (většinou ovšem není maska specifikována, typicky se jedná o specilitu MoviePy)
audio povolení či zákaz importu zvukových stop (samozřejmě pokud existují)
resize_algorithm specifikace algoritmu použitého při převzorkování snímků (volba mezi větší kvalitou nebo rychlostí převodu a výslednou velikostí videa)
fps_source volba, která metadata obsahují informaci o počtu snímků za sekundu

Třída VideoFileClip obsahuje mj. i metodu pojmenovanou write_videofile(), která slouží pro export videa do zvoleného souboru s využitím kodeku a kontejneru, který je buď přesně specifikovaný, nebo si ho knihovna sama dokáže odvodit z koncovky. V našem případě bude postačovat odvození z koncovky, protože „.ogg“ je jednoznačně mapováno na kontejner/kodek Ogg/Theora. Příklad tedy provede dvě činnosti – načte video ve formátu MPEG-4 a uloží ho do formátu Ogg/Theora (samozřejmě je nutné uvažovat nad ztrátou kvality, která dekódovací-kódovací proces provází!):

from moviepy.editor import VideoFileClip
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
clip.write_videofile('01_output_normal.ogv')
Poznámka: ve skutečnosti je možné stejné funkcionality dosáhnout i přímým zavoláním nástroje ffmpeg, který je interně knihovnou MoviePy používán.

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing01_con­vert_mp4_to_ogv.htm

4. Specifikace bitové rychlosti výsledného videa

Při exportu videa do nového souboru se zvoleným kontejnerem a kodekem je možné specifikovat i mezní bitovou rychlost (bitrate), která samozřejmě ovlivňuje jak kvalitu výsledného videa, tak i jeho celkovou velikost a požadavky na rychlost síťového připojení v případě, že video bude umístěno například na Youtube. Čím nižší bude specifikovaná hodnota bitrate, tím horší bude kvalita výsledného videa a naopak (i když vztah bitová rychlost:kvalita není lineární). Podívejme se na příklad, v němž zvýšíme bitovou rychlost na cca 700000 bitů za sekundu (bps), což je více než implicitní hodnota (proto bylo předchozí video tak nekvalitní):

from moviepy.editor import VideoFileClip
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
clip.write_videofile('02_output_better_bitrate.ogv', bitrate='700000')

Poznámka: kvalita výsledného videa je samozřejmě omezena kvalitou videa vstupního, neboli jak říkají (nejenom) lidé z IT: „garbage in, garbage out“.

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing02_con­vert_change_bitrate.htm

5. Změna rychlosti vytvářeného videa

V některých případech je nutné video zpomalit nebo naopak zrychlit. Zde se již dostáváme k jednomu z filtrů, které jsou v knihovně MoviePy uživatelům-programátorům nabízeny. Tento filtr se jmenuje speedx a lze ho volat jako metodu objektu třídy VideoFileClip, které se předá reálné číslo představující zrychlení (pokud > 1) nebo naopak zpomalení (v případě, že < 1). Výsledkem tohoto filtru bude nové video (obecně třída VideoClip), se kterým je možné provádět naprosto stejné operace jako s jakýmkoli jiným videem.

from moviepy.editor import VideoFileClip
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# novy objekt reprezentující zrychleny video klip
faster_clip = clip.speedx(1.5)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
faster_clip.write_videofile('03_output_faster_clip.ogv', bitrate='700000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing03_con­vert_speedup.htm

6. Střih videa (výběr snímků na časové ose)

Poměrně často se setkáme s požadavkem, aby výsledné video obsahovalo pouze některé sekvence z videa původního (například se odstraňují titulky atd.). I tento požadavek samozřejmě knihovna MoviePy dokáže splnit, a to konkrétně díky existenci filtru nazvaného subclip, kterému se předá čas prvního a posledního snímku z požadované sekvence. Výsledkem této operace je nové video, takže je snadné si postupně vytvořit libovolné množství takových sekvencí a ty posléze spojit do výsledného videa (popř. mezi ně vložit další efekty nebo jiná videa). Opět se podívejme na úplný kód tohoto jednoduchého příkladu:

from moviepy.editor import VideoFileClip
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# novy objekt reprezentujici video klip od 5 do 10 sekundy
sub_clip = clip.subclip(5.0, 10.0)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
sub_clip.write_videofile('04_output_subclip.ogv', bitrate='700000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing04_u­se_subclip.htm

7. Filtry aplikované na jednotlivé snímky

V následujících pěti kapitolách si ukážeme příklady použití některých filtrů, které jsou postupně aplikovány na jednotlivé snímky. Do této skupiny patří především klasické filtry určené pro úpravu rastrových obrazů, změnu velikosti (zmenšení, zvětšení) snímků, aplikaci masky, přidání okrajů ke snímkům (což může být poměrně užitečné, jak uvidíme dále), otočení snímků apod. Speciálním případem je pak filtr typu motion blur, který dokáže vypočítat průměr z několika snímků zdrojového videa. Tento filtr použijeme k vytvoření dvou efektů popsaných v kapitole 13 a 14.

Poznámka: jednotlivé filtry, které budou postupně popsány v navazujících kapitolách, jsou většinou velmi jednoduché, takže je na místě otázka, k čemu se vlastně hodí. Síla (nejenom) těchto filtrů spočívá především v tom, že je lze různými způsoby kombinovat v „pipeline“ a dosáhnout tak mnohem složitějších efektů (typicky při použití masky).

8. Změna rozlišení výsledného videa

Dalším filtrem, který se ovšem bude volat poněkud odlišným způsobem, je filtr určený pro změnu rozlišení jednotlivých snímků a tím pádem i pro změnu rozlišení celého videa. Příslušný filtr se jmenuje resize a předává se mu relativní změna velikosti (1.5 odpovídá 150%, 0.25 pak 1/4). Tento filtr se volá přes univerzální metodu VideoClip.fx(), které se předá jak funkce realizující filtr, tak i její parametry. Výsledkem je opět nový video klip, podobně jako ve všech předchozích případech.

Zvětšení videa na 150 %:

from moviepy.editor import VideoFileClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# novy klip predstavujici video zvetsene na 150% puvodni velikosti
resized_clip = clip.fx(resize, 1.50)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
resized_clip.write_videofile('05_output_larger.ogv', bitrate='700000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing05_re­size_to_larger.htm

Zmenšení videa na pouhou jednu čtvrtinu původní velikosti:

from moviepy.editor import VideoFileClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# novy klip predstavujici video zmensene na 1/4 puvodni velikosti
resized_clip = clip.fx(resize, 0.25)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
resized_clip.write_videofile('06_output_smaller.ogv', bitrate='700000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing06_re­size_to_smaller.htm

9. Modifikace kontrastu

Další jednoduchý filtr, který je aplikovaný postupně na všechny pixely ve snímcích, se sice jmenuje colorx, ovšem ve skutečnosti slouží k velmi jednoduše pojaté změně kontrastu (bez barevného vyvážení a dalších pokročilejších technik). Filtr pracuje následujícím způsobem – každou barvovou složku pixelu vynásobí předanou konstantou. Pokud je konstanta menší než 1, dojde ke snížení kontrastu (a ztmavení snímku), jinak naopak ke zvýšení kontrastu a současně i k posunu celého barevného spektra:

def colorx(clip, factor):
    """ multiplies the clip's colors by the given factor, can be used
        to decrease or increase the clip's brightness (is that the
        reight word ?)
    """
    return clip.fl_image( lambda pic: np.minimum(255,(factor*pic)).
                                                        astype('uint8'))

V demonstračním příkladu, jehož zdrojový kód je vypsán pod tímto odstavcem, se budeme snažit do videa přidat barvy, což se nám sice takto primitivním filtrem samozřejmě nepodaří, protože původní film je monochromatický, ovšem vlivem různých chyb ve videu bude ve výsledcích patrný různobarevný šum atd.:

from moviepy.editor import VideoFileClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# velmi primitivni "kolorizace"
recolored_clip = clip.fx(colorx, 2.50)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
recolored_clip.write_videofile('06_output_recolored.ogv', bitrate='700000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing07_co­lorize.htm

10. Nastavení okrajů okolo videa

Poměrně užitečným filtrem, který má všestranné využití, je filtr určený pro vytvoření okraje okolo jednotlivých snímků. Tento filtr se jmenuje margin a předává se mu požadovaná šířka okraje zadaná v pixelech. Výsledkem aplikace filtru je opět nové video, takže lze zápis provést na jediném řádku; ihned za volání konstruktoru VideoFileClip (původní video ovšem již nebudeme mít k dispozici):

from moviepy.editor import VideoFileClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False).margin(20)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
clip.write_videofile('08_output_with_margin.ogv', bitrate='700000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing08_margin.htm

11. Aplikace masky na všechny snímky videa

V mnoha situacích, například při míchání většího množství zdrojových videí, přidávání titulků do videa, aplikaci selektivního filtru atd. se setkáme s nutností aplikovat masku na zdrojové video, přičemž výsledkem bude sekvence snímků, v nichž budou barvové složky jednotlivých pixelů vynásobeny hodnotou přečtenou z masky. V knihovně MoviePy je maska představována dvourozměrným polem hodnot ležících v rozsahu 0,0 až 1,0, přičemž by toto pole mělo mít stejné rozměry, jaké je rozlišení snímků ve vstupním videu. Pokud budou použity pouze hodnoty 0,0 a 1,0, bude se aplikovat bitová maska, ale ve skutečnosti je možné použít celý rozsah (reálných) hodnot mezi nulou a jedničkou. Taková maska může být na vstupu představována rastrovým obrázkem reprezentovaným ve stupních šedi (speciální a nejčastěji používaný případ monohromatických obrázků). Následuje příklad masky speciálně vytvořené pro naše testovací vstupní video:

Obrázek 1: Maska, která bude použita v dalším demonstračním příkladu.

Maska představovaná rastrovým obrázkem se načte příkazem, jehož výsledkem bude jednosnímkový video klip:

# načtení masky
mask = ImageClip('mask.png', ismask=True)

Existuje větší množství způsobů aplikace takové masky, ovšem nejčastěji se setkáme s využitím třídy nazvané CompositeVideoClips, která vytvoří nový videoklip na základě pole videoklipů, které jsou specifikovány v konstruktoru této třídy. V našem případě se bude jednat o jediný videoklip, ovšem s nastavenou maskou, která se při kompozici automaticky aplikuje:

# aplikace masky
final = CompositeVideoClip([clip.set_mask(mask)])

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing09_masking.htm

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# načtení masky
mask = ImageClip('mask.png', ismask=True)
 
# aplikace masky
final = CompositeVideoClip([clip.set_mask(mask)])
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
final.write_videofile('09_output_masked.ogv', bitrate='600000')
Poznámka: při aplikaci masky se vždy pro jistotu přesvědčte, jestli rozměry masky skutečně odpovídají rozměrům zdrojového videa. Pokud tomu tak není, může se stát, že výsledné video bude mít nastaveno větší rozměry (rozlišení) a bude tedy obsahovat černé okraje.

12. Jednoduchý filtr pro inverzi všech snímků

Další filtr je skutečně velmi jednoduchý, protože slouží pro inverzi všech snímků načtených ze zdrojového videa (přesněji řečeno pro inverzi všech pixelů ve snímcích). I samotná implementace tohoto filtru je pouze dvouřádková:

def invert_colors(clip):
    """ Returns the color-inversed clip.
 
    The values of all pixels are replaced with (255-v) or (1-v) for masks 
    Black becomes white, green becomes purple, etc.
    """
    maxi = (1.0 if clip.ismask else 255)
    return clip.fl_image(lambda f : maxi - f)

Následuje příklad, který ukazuje způsob použití tohoto jednoduchého filtru:

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# inverzní video
inversed_clip = clip.fx(invert_colors)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
inversed_clip.write_videofile('10_inverse_video.ogv', bitrate='600000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing10_in­verse_video.htm

13. Rozmazání videa v čase (motion blur)

Další filtr dostupný v knihovně MoviePy, s nímž se v dnešním článku seznámíme, je již implementačně komplikovanější, než filtry popsané v předchozích kapitolách. Zatímco se totiž předešlé filtry postupně aplikovaly na jednotlivé snímky čtené ze zdrojového videa, je filtr nazvaný poněkud nepřesně supersample aplikován na několik snímků videa v nastaveném rozmezí. Odpovídající pixely všech snímků na vstupu jsou zprůměrovány a výsledné dvourozměrné pole průměrných hodnot je následně použito pro vytvoření snímku výstupního videa. Průměr se počítá pro jednotlivé barvové složky zvlášť. Tomuto filtru se předávají dva parametry:

Parametr Význam
d určuje časový interval ve vstupním videu [t-d, t+d], ze kterého se budou získávat zdrojové snímky
nframes určuje celkový (či maximální) počet snímků ve zvoleném intervalu, z nichž se bude počítat průměr
Poznámka: povšimněte si, že tento filtr ve skutečnosti nemusí zpracovávat sousední snímky ve vstupním videu. Časový interval totiž může být libovolný, například 30 sekund a počet snímků pro průměrování naopak malý (řekněme 10). Potom se snímky pro výpočet „průměrného snímku“ získají takto: snímek nejbližší času t-30, snímek nejbližší času t-30+(60/10) atd.

V následujícím demonstračním příkladu je časový interval velmi krátký a současně je počet snímků pro zprůměrování nastaven na hodnotu 10. Výsledkem bude klasický efekt „rozmazání pohybem“ neboli motion blur (ve skutečnosti může nastat situace, kdy se bude průměrovat méně než deset snímků – vše zde záleží na hodnotě FPS vstupního videa).

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# aplikace rozmazání pohybem
blurred_clip = clip.fx(supersample, 0.25, 10)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
blurred_clip.write_videofile('11_motion_blur.ogv', bitrate='600000')

Výsledné video vytvořené tímto příkladem naleznete na adrese:

https://tisnik.github.io/moviepy-videos/videoprocessing11_mo­tion_blur.htm

14. Rozmazání s využitím vzdálenějších snímků

Filtr supersample, s nímž jsme se ve stručnosti seznámili v předchozí kapitole je však možné použít i zcela opačným způsobem – nastavením časového intervalu na poměrně vysokou hodnotu (zde konkrétně na jednu sekundu) a snížením počtu snímků pro průměrování (zde na pouhých pět snímků):

# aplikace rozmazáni pohybem
blurred_clip = clip.fx(supersample, 1, 5)

Výsledek aplikace tohoto filtru bude odlišný, protože se ve videu budou prolínat tři „reality“ posunuté od sebe o jednu sekundu (minulost, současnost, budoucnost), což je ostatně patrné i po otevření následujícího videa:

https://tisnik.github.io/moviepy-videos/videoprocessing12_mo­tion_blur_B.htm

Poznámka: časový interval t-1 sekunda, t+1 sekunda nebyl zvolen náhodně, protože do určité míry odpovídá odpočítávání na videu (není to zcela přesné kvůli zaokrouhlovacím aj. chybám).

Následuje výpis upraveného demonstračního příkladu:

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# aplikace rozmazáni pohybem
blurred_clip = clip.fx(supersample, 1, 5)
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
blurred_clip.write_videofile('12_motion_blur.ogv', bitrate='600000')

15. Jednoduchá kompozice typu „obraz v obraze“

Velmi zajímavých efektů lze dosáhnout kompozicí většího množství zdrojových videí do jediného videa výstupního. Možností, jak vstupní videa zkombinovat, existuje hned několik. V této kapitole si ukážeme kompozici typu „obraz v obraze“, kdy se ve výstupním videu vytvoří matice, přičemž se do buněk této matice ukládají vstupní videa:

# vytvoření pole 2×2 s video klipy
final_clip = clips_array([[clip1, clip2],
                          [clip3, clip4]])

Samozřejmě nejsme omezeni pouze maticí 2×2 video klipy, ale lze použít libovolně velkou matici. Některé buňky mohou být zaplněny statickým obrázkem typu ImageClip atd. V následujícím příkladu se vytvoří matice 2×2 video klipy obsahující stejná videa. Povšimněte si, že výsledné video má rozlišení snímků 1280×720 pixelů, a to z toho důvodu, že vstupní videa mají rozlišení 640×360 pixelů:

https://tisnik.github.io/moviepy-videos/videoprocessing13_clip_a­rray.htm

Zdrojový kód příkladu, který vytvoří matici 2×2 videoklipů, vypadá následovně:

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.editor import clips_array
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# vytvoření pole 2×2 s video klipy
final_clip = clips_array([[clip, clip],
                          [clip, clip]])
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
final_clip.write_videofile('13_clip_array.ogv', bitrate='600000')

16. Vytvoření jednoduché pipeline s uložením výsledku ve formě matice čtyř videoklipů

Nyní si ukažme, jakým způsobem lze zkonstruovat různé „pipeline“, které na videa aplikují větší množství filtrů, spojují videa dohromady, například s využitím třídy CompositeVideoClip apod. Vzhledem k tomu, že výsledkem aplikace filtru na vstupní video je vždy nové video představované instancí potomka třídy VideoClip, můžeme na video aplikovat více filtrů za sebou:

clip = VideoFileClip('input_video.mp4', audio=False)
 
blurred = clip.fx(supersample, 0.25, 10)
 
faster = blurred.speedx(1.5)
 
sub_clip = faster.subclip(5.0, 10.0)
 
colorized = subclip.fx(colorx, 2.50)
 
with_margin = colorized.margin(20)

Samozřejmě je možné vše zapsat na jediný řádek:

clip = VideoFileClip('input_video.mp4', audio=False).fx(supersample, 0.25, 10).speedx(1.5).subclip(5.0, 10.0).fx(colorx, 2.50).margin(20)

Větší množství pipeline se sloučí do matice m×n videí například takto:

# vytvoření pole 2×2 s video klipy
final_clip = clips_array([[clip, clip2],
                          [clip3, clip4]])

Popř. je možné videoklipy spojit za sebe:

clips = [clip1, clip2, clip3, clip4]
 
concat_clip = concatenate_videoclips(clips, method="compose")
Poznámka: každý další filtr přidaný do pipeline samozřejmě prodlouží celé zpracování, a to mnohdy dosti výrazným způsobem (motion blur).

V dalším příkladu vytvoříme následující video ukazující jak původní (zdrojový) videoklip, tak i jeho tři modifikace:

https://tisnik.github.io/moviepy-videos/videoprocessing14_clip_a­rray.htm

Opět si ukažme, jak vypadá úplný zdrojový kód tohoto demonstračního příkladu:

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.editor import clips_array
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False)
 
# načtení masky
mask = ImageClip('mask.png', ismask=True)
 
# aplikace masky
clip2 = CompositeVideoClip([clip.set_mask(mask)])
 
# aplikace rozmazání pohybem
clip3 = clip.fx(supersample, 0.25, 10)
 
# aplikace rozmazání pohybem
clip4 = clip.fx(supersample, 1, 5)
 
# vytvoření pole 2×2 s video klipy
final_clip = clips_array([[clip, clip2],
                          [clip3, clip4]])
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
final_clip.write_videofile('14_clip_array.ogv', bitrate='800000')

Filtr je samozřejmě možné aplikovat již na vstupní video, což je ukázáno na předposledním příkladu, v němž se okolo jednotlivých částí vytvořil desetipixelový okraj:

https://tisnik.github.io/moviepy-videos/videoprocessing15_clip_wit­h_margin.htm

Zdrojový kód tohoto příkladu vypadá následovně:

from moviepy.editor import ImageClip, VideoFileClip, CompositeVideoClip
from moviepy.editor import clips_array
from moviepy.video.fx.all import *
 
# načtení video klipu uloženého v kontejneru MPEG-4
clip = VideoFileClip('input_video.mp4', audio=False).margin(10)
 
# načtení masky
mask = ImageClip('mask.png', ismask=True)
 
# aplikace masky
clip2 = CompositeVideoClip([clip.set_mask(mask)])
 
# aplikace rozmazání pohybem
clip3 = clip.fx(supersample, 0.25, 10)
 
# aplikace rozmazání pohybem
clip4 = clip.fx(supersample, 1, 5)
 
# vytvoření pole 2×2 s video klipy
final_clip = clips_array([[clip, clip2],
                          [clip3, clip4]])
 
# uložení video klipu do jiného souboru ve formátu Ogg/Theora
final_clip.write_videofile('15_clip_array_with_margin.ogv', bitrate='800000')

17. Vytvoření videa z jednotlivých snímků získaných z rastrových obrázků

V závěrečné části dnešního článku si ukážeme skript sloužící k vytvoření videa ze snímků, které jsou načteny z rastrových obrázků (zde konkrétně z obrázků uložených ve formátu PNG). Tento příklad bude sloužit jako základ pro příklady popsané příště, ve kterých se budeme zabývat tvorbou vizualizací, programováním vlastních filtrů a částečně i analýzou obrazu.

Ve skriptu se nejdříve získá seznam všech souborů s koncovkou „.png“, tento seznam se setřídí, z každého obrázku se vytvoří samostatný (jednosnímkový) videoklip představovaný instancí třídy ImageClip a na závěr se tyto krátké videoklipy spojí dohromady funkcí concatenate_videoclips, která akceptuje seznam videoklipů a nepovinně taktéž určení, jakým způsobem se tyto videoklipy mají spojit. V případě potřeby je samozřejmě možné do celého zpracování přidat nějaký filtr, například pro prolínání obrázků apod.:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import os
import glob
from moviepy.editor import ImageClip, concatenate_videoclips
 
fps = 10
frame_duration = 2
 
# ziskani serazeneho seznamu souboru *.png
base_dir = os.path.realpath(".")
file_list = glob.glob('*.png')
file_list_sorted = sorted(file_list)
 
# vytvoreni sady objektu typu ImageClip
clips = [ImageClip(filename).set_duration(frame_duration)
         for filename in file_list_sorted]
 
# kompozice vsech kratkych video klipu
concat_clip = concatenate_videoclips(clips, method="compose")
 
# ulozeni vysledneho video klipu do souboru ve formatu Ogg/Theora
concat_clip.write_videofile("test.ogv", fps=fps)
Poznámka: tento skript je omezen celkovým počtem souborů, které může uživatel současně otevřít (typicky 1024). Pokud se vám zobrazí chybové hlášení „too many files open“, bude zapotřebí zvětšit limit příkazem ulimit -n (s příslušnými právy).

18. Repositáře s demonstračními příklady a vygenerovanými videi

Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/moviepy-examples (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, stále doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Demonstrační příklad Popis Cesta
1 01_convert_mp4_to_ogv.py konverze mezi MPEG-4 a Ogg/Theora https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/01_convert_mp4_to_og­v.py
2 02_convert_change_bitrate.py konverze se změnou bitové rychlosti https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/02_convert_chan­ge_bitrate.py
3 03_convert_speedup.py změna rychlosti přehrávání videa https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/03_convert_spe­edup.py
4 04_use_subclip.py klasický střih videa https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/04_use_subclip­.py
5 05_resize_to_larger.py změna velikosti snímků (zde jejich zvětšení) https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/05_resize_to_lar­ger.py
6 06_resize_to_smaller.py změna velikosti snímků (zde jejich zmenšení) https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/06_resize_to_sma­ller.py
7 07_colorize.py pokus o jednoduchou „kolorizaci“ videa https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/07_colorize.py
8 08_margin.py přidání okraje do videa https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/08_margin.py
9 09_masking.py základní použití masky https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/09_masking.py
10 10_inverse_video.py inverze/negace všech snímků https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/10_inverse_vi­deo.py
11 11_motion_blur.py rozmazání pohybem https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/11_motion_blur­.py
12 12_motion_blur_B.py rozmazání pohybem https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/12_motion_blur_B­.py
13 13_clip_array.py matice 2×2 videa https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/13_clip_arra­y.py
14 14_clip_array_various_clips.py úprava předchozího příkladu https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/14_clip_arra­y_various_clips.py
15 15_clip_array_various_clip­s_with_margin.py další možnosti pipeline https://github.com/tisnik/moviepy-examples/blob/master/vide­o_processing/15_clip_arra­y_various_clips_with_margin­.py
       
16 01_maps tvorba videa z jednotlivých snímků https://github.com/tisnik/moviepy-examples/blob/master/visu­alization/01_maps.py

Druhý repositář obsahuje všechna videa uložená do formátu Ogg/Vorbis, která byla vygenerována demonstračními příklady. Odkazy na jednotlivá videa jsou uvedeny v následující tabulce:

# Video k příkladu Cesta
1 01_convert_mp4_to_ogv.py https://tisnik.github.io/moviepy-videos/videoprocessing01_con­vert_mp4_to_ogv.htm
2 02_convert_change_bitrate.py https://tisnik.github.io/moviepy-videos/videoprocessing02_con­vert_change_bitrate.htm
3 03_convert_speedup.py https://tisnik.github.io/moviepy-videos/videoprocessing03_con­vert_speedup.htm
4 04_use_subclip.py https://tisnik.github.io/moviepy-videos/videoprocessing04_u­se_subclip.htm
5 05_resize_to_larger.py https://tisnik.github.io/moviepy-videos/videoprocessing05_re­size_to_larger.htm
6 06_resize_to_smaller.py https://tisnik.github.io/moviepy-videos/videoprocessing06_re­size_to_smaller.htm
7 07_colorize.py https://tisnik.github.io/moviepy-videos/videoprocessing07_co­lorize.htm
8 08_margin.py https://tisnik.github.io/moviepy-videos/videoprocessing08_margin.htm
9 09_masking.py https://tisnik.github.io/moviepy-videos/videoprocessing09_masking.htm
10 10_inverse_video.py https://tisnik.github.io/moviepy-videos/videoprocessing10_in­verse_video.htm
11 11_motion_blur.py https://tisnik.github.io/moviepy-videos/videoprocessing11_mo­tion_blur.htm
12 12_motion_blur_B.py https://tisnik.github.io/moviepy-videos/videoprocessing12_mo­tion_blur_B.htm
13 13_clip_array.py https://tisnik.github.io/moviepy-videos/videoprocessing13_clip_a­rray.htm
14 14_clip_array_various_clips.py https://tisnik.github.io/moviepy-videos/videoprocessing14_clip_a­rray.htm
15 15_clip_array_various_clip­s_with_margin.py https://tisnik.github.io/moviepy-videos/videoprocessing15_clip_wit­h_margin.htm

19. Odkazy na Internetu

  1. MoviePy 0.2.3.3 na PyPi
    https://pypi.org/project/moviepy/
  2. MoviePy na GitHubu
    https://github.com/Zulko/moviepy
  3. MoviePy – dokumentace
    http://zulko.github.io/moviepy/
  4. MoviePy – galerie
    http://zulko.github.io/mo­viepy/gallery.html
  5. Data Animations With Python and MoviePy
    https://zulko.github.io/blog/2014/11/29/da­ta-animations-with-python-and-moviepy/
  6. Porovnání formátů Ogg Theora a H.264
    https://www.root.cz/zpravic­ky/porovnani-formatu-ogg-theora-a-h-264/
  7. Případ GIF
    https://www.root.cz/clanky/pripad-gif/
  8. Pravda a mýty o GIFu
    https://www.root.cz/clanky/pravda-a-myty-o-gifu/
  9. Anatomie grafického formátu GIF
    https://www.root.cz/clanky/anatomie-grafickeho-formatu-gif/
  10. GIF: animace a konkurence
    https://www.root.cz/clanky/gif-animace-a-konkurence/
  11. Two python modules : MoviePy and images2gif – part 001
    http://free-tutorials.org/two-python-modules-moviepy-and-images2gif-part-001/
  12. images2gif
    https://pypi.org/project/images2gif/
  13. Making GIFs from video files with Python
    https://www.devbattles.com/en/san­d/post-345-Making+GIFs+From+Video+Fi­les+With+Python
  14. GIF89a specification
    https://www.w3.org/Graphics/GIF/spec-gif89a.txt
  15. MPEG-4 Part 14
    https://en.wikipedia.org/wiki/MPEG-4_Part14
  16. Theora video compression
    https://www.theora.org/
  17. Theora
    https://en.wikipedia.org/wiki/Theora
  18. NumPy
    http://www.numpy.org/
  19. numpy 1.14.2 (on PyPi)
    https://pypi.org/project/numpy/
  20. Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
    https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/
  21. Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
    https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/
  22. Non-linear editing system
    https://en.wikipedia.org/wiki/Non-linear_editing_system
  23. Popis barvových map modulu matplotlib.cm
    https://gist.github.com/en­dolith/2719900#id7
  24. Ukázky (palety) barvových map modulu matplotlib.cm
    http://matplotlib.org/exam­ples/color/colormaps_refe­rence.html
  25. Customising contour plots in matplotlib
    https://philbull.wordpres­s.com/2012/12/27/customising-contour-plots-in-matplotlib/
  26. NumPy Home Page
    http://www.numpy.org/
  27. NumPy v1.10 Manual
    http://docs.scipy.org/doc/num­py/index.html
  28. NumPy (Wikipedia)
    https://en.wikipedia.org/wiki/NumPy
  29. Monitoring sucha
    http://portal.chmi.cz/aktualni-situace/sucho
Našli jste v článku chybu?