We'll be examining the effects of image distortion caused by camera lens by artificially distorting images, analyzing the underlying math and beginning the process of undistorting the images to better understand the mechanics of this process.
We’ll be using Brown’s radial distortion model to first distort the following image:
This image was chosen because it offers clear straight lines coupled with equally sized boxes in order to easily visually identify distortions.
Let's begin by importing the necessary packages and importing the image above.
from PIL import Image
import numpy as np
import math
image = Image.open('my_img.jpg')
my_img = np.array(image) # convert image to numpy array
h, w = len(my_img), len(my_img[0])
new_img = np.zeros((h, w, 3), dtype=np.uint8) # distorted image
x_c, y_c = w/2, h/2 # the x,y coordinates of the center of the image
k1, k2, k3 = 0.1, 0.1, 0.1 # which we will alter
We iterate through each pixel in the image, find it's new distorted x, y coordinates and set the pixel value of the distorted image's pixel at these new x, y coordinates to that of the original.
for h_i in range(len(my_img)):
for w_i in range(len(my_img[h_i])):
r = math.sqrt((w_i-x_c)**2 + (h_i-y_c)**2) # radial distance from center
x_d , y_d = new_cords(k1, k2, k3, r, w_i, h_i, x_c, y_c) # distorted coordinates
if in_range(x_d, y_d, h, w):
new_img[y_d][x_d] = my_img[h_i][w_i]
We'll need the following helper functions to help us accomplish this task.
# determine if x_d, y_d coordinates are within the bounds of the image array
def in_range(x_d, y_d, h, w):
return x_d < w and y_d < h and x_d >=0 and y_d>=0
# determines distorted x, y coordinates
def new_cords(k1, k2, k3, r, x, y, x_c, y_c):
# in order to use the following equation we must adjust our coordinate system
# such that the center of the image has cordinates 0, 0
k_val = 1+k1*(r**2)+ k2*(r**4) + k3*(r**6)
x_d = round(x-x_c * k_val) + x_c
y_d = round(y-y_c * k_val) + y_c
return int(x_d), int(y_d)
Finally, we save the image.
img = Image.fromarray(new_img, 'RGB')
filename = "my_imgs/distorted_img.png"
img.save(filename)
By running the distortion with various of values for k1, k2, and k3 we observe the following set of results. Let's begin by setting only one of the constants a value while the others are set to 0.
The three images above look distorted to about the same extent. The images had the following constant values respectively:
k1, k2, k3 = -0.0000003, 0, 0
k1, k2, k3 = 0, -0.000000000003, 0
k1, k2, k3 = 0, 0, -0.00000000000000003
Notice how the magnitude of k1 > k2 > k3 in order to achieve roughly the same result. This is because the constants are coefficients acting on higher order terms. As expected, the greater values of k3 lead to greater distortions than those of k2, which lead to greater distortions than those of k1. Let's examine the distortion coefficient to see why.
$$ k_{val}=1+k_1r^2+k_2r^4 +k_3r^6 $$
We can see how the sum is most sensitive to k3 followed by k2 followed by k1 since r is unchanging for a given coordinate. Also as we increase the values of k3 we notice that the edges of the image become affected as much faster degree. This is expected since r^6 grows much faster than r^4 and r^2.