Je travaille actuellement sur un projet de vision industrielle utilisant OpenCV et Python.
Objectif : L'objectif du projet est de mesurer les dimensions d'un composant avec une grande précision.
Matériel principal :
Appareil photo Basler 5MP (aca-2500-14gm)
Un rétro-éclairage rouge (100 mm x 100 mm) (la taille de mon composant est d'environ 60 mm)
Expérience
Depuis que je regarde des limites de tolérance très strictes, j'ai d'abord fait une étude de précision. J'ai gardé le composant sur la source de rétroéclairage et pris 100 images sans déplacer la pièce (imaginez comme une vidéo avec 100 images). J'ai mesuré le diamètre extérieur (OD) de toutes les 100 images. Mon rapport mm/pixel est 0,042 . J'ai mesuré l'écart type de la mesure pour trouver la précision, qui s'est avérée être d'environ 0,03 mm, ce qui est mauvais . Le composant ni la configuration sont touchés donc je m'attendais à une précision de 0,005 mm. Mais je suis loin d'un ordre de grandeur. J'utilise le cercle Hough d'OpenCV pour calculer l'OD du composant.
Code:
import sys
import pickle
import cv2
import matplotlib.pyplot as plt
import glob
import os
import numpy as np
import pandas as pd
def find_circles(image,dp=1.7,minDist=100,param1=50,param2=50,minRadius=0,maxRadius=0):
""" finds the center of circular objects in image using hough circle transform
Keyword arguments
image -- uint8: numpy ndarray of a single image (no default).
dp -- Inverse ratio of the accumulator resolution to the image resolution (default 1.7).
minDist -- Minimum distance in pixel distance between the centers of the detected circles (default 100).
param1 -- First method-specific parameter (default = 50).
param2 -- Second method-specific parameter (default = 50).
minRadius -- Minimum circle radius in pixel distance (default = 0).
maxRadius -- Maximum circle radius in pixel distance (default = 0).
Output
center -- Tuple: (x,y).
radius -- int : radius.
ERROR if circle is not detected. returns(-1) in this case
"""
circles=cv2.HoughCircles(image,
cv2.HOUGH_GRADIENT,
dp = dp,
minDist = minDist,
param1=param1,
param2=param2,
minRadius=minRadius,
maxRadius=maxRadius)
if circles is not None:
circles = circles.reshape(circles.shape[1],circles.shape[2])
return(circles)
else:
raise ValueError("ERROR!!!!!! circle not detected try tweaking the parameters or the min and max radius")
def find_od(image_path_list):
image_path_list.sort()
print(len(image_path_list))
result_df = pd.DataFrame(columns=["component_name","measured_dia_pixels","center_in_pixels"])
for i,name in enumerate(image_path_list):
img = cv2.imread(name,0) # read the image in grayscale
ret,thresh_img = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY_INV)
thresh_img = cv2.bilateralFilter(thresh_img,5,91,91) #smoothing
edges = cv2.Canny(thresh_img,100,200)
circles = find_circles(edges,dp=1.7,minDist=100,param1=50,param2=30,minRadius=685,maxRadius=700)
circles = np.squeeze(circles)
result_df.loc[i] = os.path.basename(name),circles[2]*2,(circles[0],circles[1])
result_df.sort_values("component_name",inplace=True)
result_df.reset_index(drop=True,inplace=True)
return(result_df)
df = find_od(glob.glob("./images/*"))
mean_d = df.measured_dia_pixels.mean()
std_deviation = np.sqrt(np.mean(np.square([abs(x-mean_d) for x in df.measured_dia_pixels])))
mm_per_pixel = 0.042
print(std_deviation * mm_per_pixel)
SORTIE: 0,024
L'image du composant:
Étant donné que les images sont prises sans perturber la configuration, je m'attends à ce que la répétabilité de la mesure soit d'environ 0,005 mm (5 microns) (pour 100 images), mais ce n'est pas le cas. Est-ce un problème de cercle difficile? ou qu'est-ce qui me manque ici
Hough est conçu pour détecter , pas pour quantifier . Si vous voulez des mesures précises, vous devrez utiliser une bibliothèque conçue pour cela. OpenCV n'est pas destiné à la quantification, et a par conséquent de faibles capacités là-bas.
Il y a longtemps, j'ai écrit un article sur des estimations de taille plus précises à l'aide de la transformée de Radon (la transformée de Hough est un moyen de discrétiser la transformée de Radon, elle est rapide dans certains cas, mais pas précise):
Mais parce que votre configuration est si bien contrôlée, vous n'avez pas vraiment besoin de tout cela pour obtenir une mesure précise. Voici un script Python très simple pour quantifier ces trous:
import PyDIP as dip
import math
# Load image and set pixel size
img = dip.ImageReadTIFF('/home/cris/tmp/machined_piece.tif')
img.SetPixelSize(dip.PixelSize(0.042 * dip.Units("mm")))
# Extract object
obj = ~dip.Threshold(dip.Gauss(img))[0]
obj = dip.EdgeObjectsRemove(obj)
# Remove noise
obj = dip.Opening(dip.Closing(obj,9),9)
# Measure object area
lab = dip.Label(obj)
msr = dip.MeasurementTool.Measure(lab,img,['Size'])
objectArea = msr[1]['Size'][0]
# Measure holes
obj = dip.EdgeObjectsRemove(~obj)
lab = dip.Label(obj)
msr = dip.MeasurementTool.Measure(lab,img,['Size'])
sz = msr['Size']
holeAreas = []
for ii in sz.Objects():
holeAreas.append(sz[ii][0])
# Add hole areas to main object area
objectArea += sum(holeAreas)
print('Object diameter = %f mm' % (2 * math.sqrt(objectArea / math.pi)))
for a in holeAreas:
print('Hole diameter = %f mm' % (2 * math.sqrt(a / math.pi)))
Cela me donne la sortie:
Object diameter = 57.947768 mm
Hole diameter = 6.540086 mm
Hole diameter = 6.695357 mm
Hole diameter = 15.961935 mm
Hole diameter = 6.511002 mm
Hole diameter = 6.623011 mm
Notez qu'il y a beaucoup d'hypothèses dans le code ci-dessus. Il y a aussi un problème avec la caméra qui n'est pas centrée exactement au-dessus de l'objet, vous pouvez voir le côté droit des trous reflétant la lumière. Cela ajoutera certainement de l'imprécision à ces mesures. Mais notez également que je n'ai pas utilisé la connaissance que l'objet est rond lors de la mesure (uniquement lors de la conversion de la zone en diamètre). Il pourrait être possible d'utiliser le critère d'arrondi pour surmonter certaines des imperfections de l'imagerie.
Le code ci-dessus utilise PyDIP, une interface très approximative Python avec une bibliothèque C++, DIPlib . La syntaxe Python est une traduction de la syntaxe C++, et certaines choses sont encore assez gênantes pour une personne Python (en fait, il est plus facile à utiliser en C++, je pense!). Mais elle vise spécifiquement la quantification, et donc Je vous recommande de l'essayer pour votre application.