Obsah
1. Programová tvorba a nelineární editace videa s využitím knihovny MoviePy
3. Doinstalování dalších knihoven z interaktivní smyčky Pythonu
4. První příklad – vytvoření videa obsahujícího sérii tmavých snímků
5. Vytvoření animovaného GIFu se stejným obsahem
6. Editace videa na úrovni jednotlivých snímků a pixelů
7. Konstruktory datové struktury ndarray
8. Přístup k prvkům polí, využití takzvaných řezů polí
9. Základní operace s datovou strukturou ndarray
10. Druhý příklad – pohybující se úsečka v desetisekundovém videu
12. Třetí příklad – vykreslení všech 16 milionů barev v desetisekundovém videu
14. Parametry předávané videokodeku
15. Čtvrtý příklad – animace „průletu“ Mandelbrotovou množinou
16. Přepočet parametrů obrázku v průběhu animace a výpočet snímku Mandelbrotovy množiny
17. Pomocný modul s barvovou paletou
18. Výsledek vytvořený čtvrtým příkladem
19. Repositář s demonstračními příklady
1. Programová tvorba a nelineární editace videa s využitím knihovny MoviePy
V některých případech se dostaneme do situace, kdy je nutné například při prezentaci projektu či při vizualizaci dat vytvořit soubory s videem, a to programově. Například se může jednat o animovaný průběh nějaké vícerozměrné veličiny, výstup ze simulace apod. Dnes již existuje poměrně velké množství nástrojů, které lze pro tuto činnost použít; ovšem vývojáři používající programovací jazyk Python pravděpodobně v prvé řadě použijí knihovnu nazvanou jednoduše a přímočaře MoviePy. Dnes si ukážeme jen základní možnosti této knihovny, ovšem příště se budeme zabývat i pokročilejšími tématy, například propojením MovipePy s knihovnou Matplotlib, tvorbou video efektů atd.
2. Instalace knihovny MoviePy
Knihovnu MoviePy si nejdříve nainstalujeme, a to klasicky s využitím nástroje pip3 (nebo pip), protože tato knihovna je samozřejmě registrována i na PyPI (Python Package Index). Pro jednoduchost provedeme instalaci jen pro právě aktivního uživatele:
$ pip3 install --user moviepy
Samotná instalace může být provedena za relativně krátkou dobu (samozřejmě v závislosti na rychlosti připojení k Internetu):
Collecting moviepy Downloading https://files.pythonhosted.org/packages/ee/88/1b57f7318b3079b41a5b4299ae99caedcd00b5f9897dd765c2e553b694c3/moviepy-0.2.3.3.tar.gz (397kB) 100% |████████████████████████████████| 399kB 1.2MB/s Requirement already satisfied: decorator<5.0,>=4.0.2 in /usr/lib/python3.6/site-packages (from moviepy) Collecting imageio<3.0,>=2.1.2 (from moviepy) Downloading https://files.pythonhosted.org/packages/a7/1d/33c8686072148b3b0fcc12a2e0857dd8316b8ae20a0fa66c8d6a6d01c05c/imageio-2.3.0-py2.py3-none-any.whl (3.3MB) 100% |████████████████████████████████| 3.3MB 455kB/s Collecting tqdm<5.0,>=4.11.2 (from moviepy) Downloading https://files.pythonhosted.org/packages/78/bc/de067ab2d700b91717dc5459d86a1877e2df31abfb90ab01a5a5a5ce30b4/tqdm-4.23.0-py2.py3-none-any.whl (42kB) 100% |████████████████████████████████| 51kB 9.9MB/s Collecting numpy (from moviepy) Downloading https://files.pythonhosted.org/packages/6e/dc/92c0f670e7b986829fc92c4c0208edb9d72908149da38ecda50d816ea057/numpy-1.14.2-cp36-cp36m-manylinux1_x86_64.whl (12.2MB) 100% |████████████████████████████████| 12.2MB 137kB/s Requirement already satisfied: pillow in /usr/lib64/python3.6/site-packages (from imageio<3.0,>=2.1.2->moviepy) Requirement already satisfied: olefile in /usr/lib/python3.6/site-packages (from pillow->imageio<3.0,>=2.1.2->moviepy) Installing collected packages: numpy, imageio, tqdm, moviepy Running setup.py install for moviepy ... done Successfully installed imageio-2.3.0 moviepy-0.2.3.3 numpy-1.14.2 tqdm-4.23.0 You are using pip version 9.0.1, however version 10.0.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command.
3. Doinstalování dalších knihoven z interaktivní smyčky Pythonu
Ve druhém kroku provedeme doinstalování dalších požadovaných knihoven. Použijeme přitom samotný interpret Pythonu, tj. interaktivní smyčku REPL. Nejdříve interpret spustíme:
$ python3 Python 3.6.3 (default, Oct 9 2017, 12:11:29) [GCC 7.2.1 20170915 (Red Hat 7.2.1-2)] on linux Type "help", "copyright", "credits" or "license" for more information.
Následně spustíme jediný příkaz, který zajistí import třídy VideoClip:
>>> from moviepy.editor import VideoClip
Pokud v této chvíli nejsou některé potřebné moduly a kodeky nainstalovány, spustí se automatická (lokální) instalace, která může vypadat takto:
Imageio: 'ffmpeg-linux64-v3.3.1' was not found on your computer; downloading it now. Try 1. Download from https://github.com/imageio/imageio-binaries/raw/master/ffmpeg/ffmpeg-linux64-v3.3.1 (43.8 MB) Downloading: 45929032/45929032 bytes (100.0%) Done File saved as /home/tester/.imageio/ffmpeg/ffmpeg-linux64-v3.3.1.
4. První příklad – vytvoření videa obsahujícího sérii tmavých snímků
Tvorba videa sice vyžaduje alespoň základní znalost datové struktury ndarray nabízené knihovnou Numpy, ovšem již nyní si můžeme ukázat strukturu jednoduchého skriptu, kterým se nový soubor s videem vytvoří. Nejdříve si ukažme, jak vypadá celý zdrojový kód; v dalším textu si podrobněji popíšeme význam jednotlivých řádků:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from moviepy.editor import VideoClip import numpy WIDTH = 320 HEIGHT = 240 def make_frame(t): print("time: {t}".format(t=t)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) return frame # vytvoreni video klipu animation = VideoClip(make_frame, duration=10) # export videa do formatu MPEG-4 animation.write_videofile("dark_scene.mp4", fps=24) # export videa do formatu Ogg Video File animation.write_videofile("dark_scene.ogv", fps=24)
Na začátku skriptu pouze provedeme import třídy VideoClip nabízené knihovnou MoviePy a taktéž import základních funkcí nabízených knihovnou Numpy:
from moviepy.editor import VideoClip import numpy
Další dva řádky obsahují deklaraci konstant s rozlišením výsledného videa:
WIDTH = 320 HEIGHT = 240
Následuje velmi důležitá část skriptu. Jedná se o callback funkci volanou při vytváření jednotlivých video snímků (frame). Funkci je předán čas snímku a výsledkem by měla být datová struktura typu ndarray; konkrétně trojrozměrné pole o rozměrech výškaךířka×3, kde výška a šířka odpovídá rozlišení jednotlivých snímků a poslední dimenze je rovna třem z toho důvodu, že každý pixel je reprezentován trojicí barvových složek Red, Green, Blue. Náš první video klip bude sestávat pouze ze sady tmavých snímků, takže potřebné pole reprezentující snímek vytvoříme konstruktorem numpy.zeros, kterému předáme n-tici s tvarem pole (pozor – jde o n-tici, takže závorky jsou zdvojeny):
def make_frame(t): print("time: {t}".format(t=t)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) return frame
Dále vytvoříme objekt představující budoucí video klip. Konstruktoru předáme jak referenci na callback funkci, tak i délku klipu uvedenou v sekundách:
# vytvoreni video klipu animation = VideoClip(make_frame, duration=10)
Další dva řádky spustí generování videa s jeho postupným ukládáním na disk s využitím zvoleného kodeku. Typicky se dnes používá formát (kontejner) MPEG-4 s H.264 popř. kontejner Ogg s kodekem Theora. Můžeme si samozřejmě vyzkoušet obě možnosti. Povšimněte si, že se předává i požadovaný počet snímků za sekundu, který společně s výše specifikovanou délkou videa v sekundách určuje přibližný celkový počet snímků (přibližný proto, že fps nemusí být všemi kodeky dodrženo zcela přesně a taktéž kodeky vyžadují určité řazení mezisnímků):
# export videa do formatu MPEG-4 animation.write_videofile("dark_scene.mp4", fps=24) # export videa do formatu Ogg Video File animation.write_videofile("dark_scene.ogv", fps=24)
Co se vlastně stane při zavolání metody write_videofile?
- Postupně je volána callback funkce make_frame s předáním časového razítka
- Struktura ndarray vrácená touto funkcí je transformována a předána do video kodeku
- Ten postupně vytváří výsledné video – v paměti si přitom drží jen několik posledních snímků (kvůli predikci pohybu atd., to nás však v tuto chvíli nemusí trápit)
Výsledná videa jsou dosti nudná, nicméně by měla jít přehrát:
https://tisnik.github.io/moviepy-videos/video1.htm
5. Vytvoření animovaného GIFu se stejným obsahem
Ve skutečnosti knihovna MoviePy dokáže vytvořit animaci i v dalších formátech, nejenom s využitím video kodeků. Jednou z možností je série statických snímků a taktéž – což je asi praktičtější – vytvoření animovaného GIFu. Pro některé účely se může jednat o ideální výstupní formát, ovšem musíme znát i některá jeho omezení.

Obrázek 1: GIF s deseti rámci zobrazovanými jako animace (kromě toho je v souboru přítomen i rozšiřující textový blok s poznámkou a programu, který GIF vytvořil; všimněte si potenciálně citlivých informací).
Ke stále trvající popularitě grafického formátu GIF nepochybně patří i jeho schopnost zaznamenat jednoduché animace. Pokud se podíváme do specifikace GIFu 89a, zjistíme, že se do rozšiřujícího grafického řídicího bloku (GCE – Graphic Control Extension) mohou zapsat i informace o délce prodlevy před zobrazením dalšího rámce. Tato délka je zadávána v setinách sekundy, a vzhledem k tomu, že je údaj zapsaný ve dvou bytech, je minimální prodleva rovna 1/100 s a maximální prodleva 655,36 s, což je zhruba 11 minut.
Ovšem samotná prodleva mezi zobrazením jednotlivých rámců postačuje pouze pro tvorbu animace, která proběhne jednou a poté se již neopakuje – ve specifikacích se nic o smyčce v animaci nemluví. Zde však přichází na řadu další užitečná vlastnost GIFů, která spočívá v možnosti zápisu přidaných informací od „třetích stran“. Prohlížeče buď těmto přidaným informacím rozumí (a zpracují je), nebo je ignorují.
To je ve formátu GIF zajištěno tím způsobem, že každá přidaná informace obsahuje hlavičku s její identifikací a délkou. V případě animací se jedná o rozšíření zavedené firmou Netscape pro její (kdysi) slavný webový prohlížeč Netscape Navigator. V tomto rozšíření je vlastně pouze specifikováno, kolikrát se má animace opakovat a popř. zda se má opakovat neustále (v prohlížečích bylo většinou možné animaci GIFů zastavit klávesou Esc).
Pokud budete chtít vytvořit nikoli video klip, ale animaci uloženou do formátu GIF, změní se kód skriptu následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from moviepy.editor import VideoClip import numpy WIDTH = 320 HEIGHT = 240 def make_frame(t): print("time: {t}".format(t=t)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) return frame # vytvoreni video klipu animation = VideoClip(make_frame, duration=10) # export videa do formatu GIF animation.write_gif("dark_scene.gif", fps=24)
6. Editace videa na úrovni jednotlivých snímků a pixelů
V prvním příkladu jsme každý snímek vytvořili s využitím konstruktoru numpy.zeros. Jaký objekt se však tímto konstruktorem vytvořil? Jedná se o trojrozměrné pole typu ndarray, které tvoří základní datovou strukturu známé knihovny Numpy. Tato datová struktura je v mnoha ohledech dosti zajímavá, takže se s ní seznámíme podrobněji. Je to nutné, protože právě manipulace s trojrozměrnými poli nám umožňuje editaci videa na té nejnižší rozumné úrovni abstrakce – jednotlivých pixelech reprezentovaných trojicemi hodnot R, G a B.
Funkce a objekty nabízené knihovnou Numpy se sice volají přímo z Pythonu, ve skutečnosti se však interní datové struktury dosti podstatným způsobem odlišují od datových struktur využívaných samotným Pythonem. V knihovně Numpy tvoří základ datová struktura nazvaná ndarray, která reprezentuje pole o prakticky libovolném počtu dimenzí (ostatně „nd“ ve jménu „ndarray“ značí N-dimensional). Tato pole se liší od běžných seznamů či n-tic v Pythonu, protože ndarray jsou homogenní datovou strukturou: všechny prvky totiž mají shodný typ a navíc všechny prvky leží za sebou, zatímco seznamy v Pythonu jsou měnitelné (prvky lze přidávat a odebírat) a obecně nehomogenní (každý prvek může mít odlišný datový typ). Za tuto velkou flexibilitu se samozřejmě platí, a to jak většími nároky na operační paměť (reference na objekty), tak i pomalejším zpracováním.
Při vytváření polí typu ndarray, ať již se jedná o vektory, matice či o pole s větším množstvím dimenzí, lze specifikovat datový typ všech prvků a dokonce i uspořádání prvků v paměti (buď podle zvyklostí jazyka Fortran nebo jazyka C). Podívejme se nyní na tabulku, v níž jsou vypsány možné typy prvků polí ndarray:
# | Typ | Formát | Rozsah | Jednoznakový kód |
---|---|---|---|---|
1 | bool | uloženo po bajtech | True/False | ‚?‘ |
2 | int8 | celočíselný se znaménkem | –128..127 | ‚b‘ |
3 | int16 | celočíselný se znaménkem | –32768..32767 | ‚h‘ |
4 | int32 | celočíselný se znaménkem | –2147483648..2147483647 | ‚i‘ |
5 | int64 | celočíselný se znaménkem | –9223372036854775808..9223372036854775807 | ‚l‘ |
6 | uint8 | celočíselný bez znaménka | 0..255 | ‚B‘ |
7 | uint16 | celočíselný bez znaménka | 0..65535 | ‚H‘ |
8 | uint32 | celočíselný bez znaménka | 0..4294967295 | ‚I‘ |
9 | uint64 | celočíselný bez znaménka | 0..18446744073709551615 | ‚L‘ |
10 | float16 | plovoucí řádová čárka | poloviční přesnost (half) | ‚e‘ |
11 | float32 | plovoucí řádová čárka | jednoduchá přesnost (single) | ‚f‘ |
12 | float64 | plovoucí řádová čárka | dvojitá přesnost (double) | ‚d‘ |
13 | complex64 | komplexní číslo (dvojice) | 2×float32 | ‚F‘ |
14 | complex128 | komplexní číslo (dvojice) | 2×float64 | ‚D‘ |
7. Konstruktory datové struktury ndarray
Pole typu ndarray je možné vytvořit několika různými způsoby. Základní funkcí sloužící k převodu různých typů Pythonovských objektů na pole je funkce nazvaná jednoduše numpy.array. Této funkci se předá objekt (což je typicky seznam, n-tice či objekt typu range) a popř. i další nepovinné parametry, které určují typ prvků nově vzniklého pole (dtype), zda se má provést kopie prvků (copy, většinou ano) či způsob uspořádání prvků v poli (order). Hlavička této funkce tedy vypadá následovně:
array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)
Nepovinný parametr order může nabývat hodnot:
Order | Význam |
---|---|
‚C‘ | prvky jsou uspořádány stejně jako v jazyku C |
‚F‘ | prvky jsou uspořádány stejně jako v jazyku Fortran |
‚A‘ | ponecháme na implementaci, který způsob uspořádání zvolit |
Jaký je tedy vlastně rozdíl mezi uspořádáním prvků podle ‚C‘ a ‚F‘? Předpokládejme klasickou matici o rozměrech 3×3 prvky:
| 1 2 3 | | 4 5 6 | | 7 8 9 |
Tato matice může být v operační paměti uložena následujícím způsobem:
1 2 3 4 5 6 7 8 9 - 'C'
Alternativně je však možné prohodit řádky a sloupce (což více odpovídá matematickému zápisu matice):
1 4 7 2 5 8 3 6 9 - 'F'
Podívejme se nyní na několik praktických použití funkce numpy.array. Všechny příkazy je možné zadat do interaktivní konzole Pythonu, použít IPython, Jupyter atd.:
# vytvoření pole ze seznamu numpy.array([1,2,3,4]) array([1, 2, 3, 4]) # vytvoření pole z typu 'range' numpy.array(range(10)) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) # explicitní specifikace typu všech prvků pole numpy.array(range(10), dtype=numpy.float) array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) # explicitní specifikace uspořádání prvků pole # (nemá velký význam pro 1D pole=vektory) numpy.array(range(10), order='C') array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) # explicitní specifikace uspořádání prvků pole # (nemá velký význam pro 1D pole=vektory) numpy.array(range(10), order='F') array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) # vytvoření dvourozměrné matice numpy.array([[1,2,3],[4,5,6]]) array([[1, 2, 3], [4, 5, 6]])
Kromě funkce numpy.array se poměrně často setkáme s nutností vytvořit vektor či matici s nulovými prvky. V tomto případě samozřejmě není nutné složitě vytvářet a předávat takové pole do funkce numpy.array, ale lze namísto toho využít funkci nazvanou numpy.zeros, což je rychlejší a současně i méně paměťově náročnější. Této funkci se předá n-tice (musí se skutečně jednat o n-tici, nikoli o seznam parametrů, proto nezapomeňte na závorky) specifikující dimenzi vektoru, matice či N-dimenzionálního pole:
zeros(shape, dtype=float, order='C')
Nepovinný parametr dtype se nastavuje buď na typ nebo na jednoznakový kód uvedený v tabulce v předchozí kapitole.
Podívejme se nyní na způsob použití této funkce:
# jednorozměrný vektor s jediným prvkem numpy.zeros(1) array([ 0.]) # jednorozměrný vektor s deseti prvky numpy.zeros(10) array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) # matice o velikosti 5x5 prvků, každý prvek je typu float numpy.zeros((5,5)) array([[ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.]]) # matice o velikosti 5x5 prvků, každý prvek je typu int numpy.zeros((5,5),dtype=int) array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]) # použití komplexních čísel numpy.zeros((2,2),dtype=numpy.complex) array([[ 0.+0.j, 0.+0.j], [ 0.+0.j, 0.+0.j]])
Dalším velmi často používaným typem vektoru či matice je taková struktura, jejíž všechny prvky mají hodnotu 1. Takový vektor popř. matice je možné vytvořit funkcí numpy.ones:
numpy.ones(shape, dtype=None, order='C')
Nepovinný parametr dtype se, podobně jako u předchozí funkce, nastavuje buď na typ nebo na jednoznakový kód uvedený v tabulce v předchozí kapitole.
Opět se podívejme na několik příkladů použití této funkce v praxi:
# jednorozměrný vektor s deseti prvky numpy.ones(10) array([ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]) # matice se třemi řádky a čtyřmi sloupci numpy.ones((3,4)) array([[ 1., 1., 1., 1.], [ 1., 1., 1., 1.], [ 1., 1., 1., 1.]]) # matice se třemi řádky a čtyřmi sloupci # s explicitní specifikací typu prvků numpy.ones((3,4), dtype=int) array([[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]) # trojrozměrné pole s prvky typu int numpy.ones((3,4,5), dtype=int) array([[[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]]) # trojrozměrné pole s prvky typu int # (oproti předchozímu příkladu se velikosti v jednotlivých dimenzích liší) numpy.ones((5,4,3), dtype=int) array([[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]]) # zde může být použití typu komplexní číslo možná poněkud překvapující, # ovšem stále platí, že 1=1+0j numpy.ones((3,2),dtype=numpy.complex) array([[ 1.+0.j, 1.+0.j], [ 1.+0.j, 1.+0.j], [ 1.+0.j, 1.+0.j]])
Další funkcí určenou pro konstrukci vektoru je funkce pojmenovaná numpy.arange, přičemž poněkud zavádějící název „arange“ vznikl složením slov „array“ a „range“. Této funkci se předávají parametry s podobným významem, jaký mají u funkce xrange (Python 2.x) či range (Python 3.x), samozřejmě s tím rozdílem, že návratovou hodnotou funkce numpy.arange se skutečný vektor typu ndarray. Podívejme se na několik možností použití této poměrně nenápadné, ale o to užitečnější funkce:
# při použití jednoho parametru má tento parametr význam hodnoty "stop" # vytvoří se vektor s prvky od 0 do "stop" (kromě) numpy.arange(10) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) # specifikace hodnot "start" (včetně) a "stop" (kromě) numpy.arange(10, 20) array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) # třetí nepovinný parametr určuje krok použitý při generování prvků vektoru numpy.arange(10, 20, 2) array([10, 12, 14, 16, 18]) # krok může být samozřejmě záporný numpy.arange(20, 10, -2) array([20, 18, 16, 14, 12]) # nemusíme zůstat pouze u celých čísel, protože pracovat je možné i s hodnotami # typu float a complex numpy.arange(0,5, 0.1) array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3. , 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4. , 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9]) # použití komplexních konstant numpy.arange(0+0j, 10+10j, 2+0j) array([ 0.+0.j, 2.+0.j, 4.+0.j, 6.+0.j, 8.+0.j])
Další velmi důležitou funkcí, s níž se v praxi často setkáme, je funkce nazvaná numpy.reshape, která dokáže změnit velikost matice a vhodným způsobem přeorganizovat prvky v původní matici. Této funkci se předávají dva parametry – prvním parametrem je vstupní pole (vektor, matice, …), druhým parametrem pak specifikace tvaru výsledného pole, přičemž tvar je reprezentován n-ticí, ve které jsou uloženy velikosti pole v jednotlivých dimenzích. Podívejme se na několik příkladů:
# běžná matice se dvěma řádky a třemi sloupci b=numpy.array([[1,2,3],[4,5,6]]) # změna tvaru matice na 3x2 prvky numpy.reshape(b,(3,2)) array([[1, 2], [3, 4], [5, 6]]) # zde vlastně dostaneme původní matici numpy.reshape(b,(2,3)) array([[1, 2, 3], [4, 5, 6]]) # vytvoření matice s jediným řádkem numpy.reshape(b,(1,6)) array([[1, 2, 3, 4, 5, 6]]) # vytvoření matice s jediným sloupcem numpy.reshape(b,(6,1)) array([[1], [2], [3], [4], [5], [6]])
8. Přístup k prvkům polí, využití takzvaných řezů polí
Jakým způsobem se pole s využitím knihovny Numpy vytváří již víme. Ještě si však musíme říct, jak se prvky uložené v polích vybírají neboli indexují. V případě jednorozměrných polí je to ve skutečnosti velmi jednoduché – prvky jsou totiž číslovány od nuly a díky přetížení operátoru [] (operátor indexování) je možné prvky v případě potřeby indexovat i od konce pole. V tomto případě se musí použít záporné číslo, takže a[1] značí druhý prvek pole zatímco a[-1] první prvek od konce:
a=numpy.arange(12) a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) a[0] 0 a[5] 5 a[-1] 11 a[-5] 7
U dvourozměrných či vícerozměrných polí je situace poněkud komplikovanější, neboť v tomto případě je nutné použít dva či větší počet indexů (jeden index pro každou dimenzi). Vzhledem k tomu, že v různých programovacích jazycích a rozličných specializovaných nástrojích typu R či Matlab, se používají odlišné způsoby zápisu více indexů, podporuje knihovna Numpy dva způsoby zápisu:
- buď se všechny indexy oddělí čárkou a vloží se do jediného bloku omezeného hranatými závorkami []
- nebo se alternativně pro každou dimenzi použije zvláštní hranatá závorka (syntaxe odvozená od Céčka)
Podívejme se na příklady:
import numpy m=numpy.reshape(numpy.arange(12), (3,4)) m array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) m[0] array([0, 1, 2, 3]) m[0][2] 2 m[0,2] 2
V mnoha případech je nutné z polí získat hodnoty většího množství prvků tvořících souvislý blok. Může se například jednat o všechny prvky pole kromě prvku prvního a posledního (typické pro některé filtry), prvky z první poloviny pole atd. I v tomto případě knihovna Numpy nabízí vývojářům velmi elegantní řešení, a to ve formě takzvaných řezů (slices). Namísto jediného indexu je totiž možné zadat dva indexy oddělené dvojtečkou, které potom reprezentují začátek a konec řezu. Opět se podívejme na demonstrační příklad:
a=numpy.arange(12) a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) a[3:7] array([3, 4, 5, 6])
Pokud se vynechá první index, automaticky se za něj dosadí 0, pokud se vynechá index druhý, dosadí se za něj velikost dimenze pole-1. Vynechat je možné i oba indexy; v tomto případě je řezem původní pole (tento zápis je sice možný, ale poněkud postrádá smysl):
a=numpy.arange(12) a[:7] array([0, 1, 2, 3, 4, 5, 6]) a[5:] array([ 5, 6, 7, 8, 9, 10, 11]) a[:] array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Někdy může být řez polem prázdný:
a=numpy.arange(12) a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) a[-4:-6] array([], dtype=int64)
Použít je možné i záporné indexy popř. první či druhý index zcela vynechat:
a[-6:-4] array([6, 7]) a[-6:] array([ 6, 7, 8, 9, 10, 11]) a[:-4] array([0, 1, 2, 3, 4, 5, 6, 7])
Řezy je možné provádět i u dvourozměrných či vícerozměrných polí. V tomto případě se zkombinuje již popsaný zápis s dvojtečkou:
import numpy m=numpy.reshape(numpy.arange(25), (5,5)) m array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]]) m[2:4] array([[10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]) m[2:4,3] array([13, 18]) m[2:4,3:5] array([[13, 14], [18, 19]]) m[1:4,1:4] array([[ 6, 7, 8], [11, 12, 13], [16, 17, 18]]) m[-4:-2,-4:-2] array([[ 6, 7], [11, 12]])
9. Základní operace s datovou strukturou ndarray
V případě importu knihovny Numpy dojde k přetížení mnoha běžných operátorů. Jedná se zejména o relační operátory, tj. o takové operátory, které slouží k porovnání dvou hodnot. Ve svém původním významu tyto operátory vrací jedinou pravdivostní hodnotu True nebo False. Ovšem pokud se relační operátory použijí ve své přetížené variantě pro porovnání polí (vektorů, matic), je výsledkem opět pole, ovšem pouze s hodnotami True a False vzniklými porovnáním prvků polí se stejným indexem. Při porovnávání musí mít obě pole stejný tvar, což je kontrolováno za běhu aplikace:
a=numpy.arange(1,11) b=numpy.array([100,0,100,0,100,0,100,0,100,0]) a array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) b array([100, 0, 100, 0, 100, 0, 100, 0, 100, 0]) a==b array([False, False, False, False, False, False, False, False, False, False], dtype=bool) a!=b array([ True, True, True, True, True, True, True, True, True, True], dtype=bool) a<b array([ True, False, True, False, True, False, True, False, True, False], dtype=bool)
Relační operátory je možné použít i tak, že jedním z operandů je pole a druhým operandem je skalární hodnota. Výsledkem takového porovnání je opět pole, tentokrát vytvořené porovnáním každého prvku zdrojového pole s jedinou skalární hodnotou:
a=numpy.arange(12) a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) a==5 array([False, False, False, False, True, False, False, False, False, False], dtype=bool) a<6 array([ True, True, True, True, True, True, False, False, False, False, False, False], dtype=bool)
Podobným způsobem můžeme vytvořit „Booleovská“ dvourozměrná pole:
m=numpy.arange(24) m array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]) x=numpy.reshape(m, (6,4), order='F') x>10 array([[False, False, True, True], [False, False, True, True], [False, False, True, True], [False, False, True, True], [False, False, True, True], [False, True, True, True]], dtype=bool) x%2==1 array([[False, False, False, False], [ True, True, True, True], [False, False, False, False], [ True, True, True, True], [False, False, False, False], [ True, True, True, True]], dtype=bool)
10. Druhý příklad – pohybující se úsečka v desetisekundovém videu
Nyní již máme k dispozici všechny informace, které je možné použít pro tvorbu skutečného programově generovaného videa. Ve druhém příkladu upravíme callback funkci make_frame takovým způsobem, aby se do snímků vkládala vodorovná bílá úsečka. Na každém dalším snímku bude úsečka posunuta o jeden obrazový řádek níže, takže výsledkem bude (prozatím velmi primitivní) animace postupně klesající bílé čáry. Počet řádků výsledného videa je roven 240, což velmi dobře koresponduje s desetisekundovým trváním videa a 24 snímky za sekundu (úsečka by tedy za oněch deset sekund měla proběhnout přes celý snímek). Opět si nejprve ukažme celý zdrojový kód:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from moviepy.editor import VideoClip import numpy WIDTH = 320 HEIGHT = 240 line = 0 def make_frame(t): global line print("time: {t}, line: {l}".format(t=t, l=line)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) # vykresleni jedine vodorovne usecky if line < HEIGHT: frame[line].fill(255) line += 1 return frame # vytvoreni video klipu animation = VideoClip(make_frame, duration=10) # export videa do formatu MPEG-4 animation.write_videofile("line.mp4", fps=24) # znovunastaveni pocitadla line = 0 # export videa do formatu Ogg Video File animation.write_videofile("line.ogv", fps=24) # znovunastaveni pocitadla line = 0 # export videa do formatu GIF animation.write_gif("line.gif", fps=24)
Tento kód je již nepatrně složitější, a to proto, že musíme mít pomocné počitadlo s vertikální pozicí úsečky. Počitadlo se s každým snímkem zvětšuje o jedničku:
line = 0 def make_frame(t): global line print("time: {t}, line: {l}".format(t=t, l=line)) ... ... ... line += 1 return frame
Zajímavý je i způsob vykreslení úsečky. Využíváme zde totiž „array slicing“ neboli přístup přes řez pole vysvětlený v předchozích kapitolách. Následující příkaz:
frame[line].fill(255)
Nejdříve z trojrozměrného pole vybere dvourozměrnou matici šířka×3 a posléze tuto matici vyplní hodnotami 255. Ostatně si to můžeme vyzkoušet i interaktivně na mnohem menší matici:
>>> import numpy >>> frame=numpy.zeros((5,4,3)) >>> frame array([[[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]]]) >>> frame[3].fill(255) >>> frame array([[[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]], [[ 255., 255., 255.], [ 255., 255., 255.], [ 255., 255., 255.], [ 255., 255., 255.]], [[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]]])
11. Výsledky druhého příkladu
Opět se podívejme na výsledná videa vytvořená dnešním druhým demonstračním příkladem:

Obrázek 2: Animovaný GIF vytvořený druhým příkladem.
https://tisnik.github.io/moviepy-videos/video2.htm
12. Třetí příklad – vykreslení všech 16 milionů barev v desetisekundovém videu
Nyní již máme k dispozici všechny informace nutné pro to, abychom byli schopni vytvořit video, v němž se postupně (konkrétně na 256 snímcích) zobrazí celá barvová paleta. Připomeňme si, že datová struktura typu ndarray, která je pro každý snímek vytvořena, obsahuje šířka×výška hodnot, přičemž každá hodnota je vektorem tří čísel představujících hodnoty barvových složek red, green, blue. Každá barvová složka je reprezentována celým číslem od 0 do 255. Animace bude vytvořena následovně: každý snímek bude mít rozlišení 256×256 pixelů, což znamená, že jedna barvová složka bude ve snímku konstantní a ostatní dvě barvové složky budou použity ve všech možných vzájemných kombinacích (256×256 kombinací). Pokud vytvoříme 256 takových snímků, přičemž na každém budeme měnit první barvovou složku, zobrazíme postupně všech zhruba šestnáct milionů barev, které dokážou generovat grafické karty (a zobrazit dobře nakalibrované monitory).
Funkce make_frame bude vypadat následovně (předpokládáme, že se bude volat 256×, přičemž počitadlo bude uloženo v globální proměnné index):
def make_frame(t): """Vytvoreni jednoho snimku videa.""" global index print("time: {t}, index: {i}".format(t=t, i=index)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) # vyplneni barvovym prechodem for y in range(HEIGHT): for x in range(WIDTH): frame[y][x][0] = x frame[y][x][2] = y frame[y][x][1] = index index += 1 return frame
Zdrojový kód celého demonstračního příkladu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from moviepy.editor import VideoClip import numpy WIDTH = 256 HEIGHT = 256 index = 0 def make_frame(t): """Vytvoreni jednoho snimku videa.""" global index print("time: {t}, index: {i}".format(t=t, i=index)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) # vyplneni barvovym prechodem for y in range(HEIGHT): for x in range(WIDTH): frame[y][x][0] = x frame[y][x][2] = y frame[y][x][1] = index index += 1 return frame # vytvoreni video klipu animation = VideoClip(make_frame, duration=10) # export videa do formatu Ogg Video File animation.write_videofile("colors.ogv", fps=25) # znovunastaveni pocitadla index = 0 # export videa do formatu MPEG-4 animation.write_videofile("colors.mp4", fps=25) # znovunastaveni pocitadla index = 0 # export videa do formatu GIF animation.write_gif("colors.gif", fps=25)
13. Výsledky třetího příkladu
Znovu si ukážeme výsledná videa vytvořená dnešním třetím demonstračním příkladem:

Obrázek 3: Animovaný GIF vytvořený třetím příkladem.
https://tisnik.github.io/moviepy-videos/video3.htm
14. Parametry předávané videokodeku
Při ukládání videa je možné zvolit několik parametrů předávaných video kodeku. Především se jedná o parametr progress_bar, který určuje, jestli budou výpisy z funkce make_frame přerušovány zobrazovaným progress barem (což působí rušivě). Důležitější je však parametr bitrate, kterým se určuje přibližný mezní bitový tok (bity za sekundu). Čím nižší bude zapsaná hodnota, tím horší bude kvalita výsledného videa a naopak (i když vztah není lineární). Podívejme se na příklad:
animation.write_videofile("mandelbrot_zoom.ogv", fps=20, progress_bar=False, bitrate="900000")
Rozdíly mezi videi vytvořenými s rozdílným bitovým tokem si můžete otestovat na této stránkce:
https://tisnik.github.io/moviepy-videos/video4.htm
15. Čtvrtý příklad – animace „průletu“ Mandelbrotovou množinou
V dnešním posledním příkladu si ukážeme, jak je možné vytvořit působivou animaci „průletu“ Mandelbrotovou množinou. Tématem vykreslování Mandelbrotovy množiny jsme se podrobně zabývali v seriálu o fraktálech v počítačové grafice:
- Výpočet bodů ležících v Mandelbrotově množině
https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xii#k05 - Fraktály v počítačové grafice XIII – zobrazení Mandelbrotovy množiny
https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xiii - Fraktály v počítačové grafice XVI – Animace průletu Mandelbrotovou množinou
https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xvi
Výsledná animace se vytvoří tak, že se postupně spočítá větší množství obrázků Mandelbrotovy množiny, přičemž každý obrazec bude mít nastavené odlišné parametry výpočtu. Je přitom jasné, že čím menší budou rozdíly mezi jednotlivými parametry, tím plynulejší bude výsledná animace. Vytvořené rastrové obrázky se následně seřadí do sekvence a vytvoří se z nich animace, tj. postupný přechod mezi obrázky. Animaci lze samozřejmě uložit v různých formátech, přičemž my prozatím zůstaneme u kombinace MPEG4+H.264 a Ogg+Theora. Parametry pro tvorbu jednotlivých obrázků jsou nastaveny takovým způsobem, že mapování prvního obrazu do komplexní roviny vytvoří osově orientovaný obdélník o délce stran 4,0 a 3,0 se středem v bodě specifikovaném uživatelem ve skriptu. Ve druhém a každém dalším obrázku animace je obdélník proporcionálně zmenšen a zmenšování probíhá tak dlouho, aby se u posledního obrázku dosáhlo zadaného místa v Mandelbrotově množině. Příklad BEZ funkce pro výpočet Mandelbrotovy množiny bude vypadat takto:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from moviepy.editor import VideoClip import numpy import palette_mandmap WIDTH = 320 HEIGHT = 240 MAXITER = 255 # pocatecni podminky x0 = -0.7913539 y0 = 0.161779 scale = 1.0000 scale_factor = 0.97 def make_frame(t): """Vytvoreni jednoho snimku videa.""" global scale print("time: {t}, scale: {s}".format(t=t, s=scale)) # vyplneni trojrozmerneho pole nulami frame = numpy.zeros((HEIGHT, WIDTH, 3)) calc_mandelbrot(WIDTH, HEIGHT, MAXITER, palette_mandmap.palette, x0, y0, scale, frame) scale *= scale_factor return frame # vytvoreni video klipu animation = VideoClip(make_frame, duration=15) # export videa do formatu Ogg Video File animation.write_videofile("mandelbrot_zoom.ogv", fps=20, progress_bar=False, bitrate="900000") # export videa do formatu MPEG-4 # animation.write_videofile("colors.mp4", fps=25) # export videa do formatu GIF # animation.write_gif("colors.gif", fps=25)
16. Přepočet parametrů obrázku v průběhu animace a výpočet snímku Mandelbrotovy množiny
Samotný výpočet Mandelbrotovy množiny je rozdělen na dvě části:
- Výpočet mezních hodnot v komplexní rovině, což zajišťuje funkcecalc_corner
- Výpočet barev pixelů v Mandelbrotově množine, což zajišťuje funkcecalc_mandelbrot
První zmíněná funkce na základě středu snímku (v komplexní rovině), velikosti obdélníkového výřezu a měřítka spočítá všechny čtyři mezní hodnoty:
def calc_corner(c_width, c_height, xpos, ypos, scale): return xpos - c_width * scale, \ ypos - c_height * scale, \ xpos + c_width * scale, \ ypos + c_height * scale
Druhá funkce vypočte barvy všech pixelů s využitím barvové palety popsané v navazující kapitole:
def calc_mandelbrot(width, height, maxiter, palette, xpos, ypos, scale, array): xmin, ymin, xmax, ymax = calc_corner(2.0, 1.5, xpos, ypos, scale) c = complex(xmin, ymin) for y in range(0, height): c = complex(xmin, c.imag) for x in range(0, width): z = 0.0 + 0.0J i = 0 # iteracni smycka while i < maxiter: if abs(z) > 4.0: break z = z**2 + c i += 1 # vypocet barvy r = palette[i][0] g = palette[i][1] b = palette[i][2] array[y][x][0] = r array[y][x][1] = g array[y][x][2] = b # posun na dalsi bod na radku c += (xmax - xmin) / width # posun na dalsi radek c += 1J*(ymax - ymin) / height
V×Mandelbrotově množině se nachází velké množství zajímavých oblastí, přičemž zobrazované tvary jsou soběpodobné, tj. při různém zvětšení se opakují. Některé typické tvary byly podle svého vzhledu dokonce pojmenovány. Popis zajímavých oblastí začneme částí Mandelbrotovy množiny, která se nachází v okolí takzvaného Feigenbaumova bodu. S prací Feigenbauma jsme se setkali již při popisování dynamických systémů a bifurkačních diagramů. Feigenbaumův bod je místo v Mandelbrotově množině, v jehož okolí nastává stejný jev jako v bifurkačním diagramu – prudké zdvojování period vedoucích až k chaosu. Tento bod má v komplexní rovině souřadnice –1,401155+0,0i, leží tedy v záporné části reálné osy.
Další zajímavé oblasti, které můžete vybrat a vytvořit tak zajímavé video, jsou popsány v následujících kapitolách článku ze seriálu o Fraktálech v počítačové grafice:
- Oblast nazývaná „Elephant valley“
- Oblast nazývaná „Seahorse valley“
- Oblast nazývaná „West seahorse valley“
- Oblast nazývaná „Triple spiral valley“
- Oblast nazývaná „Quad spiral valley“
17. Pomocný modul s barvovou paletou
Fraktály či obecně procedurální textury vykreslené ve stupních šedi sice mohou být pro některé projekty zajímavé, ovšem většinou požadujeme textury barevné. Ve skutečnosti je řešení jednoduché – postačuje ke každému vypočtenému indexu vybrat vhodnou barvu z barvové palety. Tradičně mají barvové palety 256 barev, ovšem samozřejmě je v případě potřeby možné vytvořit rozsáhlejší či naopak menší palety. Pro účely demonstračního příkladu použijeme barvovou paletu získanou z datových souborů programu Fractint a převedenou na Pythonovský seznam:
# taken from Fractint palette = ( (255, 255, 255), (224, 224, 224), (216, 216, 216), (208, 208, 208), (200, 200, 200), (192, 192, 192), (184, 184, 184), (176, 176, 176), (168, 168, 168), (160, 160, 160), (152, 152, 152), (144, 144, 144), (136, 136, 136), (128, 128, 128), (120, 120, 120), (112, 112, 112), (104, 104, 104), (96, 96, 96), (88, 88, 88), (80, 80, 80), (72, 72, 72), (64, 64, 64), (56, 56, 56), (48, 48, 56), (40, 40, 56), (32, 32, 56), (24, 24, 56), (16, 16, 56), (8, 8, 56), (000, 000, 60), (000, 000, 64), (000, 000, 72), (000, 000, 80), (000, 000, 88), (000, 000, 96), (000, 000, 104), (000, 000, 108), (000, 000, 116), (000, 000, 124), (000, 000, 132), (000, 000, 140), (000, 000, 148), (000, 000, 156), (000, 000, 160), (000, 000, 168), (000, 000, 176), (000, 000, 184), (000, 000, 192), (000, 000, 200), (000, 000, 204), (000, 000, 212), (000, 000, 220), (000, 000, 228), (000, 000, 236), (000, 000, 244), (000, 000, 252), (000, 4, 252), (4, 12, 252), (8, 20, 252), (12, 28, 252), (16, 36, 252), (20, 44, 252), (20, 52, 252), (24, 60, 252), (28, 68, 252), (32, 76, 252), (36, 84, 252), (40, 92, 252), (40, 100, 252), (44, 108, 252), (48, 116, 252), (52, 120, 252), (56, 128, 252), (60, 136, 252), (60, 144, 252), (64, 152, 252), (68, 160, 252), (72, 168, 252), (76, 176, 252), (80, 184, 252), (80, 192, 252), (84, 200, 252), (88, 208, 252), (92, 216, 252), (96, 224, 252), (100, 232, 252), (100, 228, 248), (96, 224, 244), (92, 216, 240), (88, 212, 236), (88, 204, 232), (84, 200, 228), (80, 192, 220), (76, 188, 216), (76, 180, 212), (72, 176, 208), (68, 168, 204), (64, 164, 200), (64, 156, 196), (60, 152, 188), (56, 144, 184), (52, 140, 180), (52, 132, 176), (48, 128, 172), (44, 120, 168), (40, 116, 160), (40, 108, 156), (36, 104, 152), (32, 96, 148), (28, 92, 144), (28, 84, 140), (24, 80, 136), (20, 72, 128), (16, 68, 124), (16, 60, 120), (12, 56, 116), (8, 48, 112), (4, 44, 108), (000, 36, 100), (4, 36, 104), (12, 40, 108), (16, 44, 116), (24, 48, 120), (28, 52, 128), (36, 56, 132), (40, 60, 140), (48, 64, 144), (52, 64, 148), (60, 68, 156), (64, 72, 160), (72, 76, 168), (76, 80, 172), (84, 84, 180), (88, 88, 184), (96, 92, 192), (104, 100, 192), (112, 112, 196), (124, 120, 200), (132, 132, 204), (144, 140, 208), (152, 152, 212), (164, 160, 216), (172, 172, 220), (180, 180, 224), (192, 192, 228), (200, 200, 232), (212, 212, 236), (220, 220, 240), (232, 232, 244), (240, 240, 248), (252, 252, 252), (252, 240, 244), (252, 224, 232), (252, 208, 224), (252, 192, 212), (252, 176, 204), (252, 160, 192), (252, 144, 184), (252, 128, 172), (252, 112, 164), (252, 96, 152), (252, 80, 144), (252, 64, 132), (252, 48, 124), (252, 32, 112), (252, 16, 104), (252, 000, 92), (236, 000, 88), (228, 000, 88), (216, 4, 84), (204, 4, 80), (192, 8, 76), (180, 8, 76), (168, 12, 72), (156, 16, 68), (144, 16, 64), (132, 20, 60), (124, 20, 60), (112, 24, 56), (100, 24, 52), (88, 28, 48), (76, 32, 44), (64, 32, 44), (52, 36, 40), (40, 36, 36), (28, 40, 32), (16, 44, 28), (20, 52, 32), (24, 60, 36), (28, 68, 44), (32, 76, 48), (36, 88, 56), (40, 96, 60), (44, 104, 64), (48, 112, 72), (52, 120, 76), (56, 132, 84), (48, 136, 84), (40, 144, 80), (52, 148, 88), (68, 156, 100), (80, 164, 112), (96, 168, 124), (108, 176, 136), (124, 184, 144), (136, 192, 156), (152, 196, 168), (164, 204, 180), (180, 212, 192), (192, 220, 200), (208, 224, 212), (220, 232, 224), (236, 240, 236), (252, 248, 248), (252, 252, 252), (252, 252, 240), (252, 252, 228), (252, 252, 216), (248, 248, 204), (248, 248, 192), (248, 248, 180), (248, 248, 164), (244, 244, 152), (244, 244, 140), (244, 244, 128), (244, 244, 116), (240, 240, 104), (240, 240, 92), (240, 240, 76), (240, 240, 64), (236, 236, 52), (236, 236, 40), (236, 236, 28), (236, 236, 16), (232, 232, 0), (232, 232, 12), (232, 232, 28), (232, 232, 40), (236, 236, 56), (236, 236, 68), (236, 236, 84), (236, 236, 96), (240, 240, 112), (240, 240, 124), (240, 240, 140), (244, 244, 152), (244, 244, 168), (244, 244, 180), (244, 244, 196), (248, 248, 208), (248, 248, 224), (248, 248, 236), (252, 252, 252), (248, 248, 248), (240, 240, 240), (232, 232, 232))
18. Výsledek vytvořený čtvrtým příkladem
Dnes již naposledy si ukážeme výsledná videa vytvořená čtvrtým demonstračním příkladem:
https://tisnik.github.io/moviepy-videos/video4.htm
19. Repositář s demonstračními příklady
Zdrojové kódy všech čtyř dnes popsaných demonstračních příkladů určených pro interpret Pythonu 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/moviepy-examples. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem prozatím velmi malý, doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- MoviePy 0.2.3.3 na PyPi
https://pypi.org/project/moviepy/ - MoviePy na GitHubu
https://github.com/Zulko/moviepy - MoviePy – dokumentace
http://zulko.github.io/moviepy/ - MoviePy – galerie
http://zulko.github.io/moviepy/gallery.html - Data Animations With Python and MoviePy
https://zulko.github.io/blog/2014/11/29/data-animations-with-python-and-moviepy/ - Porovnání formátů Ogg Theora a H.264
https://www.root.cz/zpravicky/porovnani-formatu-ogg-theora-a-h-264/ - Případ GIF
https://www.root.cz/clanky/pripad-gif/ - Pravda a mýty o GIFu
https://www.root.cz/clanky/pravda-a-myty-o-gifu/ - Anatomie grafického formátu GIF
https://www.root.cz/clanky/anatomie-grafickeho-formatu-gif/ - GIF: animace a konkurence
https://www.root.cz/clanky/gif-animace-a-konkurence/ - Two python modules : MoviePy and images2gif – part 001
http://free-tutorials.org/two-python-modules-moviepy-and-images2gif-part-001/ - images2gif
https://pypi.org/project/images2gif/ - Making GIFs from video files with Python
https://www.devbattles.com/en/sand/post-345-Making+GIFs+From+Video+Files+With+Python - GIF89a specification
https://www.w3.org/Graphics/GIF/spec-gif89a.txt - MPEG-4 Part 14
https://en.wikipedia.org/wiki/MPEG-4_Part14 - Theora video compression
https://www.theora.org/ - Theora
https://en.wikipedia.org/wiki/Theora - NumPy
http://www.numpy.org/ - numpy 1.14.2 (on PyPi)
https://pypi.org/project/numpy/ - 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/ - 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/ - Non-linear editing system
https://en.wikipedia.org/wiki/Non-linear_editing_system