Perspektiva obrazu - měření závitu

Cvičení je pokračováním cvičení zaměřeného na práci s polárními souřadnicemi při využití nestandardních 360° objektivů od firmy Opto Engineering. Způsob použití takových objektivů je demonstrován v tutoriálu na stránkách výrobce. Ve cvičení jsou použita data získaná z optického systému typu boroskopická sonda.

Boroskopická sonda

Boroskopická sonda díky zrcadlu umožňuje podívat se 360 ° dokola kolem sebe. Typickými aplikacemi jsou průmyslové inspekce děr či kontroly správnosti závitů.

No description has been provided for this image

Teorie k měření

Matice s metrickým závitem jsou podle norem ČSN ISO 261, 262 definovány pomocí písmene M a číslice průměru závitu v názvu. Dále jsou definovány veličiny stoupání závitu, které lze rozdělit na standardní, m (metric) a jemná stoupání, m-f, m-f2, m-f3 (metric-fine). Průměr závitu (D) je definován v mm, stoupání závitu (P) je také definováno v mm.

No description has been provided for this imageNo description has been provided for this image

Specifickou veličinou je úhel stoupání ($\varphi$), který může být využit jako poznávací znamení. Je definován jako:

$$\varphi = \arctan \left(\frac{P}{\pi D} \right)$$

Obrázky i vzorce jsou přejaty z webu ZDE. Stejně tak tam lze najít více informací k tématu, pokud by někoho zaujalo.


Import knihoven a konfigurace

Některé funkce jsou k dispozici ve vlastním souboru s názvem library.py. Jedná se hlavně o drobnosti použitelné pro tohle cvičení.

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

from improutils import *

np.set_printoptions(precision=3)

%matplotlib inline
%run library.py
<Figure size 432x288 with 0 Axes>

Pomocné funkce

Z následujících funkcí je potřeba vybírat ty vhodné pro splnění úkolu.

Seznam funkcí pro přehlednost:


Úkol

Cílem úlohy je poznat, která matice byla snímána pomocí boroskopické sondy. Následujícím způsobem jsou zadány hodnoty jednotlivých matic s metrickým závitem.

Název D (mm) P, m (mm) P, m-f (mm) P, m-f2 (mm)
M22 22 2.5 2 1.5
M24 24 3 2 1.5
M30 30 3.5 2 1.5
M36 36 4 3 2

atd.

Obrázek ukazuje, co je to matice a jejich různé velikosti. Je čistě ilustrační.

No description has been provided for this image
# Databáze matic z normy
# name: d, m, f, f2
nuts = {
    'M22':	[22,	2.5,	2,	1.5],
    'M24':	[24,	3,	2,	1.5],
    'M30':	[30,	3.5,	2,	1.5],
    'M36':	[36,	4,	3,	2],
    'M42':	[42,	4.5,	3,	2],
    'M48':	[48,	5,	3,	2],
    'M52':	[52,	5,	3,	2],
    'M56':	[56,	5.5,	4,	2],
    'M60':	[60,	5.5,	4,	2],
    'M64':	[64,	6,	4,	3],
    'M68':	[68,	6,	4,	3],
    'M72':	[72,	6,	4,	2],
    'M76':	[76,	6,	4,	2],
    'M80':	[80,	6,	4,	2],
    'M90':	[90,	6,	4,	2],
    'M100':	[100,	6,	4,	2],
    'M110':	[110,	6,	4,	2],
    'M120':	[120,	6,	4,	2],
    'M125':	[125,	6,	4,	2]
}

Ps = ['m', 'm-f', 'm-f2']

1) Načtení a předzpracování

Načtěte obrázek matice snímané pomocí boroskopické sondy. Obrázek je čtvercový. Vytvořte z něj obdélníkový obrázek závitu pomocí vhodného převodu mezi souřadnými systémy. Rotujte obrázek tak, aby vnitřek kruhu byl na spodní straně obdélníku (viz dokumentace OpenCV). Obrázky zobrazte.

image_path = 'data/boro_nut.png' ### cesta k obrázku
image_grey = to_gray(load_image(image_path)) ### načtení obrazu
plot_images(image_grey)

image_trans = warp_to_cartesian(image_grey) ### správná transformace
print(image_trans.shape)
plot_images(image_trans)

img_rotated = rotate(image_trans, 90) ###

# Zobrazí obrázky
plot_images(image_grey, image_trans)
plot_images(img_rotated)
(2079, 662)
No description has been provided for this image No description has been provided for this image No description has been provided for this image No description has been provided for this image

2) Segmentace

Automaticky segmentujte část obrazu (vytvořte masku) obsahující pouze závit. Ořízněte obrázek podle vytvořené masky - výsledek lze vylepšit přidáním konstanty na ořez nahoře a dole, kde je závit špatně viditelný. Zobrazte obrázek.

img_bin = segmentation_auto_threshold(img_rotated) ### segmentace
tl_x, tl_y, w, h = cv2.boundingRect(img_bin) 
print(img_rotated.shape)
crop_const = 25 ### vlastní konstanta
img_crop = crop(img_rotated, tl_x, tl_y + crop_const, tl_x + w, tl_y + h - crop_const) ### ořez
plot_images(img_crop)
(662, 2079)
No description has been provided for this image

3) Detekce závitů

Pro předzpracování obrazu využijte filtr vhodný pro zachování hran. Seznamte se a experimentujte s hranovým detektorem typu Canny pro detekci hran - závitů. Obrázek hran zobrazte.

img_filtered = filtration_median(img_crop, 5) ### 
# img_filtered = img_crop
# img_filtered = cv2.Laplacian(img_crop, cv2.CV_64F)

plot_images(img_filtered)

img_edges = cv2.Canny(img_filtered, 0, 255) ### hranový detektor s vhodnými parametry

plot_images(img_edges)
No description has been provided for this image No description has been provided for this image

4) Zisk geometrických charakteristik

Aproximujte nalezené hrany pomocí přímek (úseček). K aproximaci se seznamte a experimentujte s metodou Houghovy (čti hafovy) transformace. Obzvlášť vhodná je metoda pravděpodobnostní Houghovy transformace (HoughLinesP).

Nastavte parametry funkce tak, aby hledala pouze delší čáry (ideálně delší než 300 px klidně s větší mezerou). Vhodný počet čar jsou jednotky až desítky. Samozřejmě ideální čáry jsou ty, které kopírují směr závitu.

Vykreslete čáry do obrázku a ten zobrazte. Můžete využít funkci draw_lines().

# linesP = cv2.HoughLinesP(img_edges, , ..., ..., ..., ..., ...) ### transformace s vhodnými parametry
linesP = cv2.HoughLinesP(img_edges, 1, 3.14159265359/180, 80, 30, 10) ### transformace s vhodnými parametry
linesP = cv2.HoughLinesP(img_edges, 1, 3.14159265359/180, 80, None, 30, 10) ### transformace s vhodnými parametry
img_lines = draw_lines(img_edges, linesP) ### kreslení

plot_images(img_lines)
No description has been provided for this image

5) Spočítejte úhel stoupání závitu

Pro všechny nalezené přímky spočítejte úhel stoupání závitu.

Vzhledem k nedokonalosti v převodu obrazu z polárních souřadnic je vhodné nalezené přímky filtrovat s použitím spočteného úhlu a délky. Nemá smysl započítávat přímky, jejichž pomyslný trojúhelník žádný úhel nesvírá, stejně tak, jako přímky kratší než např. polovina délky snímku.

Použijte vhodnou metodu pro určení jedné hodnoty úhlu stoupání ze všech spočtených. Hodnotu úhlu stoupání zobrazte ve stupních.

print(linesP)
[[[ 999   66 1033   66]]

 [[1465  189 1821  183]]

 [[ 441   16  736   16]]

 ...

 [[1092  142 1164  140]]

 [[ 920   15 1022   15]]

 [[1495  160 1532  159]]]
angles = list()
for line in linesP:
    ### výpočet
    line = line[0]
    x, y = line[2] - line[0], line[3] - line[1]
    
    norm = np.linalg.norm([x, y])
    
    angle = np.arccos(np.clip(np.dot([x/norm, y/norm], [1, 0]), -1.0, 1.0))
    
    # vhodná podmínka
    if angle > 0:
        angles.append(angle) 
    
# print(angles)

# angle_rad = np....(angles) ### vhodná metoda
angle_rad = np.mean(angles)
print(str(angle_rad) + ' rad')

angle_deg = np.degrees(angle_rad) ### převod
print(str(angle_deg) + ' °')
0.02052312938468789 rad
1.1758886961435382 °

6) Bonusová část - Identifikujte matici

Nalezněte konkrétní typ snímané matice z databáze nuts (proměnná zadefinovaná výše). Výsledek vypište ve tvaru: název, typ stoupání závitu.

Co dalšího byste potřebovali vědět pro ještě přesnější identifikaci matice?

𝜑=arctan(𝑃𝜋𝐷)
### Výpočty a klasifikace
# print(Ps)
# D, P
angles = []
for k, v in nuts.items():
    d = v[0]
    p = v[1]
    angle = np.arctan(p/(np.pi*d))
    angle = np.degrees(angle)
    angles.append(angle)
#     print(angle)
#     print(v)

diff = []
for angle in angles:
    diff.append(abs(angle - angle_deg))
    
# print(nuts.keys())
k = list(nuts.keys())
k[np.argmin(diff)]
'M90'