使用 Opencv 进行精确测量 python
Precision Measurement with Opencv python
我实际上正在使用 OpenCV 和 Python 进行机器视觉项目。
Objective : 项目objective是高精度测量一个组件的尺寸。
主要硬件 :
Basler 5MP 相机 (aca-2500-14gm)
红色背光灯(100mm x 100mm)(我的元件尺寸大约是60mm)
实验
由于我正在研究非常严格的公差限制,所以我首先进行了精确研究。我将组件放在背光源上,并在不移动部件的情况下拍摄了 100 张图像(想象一下有 100 帧的视频)。我测量了所有 100 张图像的外径 (OD)。我的 mm/pixel 比率是 0.042。我测量了测量的标准偏差以找出精度,结果是 0.03 毫米左右,这是不好的。组件和设置都没有被触及,因此我期望精度为 0.005 毫米。但我偏离了一个数量级。我正在使用OpenCV的霍夫圆来计算组件的OD。
代码:
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)
输出:0.024
组件图片:
由于图像是在不干扰设置的情况下拍摄的,我预计测量的可重复性约为 0.005 毫米(5 微米)(对于 100 张图像)。但事实并非如此。是霍夫圆的问题吗?或者我在这里缺少什么
Hough 是为检测而设计的,而不是为量化而设计的。如果您想要精确的测量,则必须使用为此设计的库。 OpenCV 不适合量化,因此在这方面的能力很差。
很久以前我写了一篇关于使用 Radon 变换更精确地估计尺寸的论文(Hough 变换是离散化 Radon 变换的一种方法,它在某些情况下速度很快,但不精确):
- C.L。 Luengo Hendriks,M. van Ginkel,P.W。维贝克和 L.J。 van Vliet,广义 Radon 变换:采样、精度和内存注意事项,模式识别 38(12):2494–2505, 2005, doi:10.1016/j.patcog.2005.04.018. Here is a PDF.
但是因为您的设置控制得很好,所以您实际上并不需要所有这些来获得精确的测量值。这里有一个非常straight-forwardPython的脚本来量化这些漏洞:
import diplib as dip
import math
# Load image and set pixel size
img = dip.ImageRead('N6uqw.jpg')
img.SetPixelSize(0.042, "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)))
这给了我输出:
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
请注意,上面的代码中有很多假设。还有一个问题是相机没有恰好在物体上方居中,您可以看到反射光的孔的右侧。这肯定会增加这些测量的不精确性。但还要注意,我在测量物体时并没有使用物体是圆形的知识(仅在将面积转换为直径时)。或许可以使用圆度标准来克服一些成像缺陷。
上面的代码使用了 DIPlib,一个具有相当粗糙的 Python 接口的 C++ 库。 Python语法是C++语法的直译,有些东西还是挺别扭的non-Pythonic。但它专门针对量化,因此我建议您在您的应用程序中尝试一下。
我实际上正在使用 OpenCV 和 Python 进行机器视觉项目。
Objective : 项目objective是高精度测量一个组件的尺寸。
主要硬件 :
Basler 5MP 相机 (aca-2500-14gm)
红色背光灯(100mm x 100mm)(我的元件尺寸大约是60mm)
实验
由于我正在研究非常严格的公差限制,所以我首先进行了精确研究。我将组件放在背光源上,并在不移动部件的情况下拍摄了 100 张图像(想象一下有 100 帧的视频)。我测量了所有 100 张图像的外径 (OD)。我的 mm/pixel 比率是 0.042。我测量了测量的标准偏差以找出精度,结果是 0.03 毫米左右,这是不好的。组件和设置都没有被触及,因此我期望精度为 0.005 毫米。但我偏离了一个数量级。我正在使用OpenCV的霍夫圆来计算组件的OD。
代码:
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)
输出:0.024
组件图片:
由于图像是在不干扰设置的情况下拍摄的,我预计测量的可重复性约为 0.005 毫米(5 微米)(对于 100 张图像)。但事实并非如此。是霍夫圆的问题吗?或者我在这里缺少什么
Hough 是为检测而设计的,而不是为量化而设计的。如果您想要精确的测量,则必须使用为此设计的库。 OpenCV 不适合量化,因此在这方面的能力很差。
很久以前我写了一篇关于使用 Radon 变换更精确地估计尺寸的论文(Hough 变换是离散化 Radon 变换的一种方法,它在某些情况下速度很快,但不精确):
- C.L。 Luengo Hendriks,M. van Ginkel,P.W。维贝克和 L.J。 van Vliet,广义 Radon 变换:采样、精度和内存注意事项,模式识别 38(12):2494–2505, 2005, doi:10.1016/j.patcog.2005.04.018. Here is a PDF.
但是因为您的设置控制得很好,所以您实际上并不需要所有这些来获得精确的测量值。这里有一个非常straight-forwardPython的脚本来量化这些漏洞:
import diplib as dip
import math
# Load image and set pixel size
img = dip.ImageRead('N6uqw.jpg')
img.SetPixelSize(0.042, "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)))
这给了我输出:
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
请注意,上面的代码中有很多假设。还有一个问题是相机没有恰好在物体上方居中,您可以看到反射光的孔的右侧。这肯定会增加这些测量的不精确性。但还要注意,我在测量物体时并没有使用物体是圆形的知识(仅在将面积转换为直径时)。或许可以使用圆度标准来克服一些成像缺陷。
上面的代码使用了 DIPlib,一个具有相当粗糙的 Python 接口的 C++ 库。 Python语法是C++语法的直译,有些东西还是挺别扭的non-Pythonic。但它专门针对量化,因此我建议您在您的应用程序中尝试一下。