Note
This notebook can be downloaded here: Orientation.ipynb
Advanced Example: orientation and aspect ratioΒΆ
In this example, we show how to use inertia matrix of a given labeled object to find its orientation.
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy import ndimage
import pandas as pd
import os
%matplotlib nbagg
path = "balls.jpg"
files = os.listdir("./")
if path in files:
print("Ok, the file is in {0}".format(files))
else:
print("The file is not in {0} , retry !".format(files))
Ok, the file is in ['Image_Processing_Tutorial_3.ipynb', 'Orientation.ipynb', '.ipynb_checkpoints', 'Image_Processing_Tutorial_4.ipynb', 'bugs.jpg', 'Image_Processing_Tutorial_2.ipynb', 'balls.jpg', 'Image_Processing_Tutorial_1.ipynb']
In order to explain the concepts of inertia and aspect ratio, we use this magnificient hand drawing:
im = Image.open(path)
Nc, Nl = im.size
im = im.resize((Nc // 4 ,Nl // 4),Image.ANTIALIAS)
fig, ax = plt.subplots()
#ax.axis("off")
plt.imshow(im)
plt.colorbar()
plt.show()
<IPython.core.display.Javascript object>
R, G, B = im.split()
R = np.array(R)
G = np.array(G)
B = np.array(B)
plt.figure()
plt.hist(R.flatten(), bins = np.arange(256), histtype = "stepfilled", color = "r", alpha = .3, label = "Red")
plt.hist(G.flatten(), bins = np.arange(256), histtype = "stepfilled", color = "g", alpha = .3, label = "Green")
plt.hist(B.flatten(), bins = np.arange(256), histtype = "stepfilled", color = "b", alpha = .3, label = "Blue")
plt.grid()
plt.legend()
plt.show()
<IPython.core.display.Javascript object>
Thresholding level is obvious:
Bt = np.where(B < 50, 1, 0)
plt.figure()
plt.imshow(Bt, cmap = cm.gray)
plt.colorbar()
plt.show()
<IPython.core.display.Javascript object>
Btc = ndimage.binary_closing(Bt, structure = np.ones((10, 10)))
Bl, number = ndimage.measurements.label(Btc)
plt.figure()
plt.imshow(np.where(Bl !=0, Bl, np.nan), cmap = cm.jet)
plt.show()
number
<IPython.core.display.Javascript object>
10
obj = ndimage.measurements.find_objects(Bl)
len(obj)
10
Inertia matrix of an objectΒΆ
The object represented bellow is stretched in a direction. Letβs see how we can use its inertia matrix to determine in which direction and how much it is stretched.
plt.figure()
plt.imshow(np.array(im)[obj[1]])
plt.show()
<IPython.core.display.Javascript object>
The inertia matrix of a 2D object can be defined as follows:
This matrix is symmetric and, as a consequence, it can be diagonalized in a new frame rotated by an angle \(\theta\) in the plane. This frame is composed of the two normalized eigenvectors \((\vec e_1, \vec e_2)\) of the matrix. In this frame, the matrix has two eigenvalues \((I_1, I_2)\) ordered so that \(I_1 \geq I_2\). Then: * \(\theta = (\vec x, \vec e_1)\) and, * The aspect ratio \(a = \sqrt{I_1 / I_2}\).
The angle \(\theta\) gives the direction of the elongation of the object and \(a\) shows how much it is elongated. For example, if \(a == 1\), the object is not elongated whereas if \(a=10\) it is 10 times longer in direction 1 than in direction 2 in an inertial point of view.
data = pd.DataFrame(columns = ["area", "xg", "yg", "Ixx", "Iyy", "Ixy", "I1", "I2", "theta"])
for i in range(len(obj)):
x, y = np.where(Bl == i+1)
xg, yg = x.mean(), y.mean()
x = x - xg
y = y - yg
A = len(x)
Ixx = (y**2).sum()
Iyy = (x**2).sum()
Ixy = (x*y).sum()
I = np.array([[Ixx, -Ixy], [-Ixy, Iyy]])
eigvals, eigvecs = np.linalg.eig(I)
eigvals = abs(eigvals)
loc = np.argsort(eigvals)[::-1]
d = eigvecs[loc[0]]
d *= np.sign(d[0])
theta = np.degrees(np.arccos(d[1]))
eigvals = eigvals[loc]
data.loc[i] = [A, xg, yg, Ixx, Iyy, Ixy, eigvals[0], eigvals[1], theta]
data.sort_values("area", inplace = True, ascending = False)
data["aspect_ratio"] = (data.I1 / data.I2)**.5
data[["area","theta", "aspect_ratio"]]
area | theta | aspect_ratio | |
---|---|---|---|
1 | 27036.0 | 125.441405 | 3.367242 |
2 | 17453.0 | 99.074781 | 3.837729 |
4 | 4204.0 | 131.093224 | 7.616207 |
6 | 3039.0 | 2.379166 | 1.955832 |
5 | 2736.0 | 82.039928 | 1.359502 |
8 | 2291.0 | 39.208508 | 1.710753 |
0 | 1438.0 | 25.883299 | 1.024153 |
7 | 1433.0 | 117.968405 | 1.265832 |
9 | 905.0 | 15.912720 | 1.151798 |
3 | 872.0 | 149.487814 | 1.112203 |
fig = plt.figure()
counter = 1
for i in data.index.values:
ax = fig.add_subplot(3,4, counter)
z = Image.fromarray(np.array(im)[obj[i]])
z = z.rotate(-data.loc[i, "theta"]+90, expand = True)
z = np.array(z)
plt.imshow(z)
plt.title(str(i))
ax.axis("off")
counter += 1
#plt.grid()
plt.show()
<IPython.core.display.Javascript object>