Title: Apply a twisting transformation to images in Python
My example Apply a fisheye transformation to images in Python shows how you can use NumPy to apply a fisheye transformation to an image. Once you understand the basic technique, it's not too hard to apply other transformations. This example uses similar code to apply twisting transformations.
Twisting Images
The apply_twister function is very similar to the apply_fisheye function described in my previous post. The following code shows the new function with the new code highlighted in blue.
def apply_twister(image, cx, cy, radians=2*math.pi):
'''Apply a radial twist distortion to an image.'''
# Convert the image to a NumPy array.
image_array = np.array(image)
# Create a grid of target pixel coordinates (y, x).
hgt, wid, channels = image_array.shape
max_radius = np.sqrt((wid/2)**2 + (hgt/2)**2)
y_indices, x_indices = np.indices((hgt, wid))
# Translate to put (cx, cy) at the origin (0, 0).
x_shifted = x_indices - cx
y_shifted = y_indices - cy
# Convert to polar coordinates.
r = np.sqrt(x_shifted**2 + y_shifted**2)
theta = np.arctan2(y_shifted, x_shifted)
# Normalize the radius to the 0.0 - 1.0 range.
r_norm = r / max_radius
# Apply the non-linear twist.
theta = theta + (radians * r_norm)
# Convert back to Cartesian coordinates.
x_source = cx + r * np.cos(theta)
y_source = cy + r * np.sin(theta)
# Reconstruct the image channels using interpolation.
output_array = np.zeros_like(image_array)
for c in range(channels):
output_array[:, :, c] = map_coordinates(
image_array[:, :, c],
[y_source, x_source],
order=3, # Cubic spline interpolation for high quality
mode='constant', # Fill black colors outside bounds
cval=0,
)
# Convert the result to a PIL image.
return Image.fromarray(output_array)
The function does all of the following as before:
- Converts the image into a NumPy array
- Creates the y_indices and x_indices arrays holding each pixels' X and Y coordinates
- Translates the coordinates to move the hot spot to the origin
- Converts into polar coordinates
- Normalizes to map radius values into the range 0.0 to 1.0
- Applies a non-linear transformation to the polar coordinates
- Converts back to Cartesian coordinates
- Uses the map_coordinates function to create the new image
The only new part is the step Applies a non-linear transformation to the polar coordinates. In this example, that occurs in the following statement.
theta = theta + (radians * r_norm)
The value r_norm is the normalized distance from the pixel to the hot spot so it ranges from 0.0 at the hot spot to 1.0 at the pixels farthest away. This statement multiples the desired angle of rotation by r_norm to get a value between 0.0 and the desired angle of rotation. It then adds that result to the pixel's theta value. As a result, the pixel's angle is increased the farther it is from the hot spot thus creating the twist.
The rest of the program is similar to the one in the previous example.
Conclusion
This example is also pretty fun to use, although I think the first one is a bit more fun. Again, there's a small lag when you use a 400×400 pixel image, but it's still quite usable.
Download the example to experiment with it and to see additional details.
|