Image transformation

Open In Colab

In [1]:
import numpy as np
import matplotlib.pyplot as plt

figsize = (10,10)

Start by building our basic default points to test on

In [2]:
x_max = 20
y_max = 10
x_arr, y_arr = np.meshgrid(np.arange(x_max), np.arange(y_max))
x_arr = x_arr.flatten().reshape(1, -1)
y_arr = y_arr.flatten().reshape(1, -1)
xy = np.concatenate((x_arr, y_arr), axis=0)

print(xy.shape)
xy
(2, 200)
Out[2]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
        12, 13, 14, 15, 16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,
         8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,  1,  2,  3,
         4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
        12, 13, 14, 15, 16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,
         8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,  1,  2,  3,
         4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
        12, 13, 14, 15, 16, 17, 18, 19],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
         0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
         1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,
         2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  3,  3,  3,
         3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
         4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
         4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
         5,  5,  5,  5,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,
         6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,  7,  7,
         7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
         8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
         8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
         9,  9,  9,  9,  9,  9,  9,  9]])
In [3]:
plt.figure(figsize=figsize)
plt.scatter(xy[0, :], xy[1, :])
plt.title("Original points")
Out[3]:
Text(0.5, 1.0, 'Original points')

Scale

In [4]:
sx = 2
sy = 3
M_scale = np.array([[sx, 0],
                    [0, sy]])
M_scale
Out[4]:
array([[2, 0],
       [0, 3]])
In [5]:
xy_scaled = M_scale@xy

plt.figure(figsize=figsize)
plt.scatter(xy[0, :], xy[1, :])
plt.scatter(xy_scaled[0, :], xy_scaled[1, :], marker='*')
plt.title("Scaled points")
Out[5]:
Text(0.5, 1.0, 'Scaled points')

Rotate

In [6]:
def rot_mat(theta):
    c = np.cos(theta)
    s = np.sin(theta)
    return np.array([[c, -s],
                     [s,  c]])


rot_mat(np.pi)
Out[6]:
array([[-1.0000000e+00, -1.2246468e-16],
       [ 1.2246468e-16, -1.0000000e+00]])
In [7]:
theta = np.pi/2
xy_rotate = rot_mat(theta) @xy

plt.figure(figsize=figsize)
plt.scatter(xy[0, :], xy[1, :])
plt.scatter(xy_rotate[0, :], xy_rotate[1, :], marker='*')
plt.title("Rotated points 90 degrees")
Out[7]:
Text(0.5, 1.0, 'Rotated points 90 degrees')
In [8]:
theta = np.pi/4
xy_rotate = rot_mat(theta) @xy

plt.figure(figsize=figsize)
plt.scatter(xy[0, :], xy[1, :])
plt.scatter(xy_rotate[0, :], xy_rotate[1, :], marker='*')
plt.title("Scaled points 45 degrees")
Out[8]:
Text(0.5, 1.0, 'Scaled points 45 degrees')

Shear

In [9]:
shear_x = 2
shear_y = 0
M_shear = np.array([[1, shear_x],
                    [shear_y, 1]])
M_shear
Out[9]:
array([[1, 2],
       [0, 1]])
In [10]:
xy_sheared = M_shear@xy

plt.figure(figsize=figsize)
plt.scatter(xy[0, :], xy[1, :])
plt.scatter(xy_sheared[0, :], xy_sheared[1, :], marker='*')
plt.title("Sheared points")
Out[10]:
Text(0.5, 1.0, 'Sheared points')

Translate

In [11]:
xy1 = np.concatenate((xy, np.ones((1, xy.shape[1]))), axis=0)

xy1
Out[11]:
array([[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12.,
        13., 14., 15., 16., 17., 18., 19.,  0.,  1.,  2.,  3.,  4.,  5.,
         6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18.,
        19.,  0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11.,
        12., 13., 14., 15., 16., 17., 18., 19.,  0.,  1.,  2.,  3.,  4.,
         5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17.,
        18., 19.,  0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.,
        11., 12., 13., 14., 15., 16., 17., 18., 19.,  0.,  1.,  2.,  3.,
         4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16.,
        17., 18., 19.,  0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.,
        10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,  0.,  1.,  2.,
         3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15.,
        16., 17., 18., 19.,  0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,
         9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,  0.,  1.,
         2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14.,
        15., 16., 17., 18., 19.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
         0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,
         2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  3.,  3.,  3.,  3.,  3.,
         3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,
         3.,  3.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,
         4.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,  4.,  5.,  5.,  5.,  5.,
         5.,  5.,  5.,  5.,  5.,  5.,  5.,  5.,  5.,  5.,  5.,  5.,  5.,
         5.,  5.,  5.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,
         6.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,  6.,  7.,  7.,  7.,
         7.,  7.,  7.,  7.,  7.,  7.,  7.,  7.,  7.,  7.,  7.,  7.,  7.,
         7.,  7.,  7.,  7.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,
         8.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,  8.,  9.,  9.,
         9.,  9.,  9.,  9.,  9.,  9.,  9.,  9.,  9.,  9.,  9.,  9.,  9.,
         9.,  9.,  9.,  9.,  9.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.]])
In [12]:
t_x = 12.5
t_y = -4
M_t = np.array([[1, 0, t_x],
                [0, 1, t_y],
                [0, 0, 1]])

M_t
Out[12]:
array([[ 1. ,  0. , 12.5],
       [ 0. ,  1. , -4. ],
       [ 0. ,  0. ,  1. ]])
In [13]:
xy1_translated = M_t@xy1

plt.figure(figsize=figsize)
plt.scatter(xy1[0, :], xy1[1, :])
# no need to do hear homogenous normalization...
plt.scatter(xy1_translated[0, :], xy1_translated[1, :], marker='*')
plt.title("Translated points")
Out[13]:
Text(0.5, 1.0, 'Translated points')

Matrix concatenation

In [14]:
s_x = 2
s_y = 3.5
M_scale = np.array([[s_x, 0, 0],
                    [0, s_y, 0],
                    [0, 0, 1]])

xy1_s_then_t = M_t@M_scale@xy1

plt.figure(figsize=figsize)
plt.scatter(xy1[0, :], xy1[1, :])
# no need to do hear homogenous normalization...
plt.scatter(xy1_s_then_t[0, :], xy1_s_then_t[1, :], marker='*')
plt.title("Scaled and then translated points")
Out[14]:
Text(0.5, 1.0, 'Scaled and then translated points')
In [15]:
xy1_t_then_s = M_scale@M_t@xy1

plt.figure(figsize=figsize)
plt.scatter(xy1[0, :], xy1[1, :])
# no need to do hear homogenous normalization...
plt.scatter(xy1_t_then_s[0, :], xy1_t_then_s[1, :], marker='*')
plt.title("Translated and then scaled points")
Out[15]:
Text(0.5, 1.0, 'Translated and then scaled points')

Projective transformation

In [16]:
M_p = np.array([[1, 0, 0],
                [0, 20, 0],
                [0, 2, 1]])

M_p
Out[16]:
array([[ 1,  0,  0],
       [ 0, 20,  0],
       [ 0,  2,  1]])
In [17]:
xy1_p = M_p@xy1

plt.figure(figsize=figsize)
plt.scatter(xy1[0, :], xy1[1, :])
#here we MUST do homogenous normalization!!!
plt.scatter(xy1_p[0, :]/xy1_p[2, :],
            xy1_p[1, :]/xy1_p[2, :],
            marker='*')
plt.title("Projected points")
Out[17]:
Text(0.5, 1.0, 'Projected points')
In [ ]: