I am actually working on a Machine Vision project using OpenCV and Python.
Objective : The objective of the project is to measure the dimensions of a component with high accuracy.
Main Hardware :
Basler 5MP camera (aca-2500-14gm)
A red backlight (100 mm x 100 mm) (Size of my component is around 60mm)
Experiment
Since I am Looking at very tight tolerance limits, I first did a precision study. I kept the component on the backlight source and took 100 images without moving the part (imagine like a video with 100 frames). I measured the Outer Diameter(OD) of all the 100 images. My mm/pixel ratio is 0.042. I measured the standard deviation of the measurement to find out the precision, which turned out to be around 0.03 mm which is bad. The component nor the setup is touched thus I was expecting a precision of 0.005 mm. But I am off by an order of magnitude. I am using OpenCV's Hough circle to calculate the OD of the component.
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)
OUTPUT: 0.024
The image of the component:
Since the Images are taken without disturbing the setup, I expect the measurement's repeatability to be around 0.005 mm (5 microns) (For 100 images).But this is not so. Is it a problem of hough circle? or what am I missing here