使用Opencv python进行精确测量

时间:2018-03-28 09:24:29

标签: python opencv computer-vision dimension measurement

我实际上正在使用OpenCV和Python开发机器视觉项目。

目标:该项目的目标是以高精度测量组件的尺寸。

主要硬件

  • Basler 500万像素摄像头(aca-2500-14gm)

  • 红色背光(100毫米x 100毫米)(我的组件尺寸约为60毫米)

实验

由于我正在考虑非常严格的公差限制,我首先进行了精确研究。我将组件保留在背光源上并拍摄了100张图像而没有移动部件(想象像100帧的视频)。我测量了所有100张图像的外径(OD)。我的mm /像素比率 0.042 。我测量了测量的标准偏差,以找出精度,结果是 0.03 mm,这是不好的。触摸组件和设置因此我期望精度为0.005 mm。但是我已经退了一个数量级了。我正在使用OpenCV的Hough圆来计算组件的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

组件的图像:

enter image description here

由于在不影响设置的情况下拍摄图像,我希望测量的重复性大约为0.005毫米(5微米)(对于100张图像)。但事实并非如此。这是霍布圈的问题吗?或者我在这里缺少什么

1 个答案:

答案 0 :(得分:2)

Hough专为检测而设计,而非量化。如果您需要精确的测量,您必须使用专为此设计的库。 OpenCV不是用于量化,因此在那里具有较差的能力。

很久以前我写了一篇关于使用Radon变换进行更精确的尺寸估计的论文(Hough变换是离散化Radon变换的一种方法,它在某些情况下很快,但不精确):< / p>

  • C.L。 Luengo Hendriks,M。van Ginkel,P.W。 Verbeek和L.J. van Vliet,广义Radon变换:采样,精度和记忆考虑,模式识别38(12):2494-2505,2005,doi:10.1016/j.patcog.2005.04.018Here is a PDF

但是因为您的设置控制得非常好,所以您并不需要所有这些来获得精确的测量。这是一个非常简单的Python脚本来量化这些漏洞:

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)))

这给了我输出:

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

请注意,上面的代码中有很多假设。还有一个问题是相机没有正好位于物体正上方,您可以看到反射光线的孔的右侧。这肯定会增加这些测量的不精确性。但请注意,在测量物体时,我没有使用物体为圆形的知识(仅在将物体转换为直径时)。有可能使用圆度标准来克服一些成像缺陷。

上面的代码使用PyDIP,这是一个非常粗略的Python接口,用于C ++库DIPlib 3。 Python语法是C ++语法的直接翻译,对于Python人来说,有些事情仍然很尴尬(实际上,我认为它在C ++中更容易使用!)。但它专门针对量化,因此我建议您尝试使用它。