T-111.350 Multimediatekniikka

Laboratoriotyö - Raportti

Kuvien kompressointi

Matti Jokela, 49469P, mkjokela@cc.hut.fi
Vesa Jokinen, 44189S, vjokinen@cc.hut.fi

0. Sisällysluettelo

1. Johdanto
    1.1 Esiselvitys
        1.1.1 JPEG
        1.1.2 JPEG2000
        1.1.3 GIF
        1.1.4 PNG
        1.1.5 TIFF (Deflate)
        1.1.6 Image Soft Wavelet pakkain
    1.2 Työsuunnitelma
        1.2.1 Mitä tehdään?
        1.2.2 Työkalut
2. Työn vaiheet
    2.1 Työkalut
    2.2 Testimenetelmät
3. Tulokset ja havainnot
    3.1 lena.tif
        3.1.1 Pakkaussuhteet häviöttömillä algoritmeilla
        3.1.2 Matemaattinen analyysi
        3.1.3 Silmämääräinen analyysi
    3.2 frymire1.tif
        3.2.1 Pakkaussuhteet häviöttömillä algoritmeilla
        3.2.2 Matemaattinen analyysi
        3.2.3 Silmämääräinen analyysi
    3.3 circles.tif
        3.3.1 Pakkaussuhteet häviöttömillä algoritmeilla
        3.3.2 Matemaattinen analyysi
        3.3.3 Silmämääräinen analyysi
    3.4 ptt5_1.tif
        3.4.1 Pakkaussuhteet häviöttömillä algoritmeilla
        3.4.2 Matemaattinen analyysi
        3.4.3 Silmämääräinen analyysi
4. Kommentteja
5. Lähteet
6. Liitteet
    6.1 Päätestin Python -skripti
    6.2 Python -skriptin lokitiedosto

1. Johdanto

1.1 Esiselvitys

Työn tarkoituksena on vertailla erilaisia kuvanpakkausalgoritmeja erilaisille kuville. Valitsimme testattaviksi algoritmeiksi yleisesti käytetyt algoritmit JPEG, GIF, TIFF (deflate) ja PNG sekä JPEG:n uudemman tulokkaan JPEG2000. Erikoisemmaksi algoritmiksi valitsimme Image Soft Oy:n [9] valmistaman wavelet pohjaisen kuvanpakkausalgoritmin, jota ei ole yleisesti saatavissa. Tulemme testaamaan ainakin kuvan laatua ja kuvien kokoa eri pakkausasetuksilla. Kuvan laatua testataan sekä matemaattisin menetelmin että silmämääräisesti. Teemme testit eri tyylisillä kuvilla kuten valokuvat, vektorigrafiikka ja tekstistä otettu kuvakaappaus. Varsinaista vektorigrafiikkaformaattia ei tulla työssä käyttämään vaan kaikki kuvat ovat rasterimuodossa.

1.1.1 Tekniikka ja teoria

1.1.1.1 JPEG

JPEG (Joint Photographic Experts Group) on yleisesti käytössä oleva häviöllinen kuvanpakkausstandardi. Se perustuu DCT (diskreetti kosini muunnos) muunnokseen ja pakkaa kuvan YUV tasossa kuten monet muutkin kuvanpakkausalgoritmit. JPEG osaa pakata 8-bittisiä harmaasävykuvia ja 24-bittisiä värikuvia. Se soveltuu teoriassa hyvin luonnollisten kuvien pakkaukseen, mutta sarjakuvamainen ja vektorigrafiikka pakkautuu huonommin. JPEG aiheuttaa suurilla pakkaussuhteilla kuvaan suttuisuutta, joka haittaa etenkin vektorigrafiikan pakkauksessa. JPEG:n pakkaama kuva pakkautuu tyypillisesti 10-100 osaan alkuperäisen kuvan koosta riippuen määritellystä pakkaussuhteesta. JPEG aiheuttaa kuviin suurella pakkaussuhteella myös ns. mosaiikkimaisuutta, joka ilmenee myös luonnollisilla kuvilla. Mosaiikkimaisuus johtuu JPEG:n käyttämistä 16x16, 16x8 ja 8x8 [2] pikselin kokoisista paloista, joissa se pakkaa dataa. JPEG:stä on kehitetty myös erillinen häviötön kuvanpakkausalgoritmi nimeltään JPEG-LS, mutta emme käsittele sitä tässä työssä.

1.1.1.2 JPEG2000

JPEG2000 on uusi wavelet tekniikkaan pohjautuva häviöllinen kuvanpakkausstandardi. Se ei ole vielä juurikaan käytössä, mutta sen käyttämiseen löytyy ainakin komentorivipohjaisia sovelluksia [3]. Wavelet tekniikkaan pohjautuvan kuvanpakkausmenetelmän pitäisi teoriassa pystyä pakkaamaan kuvaa paremmin kuin DCT pohjaiset algoritmit, joita esim. perinteinen JPEG käyttää. JPEG2000:n ei pitäisi aiheuttaa kuvaan mosaiikkimaisuutta, koska se pakkaa kuvan kokonaisena. Sen käyttäytymisestä vektorigrafiikalla emme vielä tiedä, mutta veikkaisimme siihen ilmenevän saman tyylistä suttuisuutta kuin JPEG:llä. JPEG2000 osaa pakata ainakin 24-bittisiä värikuvia ja ilmeisesti 8-bittisiä harmaasävykuvia. Myös JPEG2000 tukee säädettävää pakkaussuhdetta.

1.1.1.3 GIF

GIF (Graphics Interchange Format) pakkaa kuvan käyttäen Unisys:n patentoimaa LZW algoritmia. Se tukee ainakin 8 ja 4-bittisiä värisyvyyksiä. 24-bittistä versiota ei saatu koskaan valmiiksi [5]. Pienen värimäärän ja LZW pakkauksen ansiosta GIF soveltuu hyvin vektorigrafiikan pakkaamiseen. Etenkin kuvat, joissa on paljon samaa väriä tasaisena pintana, pakkautuvat hyvin.Sen sijaan luonnollisten kuvien pakkaamiseen se soveltuu huonosti. GIF on häviötön pakkausmenetelmä.

1.1.1.4 PNG

PNG (Portable Network Graphics) on uudehko kuvaformaatti, jonka odotetaan korvaavan patentin alainen GIF. GIF:n tavoin myös PNG on häviötön pakkausmenetelmä. PNG käyttää pakkauksessa RLE (Run Length Encoding) algoritmia ja pakkaa tyypillisesti kuvan paremmin kuin GIF. Se tukee värisyvyyksiä 1-48 bittiä [5]. PNG pakkaa GIF:n tavoin hyvin vektorigrafiikkaa. Luonnolliset kuvat se pakkaa huonosti, koska niistä ei yleensä löydy juurikaan RLE algoritmin vaatimia tasaisia, yhtenäisiä väripintoja.

1.1.1.5 TIFF (Deflate)

TIFF kuvaformaatti tukee useampia pakkausalgoritmeja, kuten JPEG. Tässä työssä käytämme ainoastaan TIFF:n häviötöntä deflate kuvanpakkausalgoritmia, jota mm. kuuluisa ZIP pakkausalgoritmi käyttää. TIFF tukee 24-bittistä värisyvyyttä. Deflate algoritmi pakkaa teoriassa hyvin tasaisia väripintoja ja huonommin tavallisia valokuvia. Periaatteessa kuvassa olevat toistuvuudet pakkautuvat myös hyvin, kuten tiheä shakkiruudukko. Valokuvat pakkautuvat tyypillisesti muutamia kymmeniä prosentteja ja vektorigrafiikka saattaa pakkautua jopa murto-osaan alkuperäisestä.

1.1.1.6 Image Soft Wavelet pakkain

Käytämme vertailukohtana itse Image Soft Oy:ssä kehittämäämme wavelet pohjaista pakkausalgoritmia. Pakkain tukee 24-bittisiä täysvärikuvia sekä 8-bittisiä harmaasävykuvia. Algoritmi on suunniteltu pakkaamaan hyvin luonnollisia kuvia, joten vektorigrafiikalla tulee samantyylistä suttuisuutta kuten JPEG:llä. Algoritmi tukee useita eri pakkauslaatuja, myös eri kuvan osia pystytään pakkaamaan eri laadulla. Tässä työssä käytetään samaa pakkauslaatua koko kuvalle. Pakkain käsittelee kuvan YUV formaatissa, mutta se osaa sisäisesti muuttaa kuvan RGB ja YUV väriformaattien välillä. Mosaiikkimaisuutta ei kuvissa ilmene, koska kuvaa ei paloitella pakkausvaiheessa.

1.1.2 Väriformaatit

1.1.2.1 RGB

RGB väriformaatti on tyypillisimmin käytetty kuvien esityksessä täysvärimuodossa. Jokainen väri (punainen, vihreä ja sininen) ilmaistaan 8-bittisenä lukuna, joten RGB väriarvo on tyypillisesti 24-bitin kokoinen.

1.1.2.2 YUV

Monet häviölliset valokuvien pakkaukseeen suunnitellut algoritmit käsittelevät kuvan sisäisesti YUV väriformaatissa. Koska ihmissilmä on herkempi kuvan luminanssille kuin krominanssille, voidaan krominassi esittää pienemmällä tarkkuudella ja täten säästetään tilaa. YUV väriformaatissa Y vastaa luminanssia ja U/V krominanssia.

1.2 Työsuunnitelma

Työn tarkoituksena on vertailla erilaisia kuvanpakkausalgoritmeja erilaisille kuville. Valitsimme testattaviksi algoritmeiksi yleisesti käytetyt algoritmit JPEG, GIF, TIFF (Deflate) ja PNG sekä JPEG:n uudemman tulokkaan JPEG2000. Erikoisemmaksi algoritmiksi valitsimme Image Soft Oy:n [9] valmistaman wavelet pohjaisen kuvanpakkausalgoritmin, jota ei ole yleisesti saatavissa. Tulemme testaamaan ainakin kuvan laatua ja kuvien kokoa eri pakkausasetuksilla. Kuvan laatua testataan sekä matemaattisin menetelmin että silmämääräisesti.
 

1.2.1 Mitä tehdään?

Tulemme testaamaan eri kuvanpakkausalgoritmien tehokkuuksia erilaisilla kuvilla. Valitsimme testikuviksi kaksi värikuvaa, yhden vektorigrafiikka kuvan ja yhden kirjasta otetun kuvakaappauksen. Varsinaista vektorigrafiikkaformaattia ei tulla työssä käyttämään vaan kaikki kuvat ovat rasterimuodossa. Osaa kuvista on muokattu jotta saamme ne pakkautumaan myös Image Soft Oy:n wavelet algoritmilla, sillä se vaatii kuvan dimensioiden olevan jaollisia miellellään vähintään 32:lla.

Aiomme tehdä testit enimmäkseen Python [12] ohjelmointikieltä käyttäen. Kirjoitamme yksinkertaisen skriptin joka konvertoi alkuperäisen TIFF kuvan eri pakkausformaatteihin ja laskee niille hyvyysluvun, tiedoston koon sekä pakkaussuhteen muodossa bittejä pikseliä kohden. Tuloksista aiomme piirtää kaavioita, joista selviää eri pakkausalgoritmien toimivuus eri kuvilla ja eri pakkaussuhteilla. Lisäksi arvioimme häviöllisillä pakkausalgoritmeilla pakattujen kuvien ulkonäön silmämääräisesti.
 

1.2.1.1 lena.tif - [8]

Valitsimme lenan yhdeksi testikuvaksemme koska se on yleisesti kuvanpakkausalgoritmien testauksessa käytetty valokuva. Lena on 512x512 kokoinen 24-bittinen täysvärikuva. Siinä on tasaisia väripintoja, mutta myös tarkkoja yksityiskohtia. Väriskaala on hiukan suppea.


 

1.2.1.2 frymire1.tif - [8]

Frymire1 on hiukan alkuperäisestä frymire.tiff:stä muokattu versio. Leikkasimme kuvan laidoista muutamia pikseleitä pois, jotta saimme kuvan kooksi 1024x1024 pikseliä. Frymire on mielestämme hyvä testikuva häviöllisille kuvanpakkausalgoritmeille, koska siinä on laaja väriskaala ja runsaasti yksityiskohtia joita silmä ei välttämättä erota. Frymire on 24-bittinen täysvärikuva.


 

1.2.1.3 circles.tif - [8]

Circles on 256x256 pikselin kokoinen 8-bittinen harmaasävykuva. Kuvassa on vähän värejä ja paljon tasaisia väripintoja, joten oletamme häviöttömien kuvanpakkausalgoritmien toimivan hyvin. Häviöllisissä algoritmeissa tullee hyvin esille reunojen pehmentyminen ja mosaiikkimaisuus.
 


 

1.2.1.4 ptt5_1.tif - [7]

Ptt5_1.tif on muokattu alkuperäisestä ptt5.tiff:stä siten että alalaidasta on poistettu muutamia pikseleitä, joten kuvan koko on 2368x1728 pikseliä. Kuvassa on tekstiä ja muutamia mustavalkoisia piirroksia. Kuvan väriskaala on siis suppea ja teräviä reunoja on paljon. Oletamme häviöttömien pakkausalgoritmien toimivan hyvin tälle kuvalle. Häviöllisten algoritmien ennustamme tuottavan suttuista tekstiä ja/tai huonomman pakkaussuhteen.


 

1.2.2 Työkalut

Työkaluina aiomme käyttää Python ohjelmointikieltä, sekä siihen saatavaa ilmaista lisäkirjastoa PIL (Python Imaging Library) [13]. Graafien piirtoon käytämme GNUplot:a [14]. Tarvitsemme myös GIMP ohjelmaa [15] silmämääräisen arvion tekemiseen. Lisäksi käytämme Image Soft Oy:n konsolipohjaista wavelet kuvanpakkaus ohjelmaa sekä konsolipohjaista JPEG2000 pakkaussovellusta Kakadu [3].

2. Työn vaiheet

Ensimmäiset työn vaiheet olivat kompressioalgoritmeihin perehtyminen, niiden valinta sekä testikuvien etsiminen. Nämä työnvaiheet on käsitelty Johtanto kappaleen Esiselvitys kohdassa (1.1).  Kappaleessa Tulokset ja havainnot (3) on raportoitu työn tulokset.

2.1 Työkalut

Työkalut ovat osittain listattuina Johdanto kappaleen Työsuunnitelma kohdassa (1.2). Työkaluina käytimme Python ohjelmointikieltä, sekä siihen saatavaa ilmaista lisäkirjastoa PIL (Python Imaging Library). Tämä työkalu valittiin siksi, että meillä oli valmiiksi kokemusta kyseisen kielen/lisäkirjaston käytöstä ja kyseisellä kirjastolla voidaan helposti avata ja tallentaa kuvia haluamiimme formaatteihin (lukuunottamatta wavelet pohjaisia algoritmeja sekä TIFF:n deflate versiota). Pythonin avulla oli myös helppo laskea PSNR-arvot häviöllisesti pakatuille kuville sekä tallentaa tulokset suoraan gnuplot:n [14] tukemaan tiedostomuotoon. Gnuplot:iin päädyimme siksi, että se osaa piirtää käyriä samaan kuvaajaan vaikka niillä olisi eri x-koordinaatti arvot. Se osaa myös tallentaa graafit suoraan käteviksi postscript tiedostoiksi. Silmämääräisen arvion tekemiseen käytimme ilmaista GIMP -sovellusta [15]. Päädyimme tähän Paint Shop Pro:n sijaan siksi että työskentelimme lähinnä SiliconGraphics [16] työasemalla. Lisäksi käytimme Image Soft Oy:n konsolipohjaista wavelet kuvanpakkaus ohjelmaa, joka osasi osasi tallentaa kuvan myös TIFF:n deflate muotoon sekä kerran pakatun ja puretun kuvapuskurin raakadataksi. Päädyimme testaamaan omaa wavelet tekniikkaamme oman ammatillisen mielenkiintomme vuoksi. Päädyimme käyttämään JPEG2000 pakkaussovellusta Kakadu, sillä se on yksi harvoista konsolipohjaisista JPEG2000 sovelluksista joka on ilmaisesti saatavilla. Lisäksi käytimme Netscape Composer -sovellusta [17] tämän dokumentin kirjoittamiseen.

2.2 Testimenetelmät

Testiskripti konvertoi kuvat eri formaatteihin sekä laski niistä pakkaussuhteet ja PSNR-arvot [18]. Tulokset tallennettiin suoraan gnuplot -tiedostoformaattiin, joten saimme helposti tehtyä kuvaajat tuloksista. Kuvat pakattiin ainoastaan kerran häviöttömillä pakkausformaateilla ja häviöllisillä käytimme useita eri pakkaussuhteita. Tämän lisäksi arvioimme muutamia häviöllisten menetelmien pakkaamia kuvia silmämääräisesti. Testeissä käyttämämme testiskriptit löytyvän tämän dokumentin Liitteet kohdasta (6).

3. Tulokset ja havainnot

Vertailimme jokaista testikuvaa erikseen sekä matemaattisin analyysein että silmämääräisesti. Matemaattisessa analyysissa piirsimme käyrän joka näyttää kuvan laskennallista eroa alkuperäiseen mittaavan PSNR (Peak Signal to Noise Ratio) eri pakkaussuhteilla BPP (Bits Per Pixel) kullekin häviölliselle pakkausalgoritmille. PSNR arvot ovat desibeliasteikolla siten että suurempi arvo on parempi. Alle 30 PSNR arvoilla kuva eroaa jo merkittävästi alkuperäisestä, 30-40 arvoilla kuva on riittävän hyvä ja >40 arvoilla ero alkuperäiseen alkaa olla mahdotonta havaita valokuvilla. Silmämääräisen analyysin teimme pakkaamalla testikuvat samalla pakkaussuhteella ja vertailemalla tuloskuvia keskenään. Lisäksi laskimme pakkaussuhteet häviöttömille pakkausalgoritmeille.
 

3.1 lena.tif

3.1.1 Pakkaussuhteet häviöttömillä algoritmeilla

Pakkaussuhteeltaan huonoiten pärjäsi TIFF (deflate), jolla pakkausarvo oli 20.4 BPP. Toiseksi tuli PNG arvolla 15.3 BPP. Parhaiten pärjäsi GIF arvolla 8.6 BPP. Täytyy kuitenkin muistaa, että vaikka GIF on häviötön pakkausmenetelmä, käyttää se 8-bittistä palettia joten se ei pakannut kuvaa oikeastaan ollenkaan. Kuvan laatu on täten GIF:llä huonompi, joten PNG selviytyi voittajaksi.

3.1.2 Matemaattinen analyysi

Lena.tif kuvalla huomaamme JPEG2000 algoritmin tuottavan parhaan kuvanlaadun kullekin pakkauslaadulle. JPEG pärjää toiseksi parhaiten pienellä pakkauksella, mutta enemmän pakattaessa laatu heikkenee nopeasti. ISW näyttää pärjäävän huonosti matemaattisessa analyysissa. Pienellä pakkauksella ISW pärjää huonoiten, mutta ohittaa JPEG:n n. 0.35 BPP kohdalla. Häviöttömät pakkausmenetelmät pärjäsivät niin heikosti, ettei niitä edes näkyisi tässä kuvaajassa.
 

3.1.3 Silmämääräinen analyysi

 

JPEG, 0.20 BPP

JPEG 2000, 0.20 BPP

Image Soft Wavelet, 0.20 BPP

Valitsimme silmämääräistä testiä varten kuvat pakattuna 0.20 BPP arvolla (pakkaussuhde 1:120), sillä vähemmän pakattuina kuvista ei juurikaan huomaa eroa alkuperäiseen. JPEG pärjäsi huonoiten tässä testissä sillä kuvassa näkyy selkeää mosaiikkimaisuutta ja ero alkuperäiseen kuvaan on täten huomattava. Mainittakoon vielä että jouduimme käyttämään JPEG:n maksimaalista pakkausta. JPEG2000 ja ISW olivat nopealla vilkaisulla yhtä hyviä. Tarkemmalla tarkastelulla pientä eroa huomattiin JPEG2000:n eduksi mm. nenän ja silmien terävyydessä.
 

3.2 frymire1.tif

3.2.1 Pakkaussuhteet häviöttömillä algoritmeilla

Pakkaussuhteeltaan huonoiten pärjäsi TIFF (deflate), jolla pakkausarvo oli 9.2 BPP. Toiseksi tuli GIF arvolla 6.6 BPP. Parhaiten pärjäsi PNG arvolla 3.2 BPP. Edelleen täytyy muistaa että GIF:ssä käytettiin 8-bittistä väripalettia.

3.2.2 Matemaattinen analyysi


Myös frymire1.tif:llä huomaamme JPEG2000:n pärjäävään parhaiten matemaattisessa analyysissa. Toisena on ISW marginaalisella erolla ja viimeisenä JPEG edelleen pienellä erolla ISW:n. Suurimmilla pakkausarvoilla, yli 3, huomaamme JPEG:n olevan hiukan parempi kuin ISW. JPEG ei pysty pienempään pakkausarvoon kuin n. 0.40 BPP. Parhain häviöttömistä algoritmeista, eli PNG, pakkasi kuvan pakkausarvolla 3.2 BPP joka on varsin hyvin häviöttömältä pakkausalgoritmilta kun taas häviöllisillä PSNR menee jo alle 30:n.

3.2.3 Silmämääräinen analyysi

 

JPEG, 0.50 BPP

JPEG 2000, 0.50 BPP

Image Soft Wavelet, 0.50 BPP

Valitsimme silmämääräistä testiä varten kuvan pakkausarvolla 0.50 BPP (pakkaussuhde 1:48), sillä näin monipuolisella kuvalla jo siinä näkyy silminnähtäviä eroja. JPEG on silmämääräisesti huonoin. Tämä näkyy esim. ruudussa (2, 4) olevista "kuplista" jotka pakkautuvat paremmin wavelet pohjaisilla algoritmeilla. Kuvassa näkyy myös runsaasti mosaiikkimaisuutta tarkasteltuna oikeassa kuvakoossa 1024x1024 pikseliä. JPEG2000:ssa ja ISW:ssä ei ole juurikaan eroa. Eroja näkyy esimerkiksi ruudussa (8, 7), jossa huomaamme että ISW pakkaa kuvan suttuisemmaksi. Tämä johtunee siitä että ISW on optimoitu pakkaamaan kuvat mahdollisimman nopeasti, jolloin olemme päätyneet käyttämään lyhyempää wavelet aaltoa. Samaa suttuisuutta näkyy myös joissain muissa ruuduissa. Ruutujen (2, 5) ja (3, 5) välisellä rajalla näkyy ISW:llä pientä värin "leviämistä".
 

3.3 circles.tif

3.3.1 Pakkaussuhteet häviöttömillä algoritmeilla

Häviöttömistä algoritmeista huonoiten pärjäsi GIF pakkausarvolla 1.4 BPP. Toiseksi tuli TIFF (deflate) pakkausarvolla 0.9 BPP. Parhaiten myös tässä testissä pärjäsi PNG arvolla 0.5 BPP. Kaikki algoritmit pakkasivat kuvat 8-bittisessä värijärjestelmässä, joten testissä näkyy GIF:n käyttämän LZW algoritmin yleinen heikkous.

3.3.2 Matemaattinen analyysi


Circles.tif kuvalla JPEG2000 vaikuttaisi olevan paras matemaattisessa analyysissa lukuunottamatta kaikista pienintä pakkausarvoa jossa ISW on parempi. JPEG pärjää yleisesti huonoiten kaikilla pakkausarvoilla. Paras häviöttömistä algoritmeista (PNG) pakkasi kuvan 0.5 BPP arvolla.

3.3.3 Silmämääräinen analyysi

 

JPEG, 0.50 BPP

JPEG 2000, 0.50 BPP

Image Soft Wavelet, 0.50 BPP

Valitsimme tähänkin silmämääräiseen testiimme kuvat pakattuna 0.50 BPP pakkausarvolla (pakkaussuhde 1:16) sillä eroja alkuperäiseen näkyi jo näinkin pienellä pakkaussuhteella. JPEG on mielestämme huonoin sillä siinä näkyy selvästi eräänlaista mosaiikkimaisuutta, joka aiheuttaa haamuja terävillä rajoilla. Virhe rajoilla on "terävämpää" kuin wavelet pohjaisilla algoritmeilla ja siten sen huomaa helpommin. Mielestämme ISW on hiukan JPEG2000:sta parempi vaikka matemaattinen analyysi toista väittääkin. ISW:llä pintojen tasaisuus näkyy paremmin ja terävillä reunoilla on vähemmän haamuja. Tässä testissä ISW:n käyttämä lyhyempi aaltomuoto lienee eduksi.
 

3.4 ptt5_1.tif

3.4.1 Pakkaussuhteet häviöttömillä algoritmeilla

Huonoiten pärjäsi jälleen GIF pakkausarvolla 1.1 BPP. Toiseksi tuli TIFF (deflate) pakkausarvolla 0.6 BPP. Jälleen voittajaksi selviytyi PNG ylivoimaisella pakkausarvolla 0.2 BPP.

3.4.2 Matemaattinen analyysi


Ptt5_1.tif kuvalla JPEG2000 on selvästi paras matemaattisin menetelmin analysoituna. ISW tulee toiseksi ja JPEG jää viimeiseksi. Paras häviöttömistä pakkausmenetelmistä (PNG) pakkasi kuvan pakkausarvolla 0.2 BPP, joka on huomattavan hyvä verrattuna häviöllisiin joilla PSNR menee jo alle 30:n. Tässä testissä häviöttömät menetelmät siis pärjäsivät oletusten mukaisesti.
 

3.4.3 Silmämääräinen analyysi

 

JPEG, 0.20 BPP

JPEG 2000, 0.20 BPP

Image Soft Wavelet, 0.20 BPP
Suurennos kuvasta ptt5_1.tif

Valitsimme silmämääräiseen testiimme kuvan pakattuna pakkausarvolla 0.20 BPP (pakkaussuhde 1:40), sillä me emme havainneet pienemmällä pakkaussuhteella pakatuissa kuvissa suurta eroa. Tosin JPEG ei aivan päässyt 0.20 BPP pakkausarvoon suurimmallakaan pakkauksella. Myös tässä testissä JPEG pärjää mielestämme huonoiten johtuen samoista ilmiöistä kuin circles.tif:n kanssa eli reunoille ilmestyy teräviä haamuja. Myös wavelet algoritmeilla kuvaan ilmestyy haamuja, mutta mielestämme se on selkeämmin havaittavissa JPEG2000:ssa. ISW näyttäisi myös tällä kertaa pärjäävän paremmin lyhyen wavelet aaltomuotonsa ansiosta. Tämä kuva olisi PNG:llä pakattuna täysin alkuperäisen veroinen.
 

4. Kommentteja

Työtä tehdessämme kohtasimme muutamia ennalta arvaamattomia ongelmia. PIL (Python Imaging Library) ei tukenutkaan TIFF:n deflate pakkausalgoritmia joten jouduimme toteuttamaan sen itse käyttäen TIFF-kirjastoa [19]. TIFF kirjastoa käyttäessämme kohtasimme myös toisen ongelman: PIL ei osannutkaan avata kirjastolla tallennettuja pakkaamattomiakaan TIFF-kuvia, joten jouduimme tallentamaan ne raakadatana suoraan levylle. Tämän seurauksena jouduimme toteuttamaan raakadatakuvien lukemisen Pythonille.

Aihe oli sinäänsä mielekäs, että pääsimme vertailemaan itse toteuttamaamme wavelet algoritmia muihin algoritmeihin eri tyylisillä kuvilla. Harjoitystyö oli mielestämme työmäärältään kohtuullinen.

Odotusten mukaisesti wavelet pohjaiset algoritmit pärjäsivät hyvin. Yllätyimme PNG:n tehokkuudesta häviöttömänä algoritmina verrattuna TIFF:n deflate versioon ja GIF:iin. GIF on hyvin suosittu kuvaformaatti Internetissä. Mielestämme GIF:n käytöstä kannattaisi siirtyä PNG:n jo pelkästään sen tehokkuuden vuoksi. Lisäksi saamme 24-bittiset värit ja PNG:n käyttöä ei "varjosta" patenttikysymykset.
 

5. Lähteet

[1] http://www.jpeg.org/
[2] http://www.faqs.org/faqs/jpeg-faq/part1/
[3] http://www.jpeg.org/jpeg2000/
[4] http://www.faqs.org/faqs/compression-faq/part1/
[5] http://www.faqs.org/faqs/graphics/fileformats-faq/
[6] http://www.cs.und.edu/~mschroed/jpeg.html
[7] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/project/sensor-9/ftp/images/stills/mono/
[8] ftp://links.uwaterloo.ca/pub/BragZone/
[9] http://www.imagesoft.fi/
[10] http://www.microsoft.com/
[11] http://www.jasc.com/
[12] http://www.python.org/
[13] http://www.pythonware.com/products/pil/
[14] http://www.gnuplot.info/
[15] http://www.gimp.org/
[16] http://www.sgi.com/
[17] http://www.netscape.com/
[18] http://cray.tuug.fi/~phoenix/research/gradu/node24.html
[19] http://www.libtiff.org/

 

6. Liitteet

6.1 Päätestin Python -skripti

#!/usr/bin/python

import time, os, string, sys, math, commands, os.path
import Image, ImageStat, ImageChops
 

sKakaduCompress = 'kakadu/kdu_compress'
sKakaduExpand = 'kakadu/kdu_expand'
sIsWaveletCompress = 'wavelet/ConsoleWavelet'
sGnuplot = '/usr/bin/gnuplot'

lLossyFileFormats = [ 'JPEG', 'JPEG2000', 'ISW' ]
lLosslessFileFormats = [ 'TIFF', 'PNG', 'GIF' ]

dlQualities = { 'JPEG': [ 90, 80, 70, 60, 50, 40, 30, 20, 10, 1 ],
                'JPEG2000': [ 2.0, 1.5, 1.0, 0.7, 0.5, 0.3, 0.2, 0.15, 0.1, 0.05 ],
                'ISW': [ 90, 80, 75, 70, 60, 50, 45 ] }

lImageFiles = [ 'kuvat/lena.tif', 'kuvat/frymire1.tif',
                'kuvat/circles.tif', 'kuvat/ptt5_1.tif' ]
 

def GetFileSize(sFilename):
    return os.stat(sFilename)[6]
 

def ReadRaw(nWidth, nHeight, sMode, sFilename):
    return Image.fromstring(sMode, ( nWidth, nHeight), open(sFilename).read()).convert('RGB')
 

def GetPSNR(sFile1, sFile2):
    im1 = Image.open(sFile1).convert("RGB")

    im2 = sFile2
    if type(sFile2) == type(''):
        im2 = Image.open(sFile2).convert("RGB")

    im2 = ImageChops.subtract(im1, im2, 1.0, 0.0)

    stat = ImageStat.Stat(im2)
    nPixels = 0
    nSum2 = 0

    for i in range(len(stat.sum2)):
        nPixels = nPixels + stat.count[i]
        nSum2 = nSum2 + stat.sum2[i]

    nMSE = nSum2 / nPixels
    if nMSE < 0.01:
        nMSE = 0.001

    return -10.0 * math.log10(nMSE / 255.0 / 255.0)
 

def CompressTiffDeflate(sSrcFilename, sDstFilename):
    im = Image.open(sSrcFilename).convert("RGB")

    sCmd = sIsWaveletCompress + ' ' + sSrcFilename + ' ' + sDstFilename + ' 100'
    commands.getstatusoutput(sCmd)

    nPSNR = GetPSNR(sSrcFilename, sSrcFilename)
    nBPP = GetFileSize(sDstFilename + '_2.tif') * 8.0 / (float) (im.size[0] * im.size[1])
    return [ nBPP, nPSNR ]
 

def CompressJPEG2000(sSrcFilename, sDstFilename, nQuality):
    im = Image.open(sSrcFilename)
    im.save('temp.bmp')
    im = im.convert('RGB')

    sCmd = sKakaduCompress + ' -i temp.bmp -o ' + sDstFilename + ' -rate %.2f' % nQuality
    commands.getstatusoutput(sCmd)

    sCmd = sKakaduExpand + ' -i ' + sDstFilename + ' -o temp.bmp'
    commands.getstatusoutput(sCmd)

    nPSNR = GetPSNR(sSrcFilename, 'temp.bmp')
    nBPP = GetFileSize(sDstFilename) * 8.0 / (float) (im.size[0] * im.size[1])
    return [ nBPP, nPSNR ]
 

def CompressISWavelet(sSrcFilename, sDstFilename, nQuality):
    im = Image.open(sSrcFilename)
    sMode = im.mode
    im = im.convert('RGB')

    sCmd = sIsWaveletCompress + ' ' + sSrcFilename + ' ' + sDstFilename + ' ' + str(nQuality)
    commands.getstatusoutput(sCmd)

    nPSNR = GetPSNR(sSrcFilename, ReadRaw(im.size[0], im.size[1], sMode, sDstFilename + '.raw'))
    nBPP = GetFileSize(sDstFilename + '.isw') * 8.0 / (float) (im.size[0] * im.size[1])
    return [ nBPP, nPSNR ]
 

def Compress(sSrcFilename, sDstFilename, sFormat, nQuality):
    if (not sFormat in lLossyFileFormats) and (not sFormat in lLosslessFileFormats):
        raise Exception("Unkown format: " + sFormat)

    if sFormat == "JPEG2000":
        return CompressJPEG2000(sSrcFilename, sDstFilename, nQuality)

    elif sFormat == "ISW":
        return CompressISWavelet(sSrcFilename, 'temp_isw', nQuality)

    elif sFormat == "TIFF":
        return CompressTiffDeflate(sSrcFilename, 'temp_isw')

    im = Image.open(sSrcFilename)
    im.save(sDstFilename, sFormat, quality=nQuality)

    nBPP = GetFileSize(sDstFilename) * 8.0 / (float) (im.size[0] * im.size[1])
    nPSNR = GetPSNR(sSrcFilename, sDstFilename)

    return [ nBPP, nPSNR ]
 

sGnuplotBase = """
set encoding iso_8859_1
set size 1.2,0.8

set terminal postscript eps mono "Helvetica" 12

set output 'gnuplot/%(FILE)s.eps'
set title '%(FILE)s'
set xlabel 'BPP'
set ylabel 'PSNR'

#set yrange [1:10]
set grid
set key top left

plot \
  'gnuplot/%(FILE)s.data' using 1:2 title "JPEG" with linespoints, \\
  'gnuplot/%(FILE)s.data' using 3:4 title "JPEG2000" with linespoints, \\
  'gnuplot/%(FILE)s.data' using 5:6 title "ISW" with linespoints

exit
"""

# Main program
try:
    for sFilename in lImageFiles:
        dResults = {}
        nResults = 0

        # Test lossy file formats
        for sFormat in lLossyFileFormats:
            dResults[sFormat] = []
            nResults = max(len(dlQualities[sFormat]), nResults)
            for nQuality in dlQualities[sFormat]:
                [ nBpp, nQ ] = Compress(sFilename, 'temp.dat', sFormat, nQuality)
                print "%-20s %8s %3d: BPP=%6.3f  PSNR=%.2f" % \
                      ( sFilename, sFormat, nQuality, nBpp, nQ )

                dResults[sFormat].append([ nBpp, nQ ])

        # Test lossless file formats
        for sFormat in lLosslessFileFormats:
            [ nBpp, nQ ] = Compress(sFilename, 'temp.dat', sFormat, nQuality)
            print "%-20s %8s: BPP=%6.3f" % \
                  ( sFilename, sFormat, nBpp )

        # Write gnuplot file
        sFilename = os.path.split(sFilename)[1]
        sGnuplotFile = 'gnuplot/' + sFilename + '.gpt'
        sGnuplotData = 'gnuplot/' + sFilename + '.data'

        print 'Writing: ' + sGnuplotFile
        f = open(sGnuplotFile, 'w+').write(sGnuplotBase % { 'FILE': sFilename })

        # Write gnuplot data file
        print 'Writing: ' + sGnuplotData
        f = open(sGnuplotData, 'w+')
        f.write('# %-8s %-10s %-10s\n' % (lLossyFileFormats[0],
                lLossyFileFormats[1], lLossyFileFormats[2]))

        for n in range(0, nResults):
            for sFormat in lLossyFileFormats:
                lRes = dResults[sFormat][-1]
                if n < len(dResults[sFormat]):
                    lRes = dResults[sFormat][n]

                f.write('%4.2f %5.2f ' % (lRes[0], lRes[1]))

            f.write('\n')

        f.close()

        # Run gnuplot
        sCmd = sGnuplot + ' ' + sGnuplotFile
        print 'Running:', sCmd
        commands.getstatusoutput(sCmd)

        print

    print 'Removing temporary files..'
    commands.getstatusoutput('rm temp*')

except Exception, x:
    print "Error:", str(x)
 

6.2 Python -skriptin lokitiedosto

kuvat/lena.tif           JPEG  90: BPP= 2.112  PSNR=37.79
kuvat/lena.tif           JPEG  80: BPP= 1.347  PSNR=36.62
kuvat/lena.tif           JPEG  70: BPP= 1.036  PSNR=35.89
kuvat/lena.tif           JPEG  60: BPP= 0.854  PSNR=35.38
kuvat/lena.tif           JPEG  50: BPP= 0.742  PSNR=34.98
kuvat/lena.tif           JPEG  40: BPP= 0.640  PSNR=34.57
kuvat/lena.tif           JPEG  30: BPP= 0.539  PSNR=33.95
kuvat/lena.tif           JPEG  20: BPP= 0.424  PSNR=32.82
kuvat/lena.tif           JPEG  10: BPP= 0.292  PSNR=30.50
kuvat/lena.tif           JPEG   1: BPP= 0.175  PSNR=24.68
kuvat/lena.tif       JPEG2000   2: BPP= 1.994  PSNR=39.04
kuvat/lena.tif       JPEG2000   1: BPP= 1.499  PSNR=38.32
kuvat/lena.tif       JPEG2000   1: BPP= 1.000  PSNR=37.17
kuvat/lena.tif       JPEG2000   0: BPP= 0.700  PSNR=36.25
kuvat/lena.tif       JPEG2000   0: BPP= 0.499  PSNR=35.25
kuvat/lena.tif       JPEG2000   0: BPP= 0.299  PSNR=33.88
kuvat/lena.tif       JPEG2000   0: BPP= 0.200  PSNR=32.75
kuvat/lena.tif       JPEG2000   0: BPP= 0.150  PSNR=31.77
kuvat/lena.tif       JPEG2000   0: BPP= 0.099  PSNR=30.41
kuvat/lena.tif       JPEG2000   0: BPP= 0.050  PSNR=28.24
kuvat/lena.tif            ISW  90: BPP= 2.178  PSNR=34.95
kuvat/lena.tif            ISW  80: BPP= 1.097  PSNR=34.00
kuvat/lena.tif            ISW  75: BPP= 0.560  PSNR=32.82
kuvat/lena.tif            ISW  70: BPP= 0.287  PSNR=31.25
kuvat/lena.tif            ISW  60: BPP= 0.145  PSNR=29.50
kuvat/lena.tif            ISW  50: BPP= 0.072  PSNR=27.52
kuvat/lena.tif            ISW  45: BPP= 0.034  PSNR=25.57
kuvat/lena.tif           TIFF: BPP=20.428
kuvat/lena.tif            PNG: BPP=15.290
kuvat/lena.tif            GIF: BPP= 8.603
Writing: gnuplot/lena.tif.gpt
Writing: gnuplot/lena.tif.data
Running: /usr/bin/gnuplot gnuplot/lena.tif.gpt

kuvat/frymire1.tif       JPEG  90: BPP= 4.228  PSNR=29.72
kuvat/frymire1.tif       JPEG  80: BPP= 3.048  PSNR=28.79
kuvat/frymire1.tif       JPEG  70: BPP= 2.483  PSNR=27.84
kuvat/frymire1.tif       JPEG  60: BPP= 2.117  PSNR=27.01
kuvat/frymire1.tif       JPEG  50: BPP= 1.863  PSNR=26.33
kuvat/frymire1.tif       JPEG  40: BPP= 1.623  PSNR=25.57
kuvat/frymire1.tif       JPEG  30: BPP= 1.363  PSNR=24.68
kuvat/frymire1.tif       JPEG  20: BPP= 1.047  PSNR=23.45
kuvat/frymire1.tif       JPEG  10: BPP= 0.692  PSNR=21.99
kuvat/frymire1.tif       JPEG   1: BPP= 0.358  PSNR=19.52
kuvat/frymire1.tif   JPEG2000   2: BPP= 1.999  PSNR=28.24
kuvat/frymire1.tif   JPEG2000   1: BPP= 1.500  PSNR=26.29
kuvat/frymire1.tif   JPEG2000   1: BPP= 1.000  PSNR=24.38
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.700  PSNR=22.55
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.500  PSNR=21.36
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.300  PSNR=20.09
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.197  PSNR=19.31
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.149  PSNR=18.74
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.100  PSNR=17.96
kuvat/frymire1.tif   JPEG2000   0: BPP= 0.049  PSNR=16.94
kuvat/frymire1.tif        ISW  90: BPP= 4.496  PSNR=29.27
kuvat/frymire1.tif        ISW  80: BPP= 3.388  PSNR=28.97
kuvat/frymire1.tif        ISW  75: BPP= 2.376  PSNR=28.08
kuvat/frymire1.tif        ISW  70: BPP= 1.504  PSNR=26.11
kuvat/frymire1.tif        ISW  60: BPP= 0.801  PSNR=23.01
kuvat/frymire1.tif        ISW  50: BPP= 0.339  PSNR=19.91
kuvat/frymire1.tif        ISW  45: BPP= 0.123  PSNR=17.53
kuvat/frymire1.tif       TIFF: BPP= 9.201
kuvat/frymire1.tif        PNG: BPP= 3.179
kuvat/frymire1.tif        GIF: BPP= 6.556
Writing: gnuplot/frymire1.tif.gpt
Writing: gnuplot/frymire1.tif.data
Running: /usr/bin/gnuplot gnuplot/frymire1.tif.gpt

kuvat/circles.tif        JPEG  90: BPP= 0.894  PSNR=47.80
kuvat/circles.tif        JPEG  80: BPP= 0.702  PSNR=42.13
kuvat/circles.tif        JPEG  70: BPP= 0.599  PSNR=39.26
kuvat/circles.tif        JPEG  60: BPP= 0.534  PSNR=37.33
kuvat/circles.tif        JPEG  50: BPP= 0.484  PSNR=36.25
kuvat/circles.tif        JPEG  40: BPP= 0.436  PSNR=35.41
kuvat/circles.tif        JPEG  30: BPP= 0.390  PSNR=34.27
kuvat/circles.tif        JPEG  20: BPP= 0.326  PSNR=32.43
kuvat/circles.tif        JPEG  10: BPP= 0.252  PSNR=31.13
kuvat/circles.tif        JPEG   1: BPP= 0.176  PSNR=26.64
kuvat/circles.tif    JPEG2000   2: BPP= 1.263  PSNR=66.26
kuvat/circles.tif    JPEG2000   1: BPP= 1.263  PSNR=66.26
kuvat/circles.tif    JPEG2000   1: BPP= 0.998  PSNR=59.22
kuvat/circles.tif    JPEG2000   0: BPP= 0.700  PSNR=50.47
kuvat/circles.tif    JPEG2000   0: BPP= 0.493  PSNR=43.68
kuvat/circles.tif    JPEG2000   0: BPP= 0.299  PSNR=36.80
kuvat/circles.tif    JPEG2000   0: BPP= 0.195  PSNR=33.24
kuvat/circles.tif    JPEG2000   0: BPP= 0.146  PSNR=31.31
kuvat/circles.tif    JPEG2000   0: BPP= 0.100  PSNR=28.62
kuvat/circles.tif    JPEG2000   0: BPP= 0.050  PSNR=24.23
kuvat/circles.tif         ISW  90: BPP= 1.022  PSNR=50.37
kuvat/circles.tif         ISW  80: BPP= 0.816  PSNR=48.79
kuvat/circles.tif         ISW  75: BPP= 0.604  PSNR=43.73
kuvat/circles.tif         ISW  70: BPP= 0.405  PSNR=37.94
kuvat/circles.tif         ISW  60: BPP= 0.235  PSNR=33.27
kuvat/circles.tif         ISW  50: BPP= 0.114  PSNR=29.14
kuvat/circles.tif         ISW  45: BPP= 0.059  PSNR=25.76
kuvat/circles.tif        TIFF: BPP= 0.920
kuvat/circles.tif         PNG: BPP= 0.463
kuvat/circles.tif         GIF: BPP= 1.379
Writing: gnuplot/circles.tif.gpt
Writing: gnuplot/circles.tif.data
Running: /usr/bin/gnuplot gnuplot/circles.tif.gpt

kuvat/ptt5_1.tif         JPEG  90: BPP= 1.185  PSNR=49.73
kuvat/ptt5_1.tif         JPEG  80: BPP= 0.937  PSNR=43.61
kuvat/ptt5_1.tif         JPEG  70: BPP= 0.819  PSNR=40.18
kuvat/ptt5_1.tif         JPEG  60: BPP= 0.735  PSNR=37.84
kuvat/ptt5_1.tif         JPEG  50: BPP= 0.670  PSNR=36.23
kuvat/ptt5_1.tif         JPEG  40: BPP= 0.606  PSNR=34.32
kuvat/ptt5_1.tif         JPEG  30: BPP= 0.533  PSNR=32.40
kuvat/ptt5_1.tif         JPEG  20: BPP= 0.432  PSNR=29.85
kuvat/ptt5_1.tif         JPEG  10: BPP= 0.323  PSNR=28.07
kuvat/ptt5_1.tif         JPEG   1: BPP= 0.225  PSNR=25.30
kuvat/ptt5_1.tif     JPEG2000   2: BPP= 1.526  PSNR=78.13
kuvat/ptt5_1.tif     JPEG2000   1: BPP= 1.500  PSNR=78.13
kuvat/ptt5_1.tif     JPEG2000   1: BPP= 1.000  PSNR=54.80
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.700  PSNR=46.33
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.500  PSNR=40.08
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.300  PSNR=32.95
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.200  PSNR=28.61
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.150  PSNR=26.76
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.100  PSNR=24.63
kuvat/ptt5_1.tif     JPEG2000   0: BPP= 0.050  PSNR=22.21
kuvat/ptt5_1.tif          ISW  90: BPP= 1.190  PSNR=51.54
kuvat/ptt5_1.tif          ISW  80: BPP= 0.987  PSNR=48.15
kuvat/ptt5_1.tif          ISW  75: BPP= 0.771  PSNR=42.20
kuvat/ptt5_1.tif          ISW  70: BPP= 0.559  PSNR=36.13
kuvat/ptt5_1.tif          ISW  60: BPP= 0.371  PSNR=31.47
kuvat/ptt5_1.tif          ISW  50: BPP= 0.193  PSNR=25.43
kuvat/ptt5_1.tif          ISW  45: BPP= 0.083  PSNR=22.34
kuvat/ptt5_1.tif         TIFF: BPP= 0.616
kuvat/ptt5_1.tif          PNG: BPP= 0.244
kuvat/ptt5_1.tif          GIF: BPP= 1.072
Writing: gnuplot/ptt5_1.tif.gpt
Writing: gnuplot/ptt5_1.tif.data
Running: /usr/bin/gnuplot gnuplot/ptt5_1.tif.gpt

Removing temporary files..