Skip to content

Instantly share code, notes, and snippets.

@CoderSherlock
Created December 28, 2016 04:11
Show Gist options
  • Save CoderSherlock/834e9eb918eeb0dfee5f4550077f57f8 to your computer and use it in GitHub Desktop.
Save CoderSherlock/834e9eb918eeb0dfee5f4550077f57f8 to your computer and use it in GitHub Desktop.
'''
2D CDF 9/7 Wavelet Forward and Inverse Transform (lifting implementation)
This code is provided "as is" and is given for educational purposes.
2008 - Kris Olhovsky - code.inquiries@olhovsky.com
'''
from PIL import Image # Part of the standard Python Library
''' Example matrix as a list of lists: '''
mat4x4 = [
[0, 1, 2, 3], # Row 1
[4, 5, 6, 7], # Row 2
[8, 9, 10, 11], # Row 3
[12, 13, 14, 15], # Row 4
] # We don't do anything with this matrix.
# It's just here for clarification.
def fwt97_2d(m, nlevels=1):
''' Perform the CDF 9/7 transform on a 2D matrix signal m.
nlevel is the desired number of times to recursively transform the
signal. '''
w = len(m[0])
h = len(m)
for i in range(nlevels):
m = fwt97(m, w, h) # cols
m = fwt97(m, w, h) # rows
w /= 2
h /= 2
return m
def iwt97_2d(m, nlevels=1):
''' Inverse CDF 9/7 transform on a 2D matrix signal m.
nlevels must be the same as the nlevels used to perform the fwt.
'''
w = len(m[0])
h = len(m)
# Find starting size of m:
for i in range(nlevels-1):
h /= 2
w /= 2
for i in range(nlevels):
m = iwt97(m, w, h) # rows
m = iwt97(m, w, h) # cols
h *= 2
w *= 2
return m
def fwt97(s, width, height):
''' Forward Cohen-Daubechies-Feauveau 9 tap / 7 tap wavelet transform
performed on all columns of the 2D n*n matrix signal s via lifting.
The returned result is s, the modified input matrix.
The highpass and lowpass results are stored on the left half and right
half of s respectively, after the matrix is transposed. '''
# 9/7 Coefficients:
a1 = -1.586134342
a2 = -0.05298011854
a3 = 0.8829110762
a4 = 0.4435068522
# Scale coeff:
k1 = 0.81289306611596146 # 1/1.230174104914
k2 = 0.61508705245700002 # 1.230174104914/2
# Another k used by P. Getreuer is 1.1496043988602418
for col in range(width): # Do the 1D transform on all cols:
''' Core 1D lifting process in this loop. '''
''' Lifting is done on the cols. '''
# Predict 1. y1
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
# Update 1. y0
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] # Symmetric extension
# Predict 2.
for row in range(1, height-1, 2):
s[row][col] += a3 * (s[row-1][col] + s[row+1][col])
s[height-1][col] += 2 * a3 * s[height-2][col]
# Update 2.
for row in range(2, height, 2):
s[row][col] += a4 * (s[row-1][col] + s[row+1][col])
s[0][col] += 2 * a4 * s[1][col]
# de-interleave
temp_bank = [[0]*width for i in range(height)]
for row in range(height):
for col in range(width):
# k1 and k2 scale the vals
# simultaneously transpose the matrix when deinterleaving
if row % 2 == 0: # even
temp_bank[col][row/2] = k1 * s[row][col]
else: # odd
temp_bank[col][row/2 + height/2] = k2 * s[row][col]
# write temp_bank to s:
for row in range(width):
for col in range(height):
s[row][col] = temp_bank[row][col]
return s
def iwt97(s, width, height):
''' Inverse CDF 9/7. '''
# 9/7 inverse coefficients:
a1 = 1.586134342
a2 = 0.05298011854
a3 = -0.8829110762
a4 = -0.4435068522
# Inverse scale coeffs:
k1 = 1.230174104914
k2 = 1.6257861322319229
# Interleave:
temp_bank = [[0]*width for i in range(height)]
for col in range(width/2):
for row in range(height):
# k1 and k2 scale the vals
# simultaneously transpose the matrix when interleaving
temp_bank[col * 2][row] = k1 * s[row][col]
temp_bank[col * 2 + 1][row] = k2 * s[row][col + width/2]
# write temp_bank to s:
for row in range(width):
for col in range(height):
s[row][col] = temp_bank[row][col]
for col in range(width): # Do the 1D transform on all cols:
''' Perform the inverse 1D transform. '''
# Inverse update 2.
for row in range(2, height, 2):
s[row][col] += a4 * (s[row-1][col] + s[row+1][col])
s[0][col] += 2 * a4 * s[1][col]
# Inverse predict 2.
for row in range(1, height-1, 2):
s[row][col] += a3 * (s[row-1][col] + s[row+1][col])
s[height-1][col] += 2 * a3 * s[height-2][col]
# Inverse update 1.
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] # Symmetric extension
# Inverse predict 1.
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
return s
def seq_to_img(m, pix):
''' Copy matrix m to pixel buffer pix.
Assumes m has the same number of rows and cols as pix. '''
for row in range(len(m)):
for col in range(len(m[row])):
pix[col,row] = m[row][col]
if __name__ == "__main__":
# Load image.
im = Image.open("test1_512.png") # Must be a single band image! (grey)
# Create an image buffer object for fast access.
pix = im.load()
# Convert the 2d image to a 1d sequence:
m = list(im.getdata())
# Convert the 1d sequence to a 2d matrix.
# Each sublist represents a row. Access is done via m[row][col].
m = [m[i:i+im.size[0]] for i in range(0, len(m), im.size[0])]
# Cast every item in the list to a float:
for row in range(0, len(m)):
for col in range(0, len(m[0])):
m[row][col] = float(m[row][col])
# Perform a forward CDF 9/7 transform on the image:
m = fwt97_2d(m, 3)
seq_to_img(m, pix) # Convert the list of lists matrix to an image.
im.save("test1_512_fwt.png") # Save the transformed image.
# Perform an inverse transform:
m = iwt97_2d(m, 3)
seq_to_img(m, pix) # Convert the inverse list of lists matrix to an image.
im.save("test1_512_iwt.png") # Save the inverse transformation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment