Back to top

Paprotka 2

Po blisko dwóch latach od premiery "Paprotki", wielu godzinach wytężonej pracy i wydaniu "fortuny" na moc obliczeniową u różnych dostawców rozwiązań PaaS czy VPS - oto, mam zaszczyt przedstawić wersję drugą (poprawioną) tego hitu! 412 klatek połączonych w film o prędkości 12 klatek/s = efekt prezentowany na poniższym filmiku! Nie jest idealnie, ale dzięki dodaniu liniowego (plus-minus) zwiększenia ilości przeliczeń "per iteracja" obraz nie "rozmywa się" przy narastającym powiększeniu - to główna, widoczna różnica w stosunku do wersji poprzedniej filmu. Zwiększenie ilości obliczeń oczywiście spowodowało wzrost czasu generowania klatek (w prezentowanym, przykładowym filmie, niekiedy nawet do ponad 48h na klatkę - taki czas generowania miało około 25% z 412 klatek), co z kolei utrudniło "produkcję" w warunkach domowych - stąd wykorzystanie PaaS / VPS.

Zacznę od przepisu na wygenerowanie powyższego filmu - będziemy potrzebować programu fractal, oto jak go uzyskać:

Produkcja

1. JImageViewer

JImageViewer to skromna biblioteka odpowiedzialna za generowanie i obsługę podstawowego elementu graficznego interfejsu użytkownika programu fractal - okienka do przeglądania fraktali. Mimo, iż GUI nie będzie wykorzystywane w tym scenariuszu użycia programu, to jednak do kompilacji programu biblioteka ta jest wymagana (aktualnie), tak więc musimy zacząć budowanie programu od niej.

Pobieramy kod z serwisu GitHub (oznaczony tagiem 0.1 - to kod wykorzystywany przeze mnie w na potrzeby tej "produkcji"), wchodzimy do katalogu do którego kod został pobrany i wykonujemy polecenie "make.sh", które zbuduje potrzebny plik wynikowy (archiwum .jar).

$ git clone -b '0.1' --single-branch --depth 1 \
> https://github.com/lbacik/JImageViewer.git
$ cd JImageViewer/
$ ./make.sh 

Na razie zostawiamy bibliotekę JImageViewer i przechodzimy dalej.

2. Commons-cli

Commons-cli to druga z wymaganych do kompilacji programu fractal bibliotek - tym razem jest to projekt zewnętrzny, wymagane pliki musimy pobrać i rozpakować (z omawianą wersją programu fractal - 0.2.1 - wykorzystywałem commons-cli w wersji 1.2)

$ wget http://archive.apache.org/dist/commons/cli/binaries/commons-cli-1.2-bin.tar.gz
$ tar -xzf commons-cli-1.2-bin.tar.gz 

I ponownie, na razie zostawiamy jak jest.

3. Fractal

Rozpocznijmy od pobrania kodu (tag 0.2.1):

$ git clone -b '0.2.1' --single-branch --depth 1 \
> https://github.com/lbacik/Fractal.git
$ cd Fractal/

Teraz przenieśmy do katalogu "Fractal" wymagane biblioteki (informacja dla użytkowników nie zaznajomionych z GNU - "samotne" kropki na końcu polecenia są istotne! :)):

$ cp ../JImageViewer/jimageviewer.jar .
$ cp ../commons-cli-1.2/commons-cli-1.2.jar .

Budujemy!

$ ./make.sh		

Jeżeli poniższe zadziała to znaczy, że wszystko poszło ok (ang. teksty są do poprawy - wiem, wiem...) :)

$ lukasz@lukasz:~/tmp$ ./fractal.sh -h
usage: fractal
 -d,--drawing-class         source file (with .java ext.)/ drawing
                                  class file *REQUIRED*
 -e,--series                ...
 -h,--help                        help - this screen
 -i                          start iteration number (use with series)
 -o,--out                   png output file (optional, if it is not
                                  given the filename the name of the
                                  drawing class will be used)
 -s,--scale        use given scale
    --show-registry               shows configuration registry
 -w,--series-number-width    ...

4. Tworzenie klatek

Na początku krótka uwaga: w aktualnej, niedoskonałej wersji bardzo pomocna przy tworzeniu klatek jest opcja "-i" - pozwala ona wskazać iterację od której chcemy rozpocząć, co przy planowaniu uruchomienia kilku programów generujących klatki równocześnie pozwala "rozdzielić" zadania pomiędzy procesy. Opcja ta przydaje się również w przypadku przerwania pracy programu, kiedy chcemy pominąć już raz wygenerowane klatki (i to w sumie był główny powód jej wprowadzenia). Poniżej dwa przykłady wykorzystania opcji "-i" - generowanie klatki nr 200 będzie znacznie dłuższe niż klatki nr 100, ale nie powinno trwać dłużej niż 5 minut, klatki potrzebujące naprawdę dużo czasu na wygenerowanie rozpoczynają się, w tym konkretnym przypadku, mniej więcej od numeru 310.

$ ./fractal.sh -d fractals/f06.java -e series/f06.series -i 100 -o test
$ ./fractal.sh -d fractals/f06.java -e series/f06.series -i 200 -o test

po uruchomieniu powyższych przykładów w katalogu programu powinny pojawić się dwa pliki graficzne - test-100.png oraz test-200.png.

Technicznie, w rozumieniu programu fractal, film to sekwencja klatek (grafik) wygenerowanych za pomocą tego samego wzoru (fraktala) dla różnych parametrów wejściowych (takich jak np. skala, czy ilość iteracji). Wzór (klasę) generujący(ą) fraktal podajemy po parametrze "-d" i w przypadku filmu "Paprotka 2" wzór wykorzystanego fraktala (paprotki) można znaleźć w pliku fractals/f06.java. Aby wygenerować pojedynczy plik graficzny (png) z tym fraktalem (przy wykorzystaniu domyślnej, zdefiniowanej w pliku fractals/f06.java, skali) wykonujemy (plikiem wynikowym będzie f06.png):

$  ./fractal.sh -d fractals/f06.java

Natomiast parametry "ożywiające" ten obraz zapisane zostały w pliku: series/f06.series. Plik ten wygląda następująco (wersja dla filmu "Paprotka 2"):

#
# ver2 
#
# x1,y1 - coordinates of the left-bottom corner
# x2,y2 - coordinates of the right-top corner
#
#					iteration
# x1	y1	x2	y2	frames	major	minor 
-3	0	3	10	0	1	1
-3	0	3	10	120	1000	1000
-1.0935 3.54375 0.57 	6.4405  120	1000	1000000
-0.7175 4.24 	0.09075 5.7385	72	1000	100000000
-0.458	4.725	-0.24	5.254	100	10000	700000000

Dane zawarte w plikach f06.java i f06.series są wszystkim czego potrzeba do wygenerowania filmu "Paprotka 2".

Na pierwszy rzut oka plik f06.series nie wygląda dość jasno, i w gruncie rzeczy - taki (jasny) nie jest. W tym konkretnym przypadku można wyróżnić cztery etapy produkcji (wszystko co zaczyna się od znaku '#' to komentarz i można to pominąć). Pierwszy etap to dwie pierwsze linie, każdy kolejny określony jest przez kolejny (już pojedynczy) wpis. Każda linia zawiera informacje o ilości "klatek" w danym etapie: kolumna frames, docelowej skali (skalą początkową jest docelowa skala poprzedniego etapu, bądź skala "domyślna" - określona w pliku definiującym fraktal): kolumny x1, y1, x2, y2, oraz o ilości iteracji (sumie przeliczeń) dla ostatniej klatki w danym etapie (przy czym w aktualnej wersji wartość "major" pozostaje niezmienna): kolumny "major" i "minor", gdzie "ilość iteracji" = major * minor. Konkretne wartości x1, y1, x2, y2 oraz minor są wyliczane przez program, na podstawie danych z pliku, dla każdej z iteracji (program wypisuje wyliczone wartości przed przystąpieniem do generowania pliku graficznego, dlatego można np. "podejrzeć" wyliczenia dla konkretnej klatki wykorzystując opcję "-i", a następnie przerwać pracę programu - Ctrl+C - nie czekając na wygenerowanie pliku graficznego). Przejścia pomiędzy etapami są widoczne na filmie - nie są idealnie płynne, dynamika, sam fraktal zmienia się w wyraźny sposób (pierwszy etap wyszedł niestety słabo, wydaje się że przez pierwszych 8 sekund nic się nie dzieje... cóż, wizja była inna, zabrakło testów).

Wszystkie klatki filmu "Paprotka 2" można przejrzeć tutaj: https://onedrive.live.com/redir?resid=AF5B2BDC49C4A%2127147.

5. Składanie filmu

Dysponując już zbiorem klatek nie pozostaje nic innego jak połączyć je w film - ja wykorzystuję do tego celu program avconv, dla filmu "Paprotka 2" użyłem następujących parametrów (klatki zostały zapisane w plikach f06--X.png, gdzie X to liczba z zakresu 0..412):

$ avconv -r 12 -i f06b--%d.png -b:v 4096K test-04.mp4

Koszty

Kiedy wiemy już "jak", przyjrzyjmy się bliżej kwestii "za ile".

"Paprotka 2", w stosunku do "Paprotki", to film już z konkretnymi kosztami. Nie, żeby nie można było wszystkiego "policzyć" na domowym PC (jak zresztą na początku zamierzałem i od czego zacząłem), ale ponieważ szybko okazało się to dość niewygodne postanowiłem przenieść to "liczenie" na jakiś komputer zdalny. Pierwszym kandydatem został OpenShift - założyłem nawet konto z "Bronze plan" i uruchomiłem program na "small.highcpu" gear. Za 696 gear-hours (po 0.025 euro/h), plus podatek, zapłaciłem w listopadzie 2015r. 21.39 euro. To dużo. Programy tego typu (jak fractal), które wykorzystują CPU na 100% przez cały czas swojego działania (w końcu non-stop liczą) nie są najlepszymi kandydatami do hostowania na platformach, na których płaci się w sumie za wykorzystanie procesora właśnie (ów 0.025 euro/h). Tym razem OpenShift się niestety nie sprawdził i postanowiłem poszukać innego rozwiązania. Dodam jeszcze, że uruchomienie programu w dwóch wątkach, obciążających równolegle dwa rdzenie/wątki procesora, oczywiście mnożyło ów 0.025 euro/h razy dwa.

W drugim kroku wypróbowałem usługę typu VPS w DigitalOcean. 6.15$/m-c (z podatkiem) - to dość duża oszczędność w stosunku do 21.39 euro. I szczerze mówiąc - super to działało! Kolejny miesiąc przeliczeń poszedł gładko, ale przecież nigdy nie ma tak dobrze, aby nie mogło być lepiej. Rozejrzałem się trochę wśród ofert krajowych i znalazłem dostawcę VPS (minthost.pl), który za 25zł/m-c oferował VPS z dwurdzeniowym procesorem! Dwa rdzenie, to możliwość równoległego uruchomienia przeliczeń, co "obniżało" koszty pracy jednego procesu do 12,5zł/m-c! I w sumie na tym zakończyły się moje poszukiwania - ostatnie klatki filmu były przeliczana na minthost. Całość zajęła mi ponad dwa miesiące (suma pracy na wszystkich platformach) i kosztowała jakieś 150zł. Montowałem już na domowym PC.

Kilka słów na koniec

Dużo roboty, a efekt (z punktu widzenia widza) - mizerny :( Brakuje kolorów, dokładniejszej kontroli nad generowaniem filmu (parametry ujęć, klatek), JImageViewer nie dysponuje "ficzerami" które znacząco ułatwiały by przygotowanie pliku "series" dla konkretnego fraktala - to wszystko trzeba jeszcze dorobić / dopracować!