It seems that Python's int-to-float conversion rules are more subtle than I realized. Making sure that numbers that should be floats are treated as such is a bit tricky, and rather important for this function.
The other problem is that you put the last two lines, which convert the correct data array into an image and return it inside the inner for loop. Thus your function only ran for the first pixel (giving you only a single white pixel).
I corrected both problems in the following:
def undistort(leap_image):
raw = leap_image.data
#distortion_buffer = fake_distortion() #leap_image.distortion
distortion_buffer = leap_image.distortion
distortion_width = leap_image.distortion_width
width = leap_image.width
height = leap_image.height
corrected_width = 320
corrected_height = 120
corrected = numpy.empty((corrected_width, corrected_height), dtype=numpy.ubyte)
for i in range(0, corrected_width):
for j in range(0, corrected_height):
# Calculate position in the calibration map
calibrationX = 63.0 * i / corrected_width
calibrationY = 62.0 * (1.0 - (j* 1.0)/corrected_height)
# Fractional part to be used as weighting in bilinear interpolation
weightX = calibrationX - math.trunc(calibrationX)
weightY = calibrationY - math.trunc(calibrationY)
# Get x,y coordinates of the closest calibration map points to the target pixel
x1 = math.trunc(calibrationX)
y1 = math.trunc(calibrationY)
x2 = x1 + 1
y2 = y1 + 1
# Find x,y grid coordinates from distortion buffer
dX1 = distortion_buffer[x1 * 2 + y1 * distortion_width]
dX2 = distortion_buffer[x2 * 2 + y1 * distortion_width]
dX3 = distortion_buffer[x1 * 2 + y2 * distortion_width]
dX4 = distortion_buffer[x2 * 2 + y2 * distortion_width]
dY1 = distortion_buffer[x1 * 2 + y1 * distortion_width + 1]
dY2 = distortion_buffer[x2 * 2 + y1 * distortion_width + 1]
dY3 = distortion_buffer[x1 * 2 + y2 * distortion_width + 1]
dY4 = distortion_buffer[x2 * 2 + y2 * distortion_width + 1]
# Bilinear Interpolation of target pixel
dX = dX1*(1.0 - weightX)*(1.0 - weightY) + dX2*weightX*(1.0 - weightY) + dX3*(1.0 - weightX)*weightY + dX4*weightX*weightY
dY = dY1*(1.0 - weightX)*(1.0 - weightY) + dY2*weightX*(1.0 - weightY) + dY3*(1.0 - weightX)*weightY + dY4*weightX*weightY
#print "i,j ", i, j, " cal x,Y ", calibrationX, calibrationY, " weightX,Y ", weightX, weightY, " dX,Y ", dX, dY
# Reject points outside [0..1]
if dX>=0 and dX<=1 and dY>=0 and dY<=1:
# Denormalise points from [0..1] to [0..width] or [0..height]
denormalisedX = math.trunc(dX * width)
denormalisedY = math.trunc(dY * height)
corrected[i,j] = raw[denormalisedX + denormalisedY * width]
else:
corrected[i,j] = 0
corrected_image = Image.fromarray(corrected, 'L')
return corrected_image
def fake_distortion():
fake = [i for i in range(64*64*2)]
for i in range(0,64*64*2, 2):
w = math.trunc(i % (64 * 2))
h = math.trunc((i - w)/128)
fake[i] = w/(64.0 * 2.0)
fake[i + 1] = h/64.0
#print w, h, fake[i], fake[i+1]
return fake
I also added a "fake" distortion map generator. This map should make no changes to your image, so it is helpful to debug whether the distortion code or the distortion map is at fault.