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.

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.
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ä.

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.
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ä".

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.
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.

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.
JPEG, 0.20 BPP |
JPEG 2000, 0.20 BPP |
Image Soft Wavelet, 0.20 BPP |
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.
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.
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 'Removing temporary
files..'
commands.getstatusoutput('rm
temp*')
except Exception, x:
print "Error:", str(x)
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..