28 marca 2016

vpython, gify i mechanika klasyczna

Od dawna planuje zabrać się za naukę mechaniki klasycznej. To kolejne podejście, tym razem jednak, zacząłem od poszukiwania narzędzi, które pomogą mi w nauce. Zaczęło się od bardzo fajnego artykułu na temat biblioteki vpython, linki poniżej i inne przydatne:
Za cel eksperymentu postawiłem sobie, wygenerowanie gif-a, z animacją ruchu. Liczyłem, że wystarczy do tego ImageMagic, jednak tworzenie gif-ów jest na tyle zagmatwane, że trzeba było skorzystać również z pakietu FFmpeg. Efekt nie jest do końca satysfakcjonujący. Od pewnego czasu, za bardzo skupiam się nad idealną wersją prototypu. Co więcej staram się znaleźć odpowiedzi na wszystkie pytania jakie się pojawią podczas jego wytwarzania, szczególnie te poboczne. Nie taka był idea tego bloga, czas więc na refleksję.

W każdym razie, oto przerobiony przykład "bouncing ball" z niewielkimi modyfikacjami. Po pierwsze, skorzystałem z biblioteki PIL (Python Image Library), aby tworzyć zrzuty ekranu kolejnych klatek animacji. Biblioteka działa jedynie pod systemem Windows, a jej zamiennik pyscreenshot nie zdał rezultatu. Jakoś się z tym pogodziłem. Druga sprawa, to warunek przerwania animacji, czyli moment gdy piłka znów znajdzie się w tej samej pozycji.
from visual import *
import ImageGrab    # from PIL


starting_height = 4
floor = box (pos=(0, 0, 0), length=4, height=0.5, width=4, color=color.blue)
ball = sphere (pos=(0, starting_height, 0), radius=1, color=color.red)
ball.velocity = vector(0, -1, 0)
dt = 0.01

frame = 0
while 1:
    rate (100)
    ball.pos = ball.pos + ball.velocity*dt
    if ball.y < ball.radius:
        ball.velocity.y = abs(ball.velocity.y)
    else:
        ball.velocity.y = ball.velocity.y - 9.8*dt

    file_name = 'img-' + '{fr:03d}'.format(fr=frame)  + '.png'
    frame += 1

    im = ImageGrab.grab((0, 0, 500, 500))  # screen box from (0,0)-(500,500)
    im.save(file_name)                     # save image to disk

    if ball.pos.y > starting_height:
        exit()
Skrypt wygenerował około 140 zrzutów ekranu, które trzeba było poddać obróbce, nieoceniony okazał się tutaj ImageMagic. Kluczowym problem jest wysterowanie czasu pomiędzy kolejnymi klatkami.
Wszystkie obrazki zawierały ramki Windows-owego okienka, które trzeba było usunąć:
Przycinanie zdjęcia za pomocą mogrify (z ImageMagic). Pozycje i rozmiar dobrałem eksperymentalnie.
mogrify -crop 413x411+9+30 +repage $(ls -v *.png)
Chociaż ImageMagic potrafi generować gif-y, jednak za żadne skarby nie mogłem skonfigurować właściwego opóźnienia, pomiędzy kolejnymi klatkami. Szperając po internecie udało się ten problem ominąć, wymaga to jednak kroków pośrednich, w postaci utworzenia filmiku z animacją, a następnie przepuszczenia go jeszcze raz przez ffmpeg i convert (ImageMagic). Magiczny zestaw komend:
# Filmik ze zdjęć
ffmpeg -r 100 -f image2 -i img-%3d.png video.avi

# Wyciąganie klatek z filmiku i zlepianie w gif-a
ffmpeg -i video.avi -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - gif:- | convert -layers Optimize - output.gif

# Alternatywna wersja, też działa
ffmpeg -i video.avi -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - output2.gif
W animacji jest jakiś uszczerbek jakości, tak jakby brakowało wszystkich potrzebnych klatek. Być może uda mi się kiedyś rozwiązać tą zagadką i odpowiednio dobrać parametry, wtedy też o nich napiszę. Próbowałem także innych rozwiązań, bezskutecznie próbując wysterować opóźnienie pomiędzy kolejnymi klatkami.
# Gif generowany tylko przez convert (ImageMagic)
convert -dispose none -delay 1.5 $(ls -v *.png) -coalesce output3.gif

# Gif generowany tylko przez ffmpeg
ffmpeg -f image2 -framerate 100 -i img-%3d.png output4.gif

Brak komentarzy:

Prześlij komentarz