Transformations

The Twin Cities of Minneapolis and St. Paul is at a latitude of almost 45 degrees north. Towards the summer solstice which is in a week or so, the daylight lasts about 15.5 hours. The beauty in summer is that after work, you still have about 4 to 5 hours to enjoy the outdoors. In winter it is quite the opposite. I guess, there is no perfect place on earth.

At home, one of the gutters has a section that brings the rain water down. The section forms some type of a lazy step. My wife and I noticed a few days ago that a couple of birds decided to build their nest on the lazy step.

Yesterday we watched how the male (larger bird) left the nest, and a few seconds later the female took over, warming the eggs and protecting them. That happened around 18:00 hours. This morning around 05:30 hours the female was still in the nest. For some reason the female likes to face the house while the male likes to face the backyard.

It is interesting to us that two animals take care of their offspring for about two plus months. Nowadays there are many human couples that do less for their kids. I wonder what happened with our evolution process.

If you like the t-shirt, you can get it from amazon.com. I am not associated in any way or shape with the producers of the t-shirt.

Enough chit chat. Let’s get to the main subject of this post.

We will experiment with some image transformations. Many years ago I built a small library to handle 2D transformations. Today, I could have used scikit-image class skimage.transform.SimilarityTransform(matrix=None, scale=None, rotation=None, translation=None, *, dimensionality=2) to get not only what I needed at the time, but many additional features. 

# **** folder of interest ****
cd C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos

# **** open file of interest using VSCode ****
(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>code Transformations.py

# **** execute python script of interest ****
(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>python Transformations.py

For starters we open an Anaconda prompt and get to the folder of interest. I am using the same name as Janani Ravi, author of the PluralSight course Building Image Processing Applications Using scikit-image. Of course you can put your Python scripts in a folder convenient to you.

In addition, the author used Jupyter notebooks. I am using VSCode IDE with GitHub Copilot. At the time of this post I am a Microsoft employee and have been using VSCode and Visual Studio for quite a long time. I prefer to use a single IDE for multiple programming languages. That way I do not have to learn the different idiosyncrasies that come along with a slew of IDEs.

Once we are in the proper folder we create the Python script of interest. You may use a different name if you please. That said, after each editing pass, make sure the script is saved before running it.

# **** imports ****
import math                                 # for math functions
import matplotlib.pyplot as plt             # for plotting

from skimage import io                      # for image io
from skimage import data                    # for image data
from skimage import transform               # for image transformation
from skimage.transform import swirl, warp   # for swirl transformation

We start by adding the necessary imports.

# **** read gears image ****
gears = io.imread('./images/pexels-gears.jpg')

# **** print gears image shape ****
print(f'gears.shape: {gears.shape}')
print(f'gears.shape[0]: {gears.shape[0]}')
print(f'gears.shape[1]: {gears.shape[1]}')
print(f'gears.shape[2]: {gears.shape[2]}')

# **** display gears RGB image ****
plt.figure(figsize=(8, 8))
plt.imshow(gears)               # display the image
plt.title('Gears')
plt.show()

We now read in a RGB JPG image that contains a set of gears. Some information about the shape of the image is then displayed on the screen. Finally the image of interest is displayed.

gears.shape: (480, 640, 3)
gears.shape[0]: 480
gears.shape[1]: 640
gears.shape[2]: 3

The shape of the image is captured on the screen.

# **** Shape preserving transformation - only scalling,
#      translation, and rotation ****
tform = transform.SimilarityTransform(  scale=2.0,                  # scale factor
                                        translation=(-200, -400),   # translation
                                        rotation=math.pi/16         # rotation angle
                                     )

# **** apply transformation to gears image ****
rotated = transform.warp(   gears,
                            tform)

# **** apply inverse transformation to rotated image ****
back_rotated = transform.warp(  rotated,
                                tform.inverse)

# **** display rotated image ****
plt.figure(figsize=(10, 10))
plt.imshow(rotated)             # display the image
plt.title('Scale, Rotate & Translate')
plt.show()

The idea to perform a transformation is to populate a matrix with the values required to apply to an image.  In the SimilarityTransform we apply scale, translation and rotation.

Now that the transform matrix is ready, we can apply it to the gears image. The resulting imaged is called rotated, but all three transformations have been applied.

The back_rotated image is generated by applying the inverse of the transformation to the rotated image. The result is the original gears image.

The rotated image is then displayed. In my opinion it should have been called the transformed image.

# **** display back rotated image ****
plt.figure(figsize=(10, 10))
plt.imshow(back_rotated)        # display the image
plt.title('Back Rotated')
plt.show()

The back_rotated image is then displayed. Note that it looks like the original gears image.

# **** swirl transformation ****
gears_swirl = swirl(gears, 
                    rotation=0, 
                    strength=20, 
                    radius=150,
                    mode='constant')

# **** display gears swirl image ****
plt.figure(figsize=(10, 10))
plt.imshow(gears_swirl)         # display the image
plt.title('Gears Swirl')
plt.show()

In this step we apply a swirl transformation. Based on the arguments the image will contain a swirl look. The image is then displayed.

# **** swirl transformation ****
gears_swirl = swirl(gears, 
                    rotation=0, 
                    strength=20, 
                    radius=350,
                    mode='constant')

# **** display gears swirl image ****
plt.figure(figsize=(10, 10))
plt.imshow(gears_swirl)         # display the image
plt.title('Gears Swirl')
plt.show()

We apply once again to the gears image the swirl transformation with a different argument. This is a good time to spend some additional time experimenting with the effects caused by the different argument values.

The resulting image is then displayed.

# **** swirl transformation ****
gears_swirl = swirl(gears, 
                    rotation=0, 
                    strength=10, 
                    radius=350,
                    mode='constant')

# **** display gears swirl image ****
plt.figure(figsize=(10, 10))
plt.imshow(gears_swirl)         # display the image
plt.title('Gears Swirl')
plt.show()

Once again, a different value is applied to one of the arguments. The resulting image is then displayed.

If interested in the associated code, you can find it in my GitHub repository Transformations.

Remember that one of the best ways to learn is to read, experiment, and repeat as needed. In our case in this post, try different arguments on the different functions and different images.

Enjoy,

John

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.