Tehtävä 3 – Kuva

Kolmannnen kierroksen tehtävän tavoitteena oli ohjelmoida kourallinen suotimia joilla kuvia voisi muokata. Itse käyttöliittymä oli annettu valmiiksi, mutta molemmat suodintyypit (PixelFilter ja FourierFilter) piti viimeistellä itse annetun rungon päälle. Tässä tehtävässä hyödynnettiin ensimmäistä kertaa abstrakteja luokkia; näin kumpaakin suodintyyppiä voitiin käyttää samalla tavalla, vaikka niiden sisäinen toteutus olikin aivan erilainen.

Auringonkukka

Kohdekuvamme

PixelFilter muokkasi kuvia annetun matriisin perusteella. Tämä oli varsin mielenkiintoista, sillä oikeasti selvisi “miten matriisilla kerrotaan pikseleitä” ja myös miten monia tuttuja suotimia voi ylipäätään toteuttaa! Todennäköisesti moniin suotimiin on kuitenkin monia eri tapoja, varmasti tehokkaampiakin, mutta tässä pääsi hyvin alkuun. Alla on esimerkkejä muutamista eri efekteistä, osa poimittu GIMPin ohjemateriaalista matriiseja koskien.

PixelFilter-suotimia

Vasemmalta oikealle: sumennus (blur), tarkennus (sharpen), reunanlöytö (edge detect) ja kohokuva (emboss)

Itse vedin PixelFilterin toteutuksen vähän överiksi ja tein oman MatrixFilter -luokan, joka sisälsi kaiken tarvittavan tiedon matriisista: sen nimen, itse matriisin, loppukertoimen, kohdekanavien (R, G, B) ja totuusarvon määrittämään kummassa väriavaruudessa kuvaa suodittiin – RGB:ssä vai HSV:ssä. Lopputuloksena ei tarvinnut kuin lisätä uusi instanssi listaan ja rivi pixelfilter.txt:hen niin operateFilter teki taikansa ilman muuta säätöä. Lisäksi kiersin tehtävänannossa mainitun reunaongelman teeskentelemällä kuvan jatkuvan samana reunojen yli.

FFT

Puolitiehen jätetty kuva, joka on käynyt kerran FFT:n läpi ja saanut reunojensiirron

FourierFilter muokkasi kuvia taajuusavaruudessa, ja se oli aivan yhtä selkeä asia kuin miltä kuullostaakin. Onneksi FFT-muunnoksia ei tarvinnut itse keksiä, vaan ohjeita orjallisesti noudattaen lopuksi voi huomata koodin toimivankin vaikka teoria menisikin ohitse. Yllä oleva kuva ei tietenkään ole riittävä alkuperäisen auringonkukan muodostamiseen, sillä FastFourierTransformin jälkeen kuva on jaettu kahteen osaan: reaali- sekä imaginääriosaan. FourierFilter käytännössä kertoo FFTn jälkeisiä arvoja (reaali ja imaginääri) ja sitten muuntaa kuvan takaisin ymmärrettävään muotoon.

Esimerkkejä taajuusavaruuden suotimista:

Notch

Notch

Lowpass

Lowpass

Highpass

Highpass

Ring

Ring

Bonus! Aaltokuvio joka syntyi kirjoitusvirheen ansiosta. Maskia ei voi näyttää, sillä se ylitti reaalimaailman rajat.

Waves

Waves

Tehtävässä oli myös ongelma ratkottavana: kuinka puhdistaa “vahingoittunutta” kuvaa taajuusavaruuden avuin.

Image

Virheellinen kuva taajuusaravuutensa kanssa.

Itse sain jotain aikaan poistamalla pikselirykelmiä, jotka selkeästi poikkesivat alkuperäisestä kuvasta.

Korjailtu kuva

Korjailtu kuva

Kun tehtävän haastavuudesta selvisi, oli se yllättävän hauska; kuvia sai muokattua ihan itse monilla eri tavoin ja tulevaisuudessa muistan kyllä miten matriisifiltterit toimivat.

Tehtävä 5 – Tietokonegrafiikka ja vuorovaikutus (Sokoban)

Kurssin viidennen kierroksen harjoitustyön aiheena oli ohjelmoida yksinkertaistettu versio klassikoksi muodostuneesta Sokoban-ongelmanratkaisupelistä. Sokobanissa pelaaja siirtelee laatikoita tai muita vastaavia esteitä ennalta määrättyihin paikkoihin, ja pelin läpäistäkseen pelaajan on saatava siirrettyä kaikki laatikot merkityille paikoille. Pelin osatehtävien ratkaisemiseksi pelaajan täytyy aina miettiä, millaisia seurauksia tietyn esteen siirtämisellä on jatkoa ajatellen.

Scala-muotoinen ohjelmointitehtävämme oli tosiaankin karsittu versio alkuperäisestä esikuvastaan, sillä meidän versiossamme pelin osatehtävien läpäisemiseksi riitti ainoastaan esteiden siirteleminen pois kulkuväylältä siten, että pelaaja pääse maaliin. Tämän kertainen koodaustehtävä oli täysin erilainen kuin edellisen kierroksen kuvasuotimien kanssa puuhastelu. Kuvakoodauksen jäljiltä minulla, jolla ei ennen Otaniemeen ja Tikille tuloa ollut mitään aikaisempaa kokemusta ohjelmoinnista, oli varsin kaksijakoiset tuntemukset ohjelmoinnista. Toisaalta oli onnistunut fiilis, kun olin saanut aluksi ylipääsemättömän vaikealta tuntuneen tehtävän kunnialla tehtyä. Toisaalta oli vähän sekavat tuntemukset siitä, että tälläistako ohjelmointi sitten ylipäätänsä tulee olemaan, sekavaa ja raskasta. Onneksi tämän viidennen kierroksen Sokoban oli lähempänä sitä, mitä olin ehkä jonkin verran ohjelmoinnilta odottanutkin, vaikka sisältyyhän koodaamiseen paljon muutakin kuin graafisten käyttöliittymien parissa työskentelyä.

Tämän kertaisen näpyttelyn tehtävänanto oli jo mielestäni sitä tasoa, jota voisi ohjeistukselta odottaakin; selkeä ja kattava. Lisäksi kierrokseen liittyvän OLO-tapaamisen virike antoi perustietoa piirtämiseen tarkoitetun Graphics2D-luokan käytöstä, jota hyödynnettiin Sokobanin graafisen ilmeen muodostamisessa. Ohjelmointi 1 -kurssilla graafisia käyttöliittymiä oli jo aikaisemmin sivuutettu hiukan, mutta sen enemmin niistä tiedoista ei tässä tehtävässä ollut apua.  Näillä perusteilla ryhdyin tuottamaan tehtävän vaatimaa koodia ja aluksi tehtävä tuntuikin haastavalta,  mutta kun aiheesta sai kunnon otteen ja teki omaa etsivän työtä, alkoi koodia syntyä tasaiseen tahtiin. Tehtävän suorittamisen kannalta varmasti eniten haasteita tuotti siis se, että Scalan Swing-kirjaston oma dokumentaatio ei näin aloittelevan ohjelmoijan näkökulmasta ole se kaikkein selkein ja yksinkertaisin opas. Pulmien ratkaisemiseksi jouduinkin käyttämään huomattavan paljon Googlea apunani, jotta Eclipsen antamista virheviesteistä päästiin eroon. Assareiden antamaa apua ei pidä sitäkään yhtään vähätellä, sillä ilman heitä tämän kurssin suorittamisesta ei yksinkertaisesti tulisi yhtään mitään.  Tehtävä itsessään ei siis loppujen lopuksi ollut edes kovin vaikea, mutta ensikertalaisen alkuhaparoinnit tuottivat aluksi mutkia matkaan eikä tehtävänannon kuvan mukaiselta reaktioltakaan voitu välttyä, mutta onneksi virheet oli helposti löydettävissä ja korjattavissa. Suurimmat ongelmat johtuivatkin siis Swing-kirjaston dokumentaation auttamattomasta köyhyydestä, tai ehkä en vain osannut hyödyntää sen kaikkea potentiaalia.

Tehtävän palauttamisen jälkeen miettiessäni, mitä kierroksesta oli jäänyt käteen, pystyin rehellisesti toteamaan, että aika paljonkin. Opin nimittäin käyttämään Graphics2D:tä ja Scalan Swingiä perustasolla, ja näitähän tullaan hyödyntämään myös kurssin seuraavalla ja samalla myös kurssin päättävällä ohjelmointikierroksella, jossa koodataaan yhteensä kolme pientä peliä ja omien mieltymysten sekä taitojen mukaan myös lisäpelejä.

Tässä vielä lopuksi kuvakaappaus omasta tuotoksestani tältä harjoituskierrokselta. Aikaansaannokseni ei kuitenkaan ole se kaikkein kaunein monipuolisin toteutus, jonka tehtävänannon puitteissa olisi voinut koodata, mutta aikaa on aina rajallinen määrä ja haasteiden tuomat viivästykset syövät sitä yleensä yllättävän paljon. Nyt voin kuitenkin luottavaisin mielin jatkaa kohti finaalikierrosta. 🙂

Kierros 5 - Sokoban


Markus Pajari