Jeffrey Cross
Jeffrey Cross

Att göra en laserskärd zoetrope med behandling och Kinect

Denna kodruta visar hur du skapar en fysisk zoetrope med Processing, Kinect och en laserskärare. Skissen kommer att laddas i en film inspelad från Kinects djupkamera, använd bearbetningen OpenCV-biblioteket för att göra den här filmen till en rad konturer och spara konturerna i vektorform som en DXF-fil som kan skickas till en laserskärare. Jag ska också förklara utformningen av betraktningsmekanismen som ger zoetropen sin spinn.

Om Zoetropes

Innan du hoppar in i koden, lite kulturhistoria och inspiration. Zoetropen var en populär viktoriansk optisk leksak som skapade en form av animering ur en papperscirkel. På cirkeln skulle zoetrope tillverkare skriva ut en serie ramar från en animering. Då skulle cirkeln vara omgiven av en ogenomskinlig skiva med en serie slitsar som skärs ut av den. När papperscirkeln snurra skulle tittaren titta på den genom slitsarna och se en animering. Slitsarna fungerade som en filmprojektor, så att tittaren bara såg en bild i taget i snabb följd, vilket resulterade i illusion av rörelse.

Nyligen har människor börjat ta reda på hur man uppnår samma illusion med tredimensionella föremål. Konstnären Gregory Barsamian bygger skulpturer som snurrar runt framför stråljus för att skapa illusion av rörelse. Skulpturerna består av en rad olika föremål i olika stadier av rörelse och strömmarna fungerar som slitsarna i zoetropen för att skapa en illusion av rörelse (Barsamian kan vara bekant för vissa. Gör fans från vår tidigare täckning: Gregory Barsamians Persistence of Vision) .

Pixar plockade nyligen upp tricket för att skapa en fysisk zoetrope för sin lobby. Animatörer var övertygade om att den fysiska zoetropen är en oöverträffad demonstration av animationsprincipen: omvandlingen av en serie stillbilder till rörliga dem:

Så vad är receptet för en fysisk zoetrope? Vi behöver en serie bilder som representerar på varandra följande steg i en rörelse. Då behöver vi förvandla dessa till olika fysiska föremål. När vi har fått dessa behöver vi en mekanism som kan rotera dem runt. Och sist men inte minst, vi behöver ett strobe ljus för att "frysa" varje objekt i en ram av en animering.

Hur kan vi göra det här själva? Tja, för att få den serien av objekt vi kommer att extrahera silhuetter från en bit av video som genereras från Kinect. Vi ska sedan vända dessa silhuetter till en vektorfil som vi kan använda för att styra en laserskärare för att skära ut akrylobjekt i form av varje ram av vår animering.

Låt oss dyka in.

Spela in djupfilmen

Det första du behöver göra är att ladda ner Dan Shiffmans Kinect-bibliotek för bearbetning och placera det i mappen Processing libraries. Om du inte är bekant med hur du gör det, har Dan bra klara instruktioner på Kinects bibliotekssida.

Vi ska använda detta bibliotek för att spela in en djupfilm av Kinect. (I teorin kan du kanske också använda en vanlig kamera och ett väl upplyst rum, men hur kul skulle det vara?) Tack och lov är det bara några få kodar som spelas in från Kinect-exemplet som skickas med bearbetningsbiblioteket:

Diskussion

Låt oss prata igenom hur det här fungerar. Först, vi inkluderar Kinect biblioteket och Processing videobiblioteket; vi behöver det senare för att spela in en film. Då förklarar vi Kinect och MovieMaker-objekt. MovieMaker är det föremål som ska göra jobbet för att spela in skissens resultat i en filmfil.

I inställningen ställer vi in ​​bildfrekvensen till 24 så att den kommer att matcha filmen vi spelar in. Vi konfigurerar också skissen för att vara 640 med 480 för att matcha storleken på videobilden som kommer att komma in från Kinect. Vi gör några grundläggande Kinect-inställningar: berätta för vårt kinect-objekt att börja läsa data från enheten och aktivera djupbilden. Sedan initierar vi MovieMaker-klassen, vilket ger en kvalitetsinställning, en filtyp och ett filnamn. Du kan läsa mer om hur MovieMaker fungerar i Processing dokumentation. Det är viktigt att bildfrekvensen vi övergår till MovieMaker matchar den i skissen så att vår film spelas upp i rätt takt.

Vår rita funktion är oerhört enkel. Allt vi gör är att ringa kinect.getDepthImage () och dra utdata av det till vår skiss med hjälp av Processing image () -funktionen. Det visar oss gråskalebilden som representerar djupkartan som Kinect extraherar från scenen. Detta kommer att bli en svartvitt bild där gråfärgningen av varje pixel inte motsvarar objektets färg, men hur långt bort från Kinect. Närmare objekt kommer att ha ljusare pixlar och längre bort objekt kommer att bli mörkare. Senare kan vi bearbeta dessa pixlar för att välja ut objekt på ett visst djup för vår siluett.

Nu när vi har ritat djupbilden på skärmen är allt vi behöver göra för att fånga resultatet till en ny ram av filmen vi spelar in (mm.addFrame ()). Den sista signifikanta detaljerna i skissen är att vi använder nyckelhändelser för att ge oss ett sätt att stoppa och slutföra filmen. När någon träffar mellanslagstoppet stoppar filmen inspelningen och sparar filen. Också, vi måste komma ihåg att göra lite Kinect-rengöring vid avfarten, annars kommer vi att få några skonsamma fel när vi slutar vår skiss.

Här är ett exempel på vad en film inspelad med den här skissen ser ut som:

Nu, om du inte har en Kinect, eller har du problem med att spela in en djupfilm, förtvivla inte! Du kan fortfarande spela tillsammans med nästa steg. Du kan ladda ner den djupfilmen av mig som gör jumping jacks direkt från Vimeo: Kinect Test Movie för Laser Zoetrope. Jag har också laddat upp djupfilmen som jag använde för den sista laser zoetropen som visas ovan om du vill följa med exakt: Kinect Depth Test Movie. Den senare filmen presenterar Zach Lieberman, en artist och hacker i New York och en av co-grundarna av OpenFrameworks, en C ++-baserad kusin av Processing.

Skapa laserskärningsfilen

Nu när vi har en djupfilm, måste vi skriva en annan bearbetningsschetch som behandlar den här filmen, väljer ramar för vår animering, finner skisserna i vår figur och sparar en vektorfil som vi kan skicka till laserskäraren.

För att uppnå dessa saker ska vi använda Processing OpenCV-biblioteket och Processing's inbyggda beginRaw () -funktion. Skapa en ny bearbetningsskissa, spara den, skapa en "data" -mapp i skissmappen, flytta din djupfilm in där (heter "test_movie.mov") och klistra in följande källkod i din skiss (eller hämta den från lasercut_zoetrope_generator.pde-filen):

Diskussion

Om du kör den här skissen med den andra testfilmen jag länkade ovanför, kommer den att producera följande utdata:

... och kommer även att spara en fil som heter "full_output.dxf" i sketch-mappen. Detta är den vektorfil som vi kan ta med i Illustrator eller något annat designprogram för slutbehandling för att skicka till laserskäraren.

Nu ska vi titta på koden.

Under installationen laddar vi filen test_movie.mov i OpenCV, något som borde vara bekant med tidigare inlägg på OpenCV. Vi kallar också startRaw (), en bearbetningsfunktion för att skapa vektorfiler. startRaw () kommer att göra att vår skiss kan spela in hela sin produktion i en ny vektorfil tills vi kallar endRaw (), så att vi kan bygga upp filen över flera iterationer av dragslingan. I det här fallet skapar vi en DXF-fil i stället för en PDF, eftersom det här formatet är lättare att bearbeta för lasern, som behöver kontinuerliga linjer för att kunna producera en pålitlig produktion. PDF-filer som produceras av bearbetning tenderar att ha många diskreta linjesegment som kan orsaka funky resultat när det skärs med lasern, inklusive långsammare jobb och ojämn tjocklek.

Nu, innan vi dyker in i teckningsmetoden, lite om tillvägagångssättet. Vi vill dra ut 12 olika ramar från vår film, det skulle göra bra ramar för vår animering. Då vill vi ha OpenCV extrahera sina konturer (eller "kontur" i OpenCV parlance) och slutligen vill vi rita dem i ett rutnät över skärmen så att de inte överlappar varandra och den slutliga DXF-filen kommer att innehålla alla ramar i animering.

Denna skiss närmar sig dessa problem genom att skapa en "currentFrame" -variabel som definieras utanför dragslingan. Sedan ökas varje variabel av varje variabel, och vi använder den för att göra allt vi behöver: hoppa framåt i filmen, flytta till ett annat område av skissen för att rita osv. Slutligen, när vi en gång har slutade rita alla 12 bilder till skärmen kallar vi "endRaw ()" för att slutföra DXF-filen, precis som vi kallade "mm.finish ()" i den första skissen för att stänga filfilen.

Så, med tanke på den övergripande strukturen, hur ritar vi konturen för varje ram? Låt oss titta på koden:

opencv.jump (0.3 + map (currentFrame * timeBetweenFrames, 0, 9, 0, 1)); opencv.read ();

Detta berättar OpenCV att hoppa framåt i filmen med en viss tid. 0.3 är utgångspunkten för de ramar vi ska ta och det är något jag tänkte ut genom gissning och kontroll. Jag försökte en massa olika värden, kör skissen varje gång och såg vilka ramar jag slutade med och bedöma om de skulle göra en bra animation. "0.3" representerar starttiden i sekunder.

Vi vill att alla våra ramar ska vara jämnt fördelade så att vår animation spelar upp rent. För att uppnå detta lägger vi en ökande mängd till vårt hopp på 0,3 baserat på vilken ram vi är på. När vi har beräknat rätt tid läser vi filmens ram med "opencv.read ()"

De kommande raderna använder modulo-operatören ("%") med nuvarandeFrame-numret för att dra ramarna i ett fyra av tre rutor. Då är det ett enkelt utseende OpenCV-samtal som faktiskt är ganska cool med tanke på sammanhanget:

opencv.threshold (150);

Detta berättar vårt opencv-objekt att platta ramen till en ren svartvitt bild, vilket eliminerar alla nyanser av grått. Det bestämmer vilka delar som ska behållas baserat på det gråtonvärde vi passerar in, 150. Men eftersom gråskalans värden i vår djupbild motsvarar det faktiska fysiska avståndet mellan objekt, innebär det i praktiken att vi har eliminerat allt i bilden längre bort än ett par meter, vilket bara lämnar vårt ämne isolerat i bilden.

Om du använder din egen djupbild ska du experimentera med olika värden här tills du ser en silhuett som bara representerar den figur som du vill fånga i animering.

De närmaste raderna, inslagna mellan samtal till "pushMatrix ()" och "popMatrix ()" är förmodligen de mest förvirrande i skissen. Tack och lov kan vi bryta ner dem i två delar för att förstå dem: flytta och skala vår bild och rita siluetten beräknad av OpenCV.

De tre första raderna i det här avsnittet gör ingenting, men ändrar vår referensram. pushMatrix () och popMatrix () är en märkligt namngiven konvention som gör det väldigt lättare att göra komplicerad ritningskod. Vad vi får göra är att ändra storleken och formen på vår bearbetningsskissa tillfälligt så att vi kan använda samma teckenkod om och om för att rita på olika skalor och på olika delar av skärmen.

pushMatrix (); översätt (x + 20, y); skala (0,2);

Så här fungerar det. Först kallar vi pushMatrix (), vilket innebär att "spara vår plats" så att vi kan hoppa tillbaka till det när vi kallar popMatrix (). Då kallar vi "translate ()" som flyttar oss till en annan del av skissen med hjälp av x- och y-variablerna som vi ställde ovan baserat på vår nuvarande ram. Då kallar vi "scale ()" så att allt annat vi ritar till nästa popMatrix () kommer att vara 20 procent den storlek det normalt skulle vara.

Resultatet av dessa tre linjer är att vi kan göra OpenCV-delen som kommer nästa - beräkning och ritning av konturen - utan att behöva tänka på var på skärmen detta äger rum. Utan pushMatrix skulle vi behöva lägga till våra x- och y-värden till alla våra koordinater och multiplicera alla våra storlekar med 0,2. Detta gör sakerna mycket enklare.

Nu, OpenCV-koden:

Blob [] blobs = opencv.blobs (1, bredd * höjd / 2, 100, sann, OpenCV.MAX_VERTICES * 4); för (int i = 0; i

Den här koden ser verkligen ut komplicerat, men det är inte så illa. Den mest intressanta raden är den första som kallar "opencv.blobs ()". Den funktionen analyserar bilden vi har lagrat och letar efter områden som är kontinuerliga, där alla intilliggande pixlar har samma färg. När det gäller vår exemplarfilm kommer det att finnas exakt en blob och det kommer att ligga runt Zachs silhuett. Vår användning av tröskeln eliminerade allt annat från scenen. Om du använder min andra exemplarfilm eller din egen djupfilm kan du ha flera blobs och det är okej, du hamnar precis med en mer komplicerad vektorfil.

Och när vi kommer ner till det, är det inte heller så att dra på dessa blobs. Vi slår över arrayen av dem och varje blob har en poängmatris inuti den som vi har tillgång till för att skapa vektorer. I grund och botten spelar vi kopplar prickarna: Gå från varje punkt till nästa ritningslinje mellan dem tills vi fyller hela formen.

Och det är allt som finns att generera DXF-filen.

Förberedelser för lasern

Efter att du skapat den här DXF-filen måste du ta med den i Illustrator eller ditt favorit vektorredigeringsprogram för att utföra grundläggande rengöring: gruppera varje ram tillsammans i ett enda objekt, klippa ut de delar av silhuetterna som överlappar rektangeln så att figuren kommer faktiskt att fästas till dess bas etc. Jag har också valt 9 av dessa tolv ramar och sedan duplicerade dem så att jag skulle ha en looping-animering snarare än en som återställde till en startställning. Jag har laddat upp den slutliga Illustrator-filen här för att du ska titta på: contour_animation_for_laser.ai

När vi har fått konturerna, är det sista steget att designa och klippa hjulet som de ska snurra på. Jag förvärvade ett draglager (en slags ingenjörs lata susan) som skulle låta min skiva snurra fritt. Min lager innehöll hål på toppen för att fästa saker på den. Jag mätta avståndet mellan dem och sedan sätta ihop en design för en skiva som kan monteras på lageret och hålla alla animationsramar:

Att få rätt storlek för slitsarna så att silhuetterna skulle passa in i tätt utan lim tog lite experiment och några falska startar på lasern. Du kan ladda ner Illustrator-filen för den här designen här: contour_disc_for_laser.ai

När du har fått de två Illustrator-filerna är det nästan lika lätt att klippa ut dem på lasern som att trycka på. Bokstavligen: du startar faktiskt processen genom att trycka i Illustrator. Du måste fylla i några ytterligare detaljer om laserns kraft- och hastighetsinställningar, men då går du till raserna. Lasern ser ut så här i åtgärd (inte klippa en zoetrope del i det här fallet, men det är samma laser):

Förhoppningsvis har denna handledning gett dig tillräckligt med vad du behöver för att börja spela in Kinect-djupdata och använda den för att generera laserskärbara vektorfiler. Ha så kul!

Få dina egna laserskärda zoetropes!

Som svar på alla de stora reaktionerna på detta projekt har jag startat en webbplats för att faktiskt producera laserskärda zoetropes för inköp: PhysicalGIF.com. Vi erbjuder kits för att sätta samman sötnoser från designer-made animerade GIF-filer. Kitsna kommer med allt du behöver för att montera en zoetrope som den som visas här: de laserskärna delarna, basen, även strobbelysningen. Så småningom kan du även ladda upp dina egna GIF-filer så att de kan omvandlas till fysisk form. Huvud där borta för att anmäla dig för att bli underrättad när kitsna blir tillgängliga.

Mer: Kolla in alla Codebox-kolumnerna här Besök vår Make: Arduino-sida för mer om den här hobby mikrokontrollern

I Maker Shed:

Komma igång med bearbetning Lär dig programmering på ett enkelt sätt med Behandling, ett enkelt språk som låter dig använda kod för att skapa ritningar, animationer och interaktiv grafik. Programmeringskurser börjar vanligtvis med teorin, men den här boken kan du hoppa direkt in i kreativa och roliga projekt. Det är perfekt för alla som vill lära sig grundläggande programmering, och fungerar som en enkel introduktion till grafik för personer med vissa programmeringsförmåga.

Del

Lämna En Kommentar