Hi everybody!

First of all I should say I am a newbie with Python/Scipy. Have been
searching a little bit (google and lists) and haven't found a helpful
answer...so I'm posting.

I'm using Scipy/Numpy to do image wavelet transforms via the lifting scheme.
I grabbed some code implementing the transforms with Python lists (float
type). This code works perfectly, but slow for my needs (I'll be doing some
genetic algorithms to evolve coefficients of the filters and the forward and
inverse transform will be done many times). It's just implemented by looping
in the lists and making computations this way. Reconstructed image after
doing a forward and inverse transform is perfect, this is, original and
reconstructed images difference is 0.

With Scipy/Numpy float arrays slicing this code is much faster as you know.
But the reconstructed image is not perfect. The image difference maximum and
minimum values returns:
maximum difference => 3.5527136788e-15
minimum difference => -3.5527136788e-15

Is this behavior expected? Because it seems sooo weird to me. If expected,
anyway to override it?

I include some test code for you to reproduce. It's part of a transform over
a 8x8 2D signal (for simplicity). It's not complete (again for simplicity),
but enough to reproduce. It does part of a forward and inverse transform
both with lists and arrays, printing the differences (there is commented
code showing some plots with the results as I used when transforming real
images, but for the purpose, is enough with the return results I think).
Code is simple (maybe long but it's always the same). Instead of using the
faster array slicing as commented above, I am using here array looping, so
that the math code is exactly the same as in the case list.

This happens in the next three system/platforms.

* System 1 (laptop):
---------------------------
64 bit processor running Kubuntu 8.04 32 bits
Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52
Numpy version: 1:1.0.4-6ubuntu3
Scipy version: 0.6.0-8ubuntu1

* System 2 (PC):
--------------------------
Windows Xp on 64 bit processor
Enthought Python distribution  (EPD Py25 v4.1.30101). This is a Python 2.5.2
with Numpy 1.1.1 and Scipy 0.6.0

* System 3 (same PC as 2):
--------------------------------------
Debian Lenny 64 bit on 64 bit processor
Not sure about versions here, but doesn't mind because behavior is prety
much the same in the 3 systems

Thanks everybody in advance for the help!

Rubén Salvador
PhD Student
Industrial Electronics Center
Universidad Politécnica de Madrid
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy
import scipy
import pylab as plt

################################################################
# The test shows precision issues with numpy floats against
# perfect behavior with python lists
#
# The computations are based on image wavelet transforms
# via the lifting scheme, wich allows computations in place

# Note that this is just one step of the transform, not the
# whole of it (just first lifting stage of row processing), but
# enough to show weird behavior of floating point operations
#
# Inverse transformed image should be exact to the original image
################################################################

########################
# Test with numpy arrays
########################
marr = [[ 1,  5,  6,  2,  9,  7,  1,  4],[3, 6, 2, 7, 4, 8, 5, 9],[ 8, 2,  9, 1, 3, 4, 1, 5],[4, 1, 5, 8, 3, 1, 4, 7],[6, 2, 1, 3, 8, 2, 4, 3],[8, 5, 9, 5, 4, 2, 1, 5],[4, 8, 5, 9, 6, 3, 2, 7],[5, 6, 5, 1, 8, 2, 9, 3]]
m = scipy.array(marr, dtype=scipy.float64)
morig = m.copy()

######
# Forward transform (9/7)
######
a1 = -1.586134342
a2 = -0.05298011854
k1 = 0.81289306611596146 # 1/1.230174104914
k2 = 0.61508705245700002 # 1.230174104914/2
width = len(m[0])
height = len(m)

# transform
for col in range(width):
    for row in range(1, height-1, 2):
        m[row][col] += a1 * (m[row-1][col] + m[row+1][col]) 
    m[height-1][col] += 2 * a1 * m[height-2][col]
    for row in range(2, height, 2):
        m[row][col] += a2 * (m[row-1][col] + m[row+1][col])
    m[0][col] +=  2 * a2 * m[1][col]
temp_bank = scipy.zeros((height,width),scipy.float64)

# transpose/interleave 2D signal and scale
for row in range(height):
    for col in range(width):
        if row % 2 == 0: # even
            temp_bank[col][row/2] = k1 * m[row][col]
        else:            # odd
            temp_bank[col][row/2 + height/2] = k2 * m[row][col]
for row in range(width):
    for col in range(height):
        m[row][col] = temp_bank[row][col]

######
# Inverse transform (9/7)
######
w = len(m[0])
h = len(m)
a1 = 1.586134342
a2 = 0.05298011854
# Inverse scale coeffs:
k1 = 1.230174104914
k2 = 1.6257861322319229
s = m.copy()
temp_bank = scipy.zeros((height,width),scipy.float64)

# transpose/interleave 2D signal and scale back
for col in range(width/2):
    for row in range(height):
        temp_bank[col * 2][row] = k1 * s[row][col]
        temp_bank[col * 2 + 1][row] = k2 * s[row][col + width/2]
for row in range(width):
    for col in range(height):
        s[row][col] = temp_bank[row][col]

# inverse transform
for col in range(width): # Do the 1D transform on all cols:
    for row in range(2, height, 2):
        s[row][col] += a2 * (s[row-1][col] + s[row+1][col])
    s[0][col] += 2 * a2 * s[1][col]
    for row in range(1, height-1, 2):
        s[row][col] += a1 * (s[row-1][col] + s[row+1][col])
    s[height-1][col] += 2 * a1 * s[height-2][col] # Symmetric extension

# show results
print
print "="*20
print "ARRAY TEST"
print "="*20
print "* Original vs Reconstructed image comparison: "
print numpy.all(numpy.equal(s, morig))
diff_image = scipy.zeros((height,width),scipy.float64)
for row in range(height):
    for col in range(width):
        diff_image[row][col] = morig[row][col] - s[row][col]
print "\n* Difference image:"
print diff_image
print "maximum difference =>", diff_image.max()
print "minimum difference =>", diff_image.min()



#########################
# Test with python lists
#########################
ml = [[ 1,  5,  6,  2,  9,  7,  1,  4],[3, 6, 2, 7, 4, 8, 5, 9],[ 8, 2,  9, 1, 3, 4, 1, 5],[4, 1, 5, 8, 3, 1, 4, 7],[6, 2, 1, 3, 8, 2, 4, 3],[8, 5, 9, 5, 4, 2, 1, 5],[4, 8, 5, 9, 6, 3, 2, 7],[5, 6, 5, 1, 8, 2, 9, 3]]

for row in range(0, len(m)):
    for col in range(0, len(m[0])):
        m[row][col] = float(m[row][col])

morigl = ml[:]

######
# Forward transform (9/7)
######
a1 = -1.586134342
a2 = -0.05298011854
k1 = 0.81289306611596146 # 1/1.230174104914
k2 = 0.61508705245700002 # 1.230174104914/2
width = len(ml[0])
height = len(ml)

# transform
for col in range(width):
    for row in range(1, height-1, 2):
        ml[row][col] += a1 * (ml[row-1][col] + ml[row+1][col]) 
    ml[height-1][col] += 2 * a1 * ml[height-2][col]
    for row in range(2, height, 2):
        ml[row][col] += a2 * (ml[row-1][col] + ml[row+1][col])
    ml[0][col] +=  2 * a2 * ml[1][col]

# transpose/interleave 2D signal and scale
temp_bank = [[0]*width for i in range(height)]
for row in range(height):
    for col in range(width):
        if row % 2 == 0: # even
            temp_bank[col][row/2] = k1 * ml[row][col]
        else:            # odd
            temp_bank[col][row/2 + height/2] = k2 * ml[row][col]
for row in range(width):
    for col in range(height):
        ml[row][col] = temp_bank[row][col]

######
# 9/7 inverse coefficients:
######
w = len(ml[0])
h = len(ml)
a1 = 1.586134342
a2 = 0.05298011854
# Inverse scale coeffs:
k1 = 1.230174104914
k2 = 1.6257861322319229
sl = ml[:]
temp_bank = [[0]*width for i in range(height)]

# transpose/interleave 2D signal and scale back
for col in range(width/2):
    for row in range(height):
        temp_bank[col * 2][row] = k1 * sl[row][col]
        temp_bank[col * 2 + 1][row] = k2 * sl[row][col + width/2]
for row in range(width):
    for col in range(height):
        sl[row][col] = temp_bank[row][col]

# inverse transform
for col in range(width):
    for row in range(2, height, 2):
        sl[row][col] += a2 * (sl[row-1][col] + sl[row+1][col])
    sl[0][col] += 2 * a2 * sl[1][col]
    for row in range(1, height-1, 2):
        sl[row][col] += a1 * (sl[row-1][col] + sl[row+1][col])
    sl[height-1][col] += 2 * a1 * sl[height-2][col]

# show results
print
print "="*20
print "LIST TEST"
print "="*20
print "* Original vs Reconstructed image comparison: "
print sl==morigl
diff_imagel = [[0.0]*width for i in range(height)]
for row in range(height):
    for col in range(width):
        diff_imagel[row][col] = morigl[row][col] - sl[row][col]
print "\n* Difference image:"
print diff_imagel
print "maximum difference =>", max(diff_imagel)
print "minimum diff =>", min(diff_imagel)


# shows the difference in expected values in grayscale
# Gray shows perfect reconstruction while black/white diffs
#numpy.putmask(diff_image, diff_image>0.0, 255)
#numpy.putmask(diff_image, diff_image<0.0, 0)
#numpy.putmask(diff_image, diff_image==0.0, 128)
#plt.gray()
#plt.figure()
#plt.imshow(diff_image, vmin=0, vmax=255, interpolation='nearest')
#plt.title("(ARRAYS) Difference image (diff_image)")
#plt.figure()
#plt.imshow(morig)
#plt.title("(ARRAYS) Original image (morig)")
#plt.figure()
#plt.imshow(m)
#plt.title("(ARRAYS) Transformed image (m)")
#plt.figure()
#plt.imshow(s)
#plt.title("(ARRAYS) Inverse transformed image (s)")

# Black is perfect reconstruction in the list case
#plt.figure()
#plt.imshow(diff_imagel)
#plt.title("(LISTS) Difference image (diff_image)")
#plt.figure()
#plt.imshow(morigl)
#plt.title("(LISTS) Original image (morig)")
#plt.figure()
#plt.imshow(ml)
#plt.title("(LISTS) Transformed image (m)")
#plt.figure()
#plt.imshow(sl)
#plt.title("(LISTS) Inverse transformed image (s)")
#plt.show()

_______________________________________________
Numpy-discussion mailing list
Numpy-discussion@scipy.org
http://mail.scipy.org/mailman/listinfo/numpy-discussion

Reply via email to