Minden pixel értékét egy küszöbérték felett 1-re, alatta 0-ra változtatjuk. Képfeldolgozáskor általában 1 helyett 255 értékűre van írva az előtér, 0 értékűre a háttér, vagyis az objektumokat tartalmazó pixelek fehérek, a többi pedig fekete. Ekkor bináris kép keletkezik, amelyet könnyen lehet elemezni. A küszöbérték meghatározása a felhasználó feladata, de vannak algoritmusok annak automatikus megtalálására (pl. Otsu módszere).
Ha a háttér intenzitása nem egyenletes a képen, akkor lokális, másnéven adaptív módszerrel eredményesebb lehet a bináris kép előállítása. Minden pixel egyedi küszöbértéket kap a környezeténben lévő intenzitások súlyozott átlaga alapján. Ez az adaptív módszer nyilvánvalóan sokkal lassabb, mint a globális.
Képek feldolgozásakor integrálás vagy deriválás helyett diszkrét műveletek vannak, mivel egy kép függvénye nem folytonos. Bináris képeken egy objektum területe a fehér képpontok száma, középpontjának koordinátái pedig a fehér képpontok koordinátáinak átlaga a két tengelyen.
$ A = \displaystyle\sum_{y=1}^{H} \displaystyle\sum_{x=1}^{W} I(x,y) $
$ \bar{x} = \dfrac{1}{A} ⋅ \displaystyle\sum_{y=1}^{H} \displaystyle\sum_{x=1}^{W} x ⋅ I(x,y) $
$ \bar{y} = \dfrac{1}{A} ⋅ \displaystyle\sum_{y=1}^{H} \displaystyle\sum_{x=1}^{W} y ⋅ I(x,y) $
Bináris képen lévő objektum dőlésszögének meghatározására több módszer létezik, ezekből az egyik a PCA, vagyis Principal Component Analysis. Ezt többdimenziós adatok dimenzióinak redukálására használják az adattudományban, de mivel az első főkomponens egy kétdimenziós képen a dőlésszög, így erre az egyszerű feladatra is megfelel. Matematikailag a dőlésszög annak az egyenesnek az x-tengellyel bezárt szöge, amely áthalad a középponton és az objektum pontjaitól mért távolságok összege minimális.
Az első lépés a PCA során a pontok eltolása úgy, hogy az új koordináta-rendszer origója a középpont, vagyis minden pont koordinátáiból ki kell vonni a középpont koordinátáit. A számolás az új pontokon folytatódik.
$ (2,3) \boldsymbol{\rightarrow} (-2,-1.6) $
$ (3,3) \boldsymbol{\rightarrow} (-1,-1.6) $
$ (4,4) \boldsymbol{\rightarrow} (0,-0.6) $
$ (5,5) \boldsymbol{\rightarrow} (1,0.4) $
$ (6;8) \boldsymbol{\rightarrow} (2,3.4) $
A kovarianciamátrix átlóján az egyes dimenziók varianciája, másnéven szórásnégyzete található, a mátrix többi eleme pedig az egyes dimenziópárok kovarianciája. Két dimenzió kovarianciája megmutatja, hogy azok mennyire mozognak együtt, vagyis mennyire van közöttük lineáris összefüggés. A mátrix tehát megpróbálja egyszerűen rögzíteni a dimenziók egymás közötti összefüggéseit. Képek esetén mérete 2×2.
$ C = \begin{pmatrix} Var(x') & Cov(x',y') \\ Cov(x',y') & Var(y') \end{pmatrix} $
$ Var(x') = \dfrac{1}{n} ⋅ \displaystyle\sum_{i=1}^{n} (x_i')^2 $
$ Var(y') = \dfrac{1}{n} ⋅ \displaystyle\sum_{i=1}^{n} (y_i')^2 $
$ Cov(x',y') = \dfrac{1}{n} ⋅ \displaystyle\sum_{i=1}^{n} x_i' ⋅ y_i' $
$ C = \begin{pmatrix} 2 & 2.4 \\ 2.4 & 3.44 \end{pmatrix} $
Ha egy vektor és egy mátrix össze van szorozva, az eredmény egy újabb vektor, amely az eredetihez képest kétféle változást szenvedhetett el: megváltozhatott a hossza és iránya. Ha adott a mátrix és keresünk hozzá egy olyan vektort, aminek nem fog a szorzás hatására megváltozni az iránya, akkor a mátrix sajátvektorát keressük. Cserébe a sajátvektor az a vektor, amelynek hossza a legnagyobbat változik. A sajátérték azt adja meg, hogy milyen mértékű ez a hosszváltozás. Először a sajátértékeket kell kiszámolni.
$ det(C - \lambda ⋅ I) = 0 $
$ I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} $
$ det \begin{pmatrix} 2 - \lambda & 2.4 \\ 2.4 & 3.44 - \lambda \end{pmatrix} = 0 $
A fenti képletekből végül egy másodfokú egyenlet alakul ki, amelynek két megoldása közül a dőlésszöget a nagyobbikból, vagyis a nagyobb jelentősséggel bíró sajátvektornak a sajátértékéből lehet majd kiszámolni.
$ \lambda_1 = 5.226 $
$ \lambda_2 = 0.214 $
Ismert a sajátérték, így ki kell számolni a hozzá tartozó sajátvektort. Behelyettesítés után a képlet megadja az első főkomponenst (PC.1), de fontos, hogy a sajátvektor nem lehet nullvektor. Az első főkomponens, vagyis a legnagyobb sajátértékhez tartozó sajátvektor egy bináris képen az objektum irányvektora, amelyből könnyen számolható a dőlésszög is. A méret, középpont és dőlésszög ismeretében például egy kamerával szerelt robot magabiztosan rá tud fogni objektumokra. A PCA számítógéppel milliszekundumok alatt lefuthat.
$ (C - \lambda ⋅ I) ⋅ \vec{v} = 0 $
$ \begin{pmatrix} 2 - \lambda & 2.4 \\ 2.4 & 3.44 - \lambda \end{pmatrix} ⋅ \begin{pmatrix} v_1 \\ v_2 \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \end{pmatrix} $
$ \begin{pmatrix} -3.226 & 2.4 \\ 2.4 & -1.786 \end{pmatrix} ⋅ \begin{pmatrix} v_1 \\ v_2 \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \end{pmatrix} $
$ PC. 1 = \begin{pmatrix} 1 \\ 1.344 \end{pmatrix} $
$ \varphi = atan2(1.344, 1) = 53.35° $
Csavarkulcs fénykép: Letöltés
Képszekvencia: Letöltés
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# Open the image and convert to grayscale
image = Image.open("path-to-resources/wrench.png").convert("L")
# Convert image to a NumPy array
data = np.array(image, dtype=np.uint8)
# Apply thresholding
threshold = 135
data = np.where(data > threshold, 255, 0)
# Convert image from {0, 255} to {0, 1} (Normalize)
data = data / 255
# Helper arrays to calculate the center
x_range = np.arange(0, data.shape[1])
y_range = np.arange(0, data.shape[0])
# Calculate area and center
area = data.sum()
x_cntr = np.matmul(data, x_range).sum() / data.sum()
y_cntr = np.matmul(data.T, y_range).sum() / data.sum()
# Display the image
plt.imshow(data, cmap="gray")
plt.plot(x_cntr, y_cntr, "og", markersize=5) # Mark center with green circle
plt.title("Center point")
plt.axis('off') # Hide the axis
plt.show()
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# Open the image and convert to grayscale
image = Image.open("path-to-resources/wrench.png").convert("L")
# Convert image to a NumPy array
data = np.array(image, dtype=np.uint8)
# Apply thresholding
threshold = 135
data = np.where(data > threshold, 255, 0)
# Convert image from {0, 255} to {0, 1} (Normalize)
data = data / 255
# Helper arrays to calculate the center
x_range = np.arange(0, data.shape[1])
y_range = np.arange(0, data.shape[0])
# Calculate area and center
area = data.sum()
x_cntr = np.matmul(data, x_range).sum() / area
y_cntr = np.matmul(data.T, y_range).sum() / area
# --- PCA (Principal Component Analysis) ---
# Extract foreground pixel coordinates
y, x = np.nonzero(data)
coords = np.column_stack((x, y))
# Centered array
cntr_coords = coords - (x_cntr, y_cntr)
# Covariance matrix
cov_matrix = np.cov(cntr_coords, rowvar=False)
# Eigen value decomposition (EVD) to find the principal components
eig_vals, eig_vecs = np.linalg.eigh(cov_matrix)
# Eigenvector corresponding to the largest eigenvalue
pr_eig_vec = eig_vecs[:, np.argmax(eig_vals)]
# Orientation angle in radians
orientation = np.arctan2(pr_eig_vec[1], pr_eig_vec[0])
# Start and End point for the orientation line
half_len = 500
x_line = [x_cntr - half_len * np.cos(orientation), x_cntr + half_len * np.cos(orientation)]
y_line = [y_cntr - half_len * np.sin(orientation), y_cntr + half_len * np.sin(orientation)]
# Display the image
plt.imshow(data, cmap="gray")
plt.plot(x_line, y_line, color="red", linewidth=3) # Draw a red line on the image
plt.plot(x_cntr, y_cntr, "og", markersize=5) # Mark center with green circle
plt.title("Center and orientation")
plt.axis('off') # Hide the axis
plt.show()
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import os
# Folder containing the image sequence
folder_path = "path-to-resources/wrench-sequence"
# Set threshold value
threshold = 175
# Loop through each image in the folder
for filename in sorted(os.listdir(folder_path)):
if filename.startswith("wrench00108"):
# Open the image and convert to grayscale
image_path = os.path.join(folder_path, filename)
image = Image.open(image_path).convert("L")
# Convert image to a NumPy array
data = np.array(image, dtype=np.uint8)
# Apply thresholding
data = np.where(data > threshold, 255, 0)
# Convert image from {0, 255} to {0, 1} for center calculation
data = data / 255
# Helper arrays to calculate the center
x_range = np.arange(0, data.shape[1])
y_range = np.arange(0, data.shape[0])
# Calculate area and center
area = data.sum()
x_cntr = np.matmul(data, x_range).sum() / area
y_cntr = np.matmul(data.T, y_range).sum() / area
# --- PCA (Principal Component Analysis) ---
# Extract foreground pixel coordinates
y, x = np.nonzero(data)
coords = np.column_stack((x, y))
# Centered array
cntr_coords = coords - (x_cntr, y_cntr)
# Covariance matrix
cov_matrix = np.cov(cntr_coords, rowvar=False)
# Eigen value decomposition (EVD) to find the principal components
eig_vals, eig_vecs = np.linalg.eigh(cov_matrix)
# Eigenvector corresponding to the largest eigenvalue
pr_eig_vec = eig_vecs[:, np.argmax(eig_vals)]
# Orientation angle in radians
orientation = np.arctan2(pr_eig_vec[1], pr_eig_vec[0])
# Start and end point for the orientation line
half_len = 300
x_line = [x_cntr - half_len * np.cos(orientation), x_cntr + half_len * np.cos(orientation)]
y_line = [y_cntr - half_len * np.sin(orientation), y_cntr + half_len * np.sin(orientation)]
# Display the image
plt.figure(1); plt.clf() # This is needed to refresh the image
plt.imshow(image, cmap="gray")
plt.plot(x_line, y_line, color="red", linewidth=3) # Draw a red line on the image
plt.plot(x_cntr, y_cntr, "og", markersize=5) # Mark center with green circle
plt.title(f"Orientation of {filename}")
plt.axis('off') # Hide the axis
plt.pause(.033) # This is needed to refresh the image