– fordi tiden kræver et MODSPIL

15. Sep 2008

Fri software og multimedier: Videostabilisering med Yuv Motion Tools og mjpegtools

 


- af Rene Jensen

I dette indlæg oprulles historien om tilblivelsen af et værktøj til at stabilisere nogle optagelser fra en "skattejagt" i det fri i forbindelse med Bathos' sommerfest. Man skulle selvfølgelig gå omkring på markvej og hvad ved jeg af uskikkeligt jysk fantasteri, og da holdene konkurrerede både til fods og i bil, endte det med at der også blev løbet. Jeg havde håbet at de rystende autentiske optagelser kunne nå at blive forbedrede ad digital vej, men FOSS-nørd som jeg er, var Adobe AfterEffects jo ikke en mulighed.

Cinelerra har et filter til motion tracking, men det virker ved at spore et af brugeren defineret punkt på skærmen, og kompensere for hop og rotationer fra frame til frame. Man kan helt sikkert få meget sjov ud af deres motion filter, fordi det er ment som et alsidigt værktøj til tracking af visuelle billedpunkter. Men til billedstabilisering er det for upræcist.

Indeholdt i MJPEGTools-pakken er programmet y4mstabilizer. Det voldte nogen hovedbrud at få det til at fungere rigtigt, dvs. at få stillet en kommandolinje op, så det modtog videodata i det rigtige format. Resultatet blev endnu mere springende end før, eller også kom programmet og jeg skævt ind på hinanden. Jeg prøvede dog heller ikke særlig hårdt, eftersom min underbevidsthed nok mere eller mindre allerede havde besluttet at det sjoveste ville være at kode noget selv. Der er en enkelt parameter eller to, man kan justere på. Min umiddelbare fornemmelse siger mig dog, at den algoritmiske fremgangsmåde, der anvendes, er den samme som Cinelerra bruger.
    lav2yuv myfilm -C 420jpeg
    | yuvdeinterlace -s1
    | y4mtoyuv
    | yuv4mpeg -w 720 -h 576 -x 420paldv -i p -a 59:54
    | y4mstabilizer
    | y4mtoyuv
    | yuv4mpeg -w 720 -h 576 -x 420mpeg2 -i p -a 59:54
    | yuvplay
Åbenbart er det ikke så ligetil at skrive et sådant program, men da Modspil jo har haft fingrene i begrebet motion tracking før, så var det jo en nærliggende tanke at man kunne gøre det selv (med den beskedne bieffekt, at jeg brugte al tiden på at kode i stedet for, så Bathos' redaktør til sidst brugte optagelserne som de var, mens jeg hyggede igennem ved tastaturet).

Kunsten at vælge et videoprocesmiljø

Jeg er efterhånden ved at være af den formening, at MJPEGTools er en god værktøjskasse til forbehandling af videomateriale. Programmernes største dyd er ganske givet, at filosofien med at man starter en stribe programmer på kommmandolinjen, som er forbundede af shell pipes, er uhyre simpel og i fortsættelse af Unix-tankegangen. Formatet af de data, der strømmer rundt er tilsvarende enkelt, så set fra en programmørs synspunkt kan man komme igang med at lave nye programmer med så lidt som følgende:

    // Deklarer et par variable ....

    // Læs header fra input
    scanf  ("YUV4MPEG2 W%d H%d F%d:%d I%c A%d:%d %s\n", &width, &fheight, 
            &fpsNum, &fpsDen, &interlaceType, 
            &aspectNum, &aspectDen, colorSpace);

    // Skriv yuv4mpeg header til output
    printf ("YUV4MPEG2 W%d H%d F%d:%d I%c A%d:%d C420paldv\n", width, height, 
             fpsNum, fpsDen, interlaceType, 
             aspectNum, aspectDen);

    // Opret en omgang arrays, Ydata, Udata, Vdata;

    while (true)
    {
        if (scanf ("FRAME") == EOF)
            break;
        fread (Ydata, width*height, stdin);
        fread (Udata, width*height/4, stdin);
        fread (Vdata, width*height/4, stdin);

        printf("FRAME")
        fwrite (Ydata, width*height, stdout);
        fwrite (Udata, width*height/4, stdout);
        fwrite (Vdata, width*height/4, stdout);
    }
I praksis er man nok bedst tjent med at bruge det medfølgende bibliotek. Ikke desto mindre kunne jeg bruge ovenstående kode som udgangspunkt gennem hele nedenstående kodeforløb. De ihærdige kan eventuelt læse formatbeskrivelsen.

Selve opgaven med at analysere billederne og følge forandringer klares ligesom sidst af Intel's OpenCV-bibliotek.

Misfornøjelsen ved at vælge et udviklingsmiljø

Selvom jeg er en stor fan af Python, så er det ikke til at fornægte, at jeg har brugt utilgiveligt meget tid på at prøve at opfinde eller forbedre bindinger til eksisterende C/C++-biblioteker vha. Py++, Boost-Python, Swig, CTypes osv. Mine nerver trænger i det mindste for en periode til at jeg bruger C++, når der er udsigt til at de biblioteker, jeg får brug for, ikke lige er klar på Python-hylden, og vel at mærke på alle platforme og distro'er. Det gav mig en kærkommen lejlighed til at vræle over hvor dybt jeg føler, at C++ er sunket, svigtet af sin fader som det er. Intet stabilt FOSS udviklingsmiljø, der kan bruge introspektion i en fornuftigt virkende autocompletion. Ingen refactor-værktøjer. Intet kæmpe standardbibliotek, der er nemt at forstå og læse. Ingen compiler, der deler intermediært sprog med fire-fem andre sprog, så de alle kan linke sammen. Vræl. Will program C++ for food.

Nå så gik der lidt af jeres tid med det også. Det er da en trøst. Jeg vil dog indrømme en vis fascination over standardbiblioteket i C++, altså STL, samt det i ånden samstemmende hjælpebibliotek Boost.

Tænkearbejdet

Nu kommer det svære. Inden læseren loves mere end programmøren kan holde, skal jeg afsløre, at jeg af hensyn til en veludviklet sans for at undgå projekter, der løber ud i sandet, tog tøjlerne, og færdiggjorde EN løsning, modsat slet ingen.

Algoritmen i denne første version sporer en stribe punkter fra frame til frame. OpenCV er flink til selv at kunne foreslå de mest oplagte punkter, altså særligt tydelige kandidater i billedet, som udmærker sig med god kontrast og andre egenskaber. Efterhånden som et sådant "feature point" ikke med rimelig sikkerhed kan spores mere, fjernes det fra puljen, og når den bliver for slunken, så ledes der efter en ny gang kandidater, således at det samlede antal aldrig bliver for lavt.

Den samlede forskydelse for hele skærmen udregnes på mest primitive manér ved simpelthen at tage tværsnittet af alle punkternes forskydninger. Det er selvfølgelig helt forkert, men i praksis virker det bedre, end det lyder.

Prøv at bruge disse parametre til programmet, for at få visualiseret sporingspunkter og lignende:
    --show-featurepoints         # viser hvert punkt
    --show-velocityvectors       # viser hvert punkts retningsvektor
    --show-velocitydistribution  # viser alle retningsvektorer med udgangspunkt i centrum
Når man tænker det lidt igennem, så er der mange typer samtidige bevægelser i en optagelse, som en god stabilizer bør forsøge at tage hensyn til. For at nævne nogle få: 1) Panorering, dvs. resultatet af at dreje kameraet rundt i hånden. 2) Rotation omkring kameraets egen akse, hvorved horisontlinjen roterer på billedet. 3) Bevægelse af kameraet i rummet. Sådan en bevægelse giver parallakseforskydning af de forskellige objekter foran kameraet. Jeg håber at kunne identificere dem ved at lure, at nærmere objekters bevægelser er større end fjerne, men dog i samme retning. 4) Perspektivisk forvrængning. Eksempelvis vil sporingspunkter langs vejen nærme sig hurtigere og hurtigere. 5) Objekters egenbevægelse - biler, personer osv. 6) Fejl, som udgør en grimt stor del af det hele.

Lige nu er det en naiv algoritme, der udjævner bevægelsen ved en simpel formel
    
     forskydning_ny = 0.95 * (forskydning_gammel 
          + SUM(sporingspunkters-hastighed) / antal-sporingspunkter))

Men mere om det ved en anden lejlighed - hinsides et par bøger eller tre om statistik. Desuden har jeg valgt fuldstændigt at ignorere, at billedet kan blive så forskudt, at det forsvinder helt. Der er med andre ord ingen maksimal begrænsning på forskydningen. Hvis jeg hurtigt drejer kameraet til højre, så skubber algoritmen billedet til venstre i et stykke tid.

Problemet ved at vælge et hostingmiljø

Nu er koden klar i denne første primitive version, og for forandringens skyld ville jeg prøve at smide den ud på et rigtigt Open Source hostingmiljø.

Offentligheden bør altid have en chance for at kunne komme med offentlig kritik, helst i form af fejlrapporter. Personligt finder jeg det også tiltalende, at kode, der er overdraget til offentligheden, ikke nødvendigvis ligger på et personligt site. Okay, jeg har flydende grænser og er ikke afklaret omkring det, før jeg har talt med min psykolog. Min lillebror (min psykolog) er meget imod SourceForge. Desuden faldt jeg rent tilfældigt atter over denne gamle svend: www.gnu.org/philosophy/open-source-misses-the-point.html, hvori Stallman uddyber forskellen mellem det politisk neutrale begreb Open Source, og hans sociale imperativ Free Software. Den var herligt frigørende, fordi jeg åbenbart trængte til at ytre dette terapeutiske mantra "Open Source er ikke nødvendigvis en god udviklingsmodel. Faktisk stinker den ret ofte" ti gange i træk. Dybest set er jeg nok bare altruist på godt og ondt.

Så valget faldt på savannah.nongnu.org, som virker i god tråd med denne filosofi. Man skal ligefrem forsikre dem om, at man vil bruge udtrykket fri software fremfor open source. Jeg venter i skrivende stund stadig på svar fra dem, så indtil videre må andre nøjes med at hente fra mit eget site.

Find programmet og vejledning her: vaults.atomicmonstergirls.net/useful/techworks/utilities/yuvmotiontools/.

Brug denne kommandolinje for at stabilisere en stump DV video:
    lav2yuv video.avi | yuvdeinterlace -s1 | yuvstabilizecv | yuvplay
Eller denne for at udvælge 80% af billedet og skære kanterne væk:
    lav2yuv video.avi | yuvdeinterlace -s1 | yuvstabilizecv | \
          yuvscaler -I USE_576x460+72+56 -M BICUBIC -O SIZE_720x576 | yuvplay
Følgende eksempler demonstrerer resultatet på godt og ondt. Når man ser på de stabiliserede optagelser, er det tydeligt, at det næste trin i udviklingen af algoritmen er at dæmpe rotationen i billedet også. Et andet betydeligt problem er, at algoritmen ukritisk skærer for meget af billedet af. I nedenstående eksempler har jeg zoomet en del ind på billedet, for at fjerne noget af den sorte kant. Problemet er naturligvis fundamentalt at man må vælge hvilken del af billedet, der er værd at bevare. Hvis ikke man kan holde kameraet i ro ved hjælp af fysiske hjælpemidler - stativ, steadycam eller lignende - så er det bedste, man kan gøre, at bruge en vidvinkel og et kamera med god opløsning. Teknisk set er det ikke utænkeligt, at man kan rekonstruere en del af en statisk horisont.


Spol frem til midten af klippet for at se det stabiliserede resultat
Det er tydeligt, at kameraet både drejer, hopper og vælter rundt om sin egen akse.
Det sidste giver en rotation, som er generende, og som helt klart skal ordnes,
før programmet er noget værd til disse optagelser.



Spol frem til midten af klippet for at se det stabiliserede resultat
Her melder der sig et nyt problem: Optiske effekter på linsen.
I det stabiliserede hopper de rundt sammen med originaloptagelsen. Fail!



Spol frem til midten af klippet for at se det stabiliserede resultat
Et centralt motiv, der alligevel ikke forstyrrede så meget.
Jeg vil tro, at det skyldes, at bidraget til den kumulative bevægelse er konstant.


Kommentarer: