测量金属零件孔的直径图片,用远心、单色相机和opencv拍摄

时间:2022-11-19
本文介绍了测量金属零件孔的直径图片,用远心、单色相机和opencv拍摄的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

设置:

  • 相机:Blackfly S Mono 20.0 MP
  • 镜头:光远心镜头 TC23080
  • 灯:16 个绿色 LED
  • Python:3.7.3
  • openCV:4.0+

抱歉图片链接,但一张图片大约 20MB,也不想失去任何质量

图像样本:

https://drive.google.com/file/d/11PU-5fzvSJt1lKlmP-lQXhdsuCJPGKbN/view?usp=sharinghttps://drive.google.com/file/d/1B3lSFx8YvTYv3hzuuuYtphoHBuyEdc4o/view

案例:将有不同形状的金属零件,从 5x5 到 10x10 尺寸(cm).在这些金属部件内部有很多从 2 到 10~ 的圆孔,必须非常准确地检测出来.孔的实际尺寸是未知的,因为可能的零件种类繁多.目标是使用 OpenCV 编写一个通用算法,该算法可以处理任何金属部件并检测圆孔.

Case: There will be metal parts with different shapes from 5x5 to 10x10 size(cm). Inside these metal parts there are plenty of circular holes from 2 to 10~ that have to be detected very accurately. The actual size of holes are unknown, as there are huge variety of possible parts. The goal is to write a generic algorithm with OpenCV, that could work with any metal parts and detect circular holes.

我们尝试过的:我们尝试使用 HoughCircles 算法检测漏洞,但几乎没有成功.该算法要么过于敏感,要么根本没有检测到漏洞.我们尝试了不同的 param1 和 param2 值,但没有成功.在使用 HoughCircles 之前,我们也尝试过模糊图像并通过 Canny,但这种方法并没有产生更好的结果.同样的算法在分辨率较低的图片上效果明显更好.但是,不能牺牲分辨率,因为准确性在这个项目中非常重要.

What we have tried: We have tried to detect the holes with HoughCircles algorithm with little to no success. The algorithm is either too sensitive, or it does not detect the holes at all. We have experimented with different param1 and param2 values with no success. We have also tried blurring the image and passing it through Canny before using HoughCircles, but such an approach did not produce better results. The very same algorithm works significantly better with lower resolution pictures. However, resolution cannot be sacrificed as accuracy is extremely important in this project.

https://drive.google.com/file/d/1TRdDbperi37bha0uJVALS4C2dBuaNz6u/view?usp=sharing

使用以下参数检测到上述圆圈:

The above circles were detected with the following parameters:

minradius=0
maxradius=0
dp=1
param1=100
param2=21

通过玩弄上面的参数,我们几乎可以得到我们想要的结果.当我们对不同的图片使用相同的参数时,就会出现问题.

By playing around with the above parameters, we can get almost the results that we want. The problem arises when we use the same parameters with different pictures.

我们想要得到的最终结果是给定圆的直径非常准确,我们希望相同的算法可以用于不同的零件图片

The end result we want to get is the diameter of a given circle with great accuracy, and we want the same algorithm to be usable on different part pictures

这个问题与其他问题的不同之处在于我们不知道给定圆的大致半径(因此我们无法操纵 minradius、maxradius、param1、param2 或任何其他值).

What makes this problem different from the other ones posted is that we do not know the approximate radius of a given circle (so we cannot manipulate minradius, maxradius, param1, param2 or any other values).

推荐答案

关于这些图片,我们知道两件事:

We know two things about these images:

  1. 这些物体在明亮的背景上是深色的.
  2. 孔都是圆形,我们要测量所有孔.

所以我们需要做的就是检测漏洞.这实际上是微不足道的:

So all we need to do is detect holes. This is actually quite trivial:

  1. 阈值(背景变成对象,因为它很亮)
  2. 移除边缘对象

剩下的是洞.不包括任何接触图像边缘的孔.我们现在可以轻松测量这些孔.由于我们假设它们是循环的,我们可以做三件事:

What is left are the holes. Any holes touching the image edge will not be included. We can now easily measure these holes. Since we assume they're circular, we can do three things:

  1. 计算对象像素,这是对区域的无偏估计.我们根据面积确定孔径.
  2. 检测轮廓,找到质心,然后使用例如以轮廓点到质心的平均距离为半径.
  3. 将图像强度归一化,使背景照明的强度为 1,其中有孔的物体的强度为 0.每个孔的强度的积分是该区域的亚像素精度估计值(请参阅在底部快速解释此方法).

这个 Python 代码,使用 DIPlib (免责声明:我是作者)展示了如何做这三种方法:

This Python code, using DIPlib (disclaimer: I'm an author) shows how to do these three approaches:

import diplib as dip
import numpy as np

img = dip.ImageRead('geriausias.bmp')
img.SetPixelSize(1,'um') # Usually this info is in the image file
bin, thresh = dip.Threshold(img)
bin = dip.EdgeObjectsRemove(bin)
bin = dip.Label(bin)
msr = dip.MeasurementTool.Measure(bin, features=['Size','Radius'])
print(msr)
d1 = np.sqrt(np.array(msr['Size'])[:,0] * 4 / np.pi)
print("method 1:", d1)
d2 = np.array(msr['Radius'])[:,1] * 2
print("method 2:", d2)

bin = dip.Dilation(bin, 10) # we need larger regions to average over so we take all of the light
                            # coming through the hole into account.
img = (dip.ErfClip(img, thresh, thresh/4, "range") - (thresh*7/8)) / (thresh/4)
msr = dip.MeasurementTool.Measure(bin, img, features=['Mass'])
d3 = np.sqrt(np.array(msr['Mass'])[:,0] * 4 / np.pi)
print("method 3:", d3)

这给出了输出:

  |       Size |                                            Radius | 
- | ---------- | ------------------------------------------------- | 
  |            |        Max |       Mean |        Min |     StdDev | 
  |      (m) |       (m) |       (m) |       (m) |       (m) | 
- | ---------- | ---------- | ---------- | ---------- | ---------- | 
1 |  6.282e+04 |      143.9 |      141.4 |      134.4 |      1.628 | 
2 |  9.110e+04 |      171.5 |      170.3 |      168.3 |     0.5643 | 
3 |  6.303e+04 |      143.5 |      141.6 |      133.9 |      1.212 | 
4 |  9.103e+04 |      171.6 |      170.2 |      167.3 |     0.6292 | 
5 |  6.306e+04 |      143.9 |      141.6 |      126.5 |      2.320 | 
6 |  2.495e+05 |      283.5 |      281.8 |      274.4 |     0.9805 | 
7 |  1.176e+05 |      194.4 |      193.5 |      187.1 |     0.6303 | 
8 |  1.595e+05 |      226.7 |      225.3 |      219.8 |     0.8629 | 
9 |  9.063e+04 |      171.0 |      169.8 |      167.6 |     0.5457 | 

method 1: [282.8250363  340.57242408 283.28834869 340.45277017 283.36249824
 563.64770132 386.9715443  450.65294139 339.70023023]
method 2: [282.74577033 340.58808144 283.24878097 340.43862835 283.1641869
 563.59706479 386.95245928 450.65392268 339.68617582]
method 3: [282.74836803 340.56787463 283.24627163 340.39568372 283.31396961
 563.601641   386.89884807 450.62167913 339.68954136]

图像bin,在调用dip.Label之后,是一个整数图像,其中洞1的像素都为1,洞2的像素都为2,以此类推. 所以我们仍然保留测量尺寸和它们是哪些孔之间的关系.我没有费心制作显示图像尺寸的标记图像,但是正如您在其他答案中看到的那样,这很容易做到.

The image bin, after calling dip.Label, is an integer image where pixels for hole 1 all have value 1, those for hole 2 have value 2, etc. So we still keep the relationship between measured sizes and which holes they were. I have not bothered making a markup image showing the sizes on the image, but this can easily be done as you've seen in other answers.

因为图像文件中没有像素大小信息,所以我对每个像素强加了 1 微米.这可能不正确,您必须进行校准以获取像素大小信息.

Because there is no pixel size information in the image files, I've imposed 1 micron per pixel. This is likely not correct, you will have to do a calibration to obtain pixel size information.

这里的一个问题是背景照明太亮,导致像素饱和.这会导致孔看起来比实际更大.校准系统非常重要,以便背景照明接近相机可以记录的最大值,但不能达到最大值或更高.例如,尝试将背景强度设为 245 或 250.第 3 种方法受光照不好的影响最大.

A problem here is that the background illumination is too bright, giving saturated pixels. This causes the holes to appear larger than they actually are. It is important to calibrate the system so that the background illumination is close to the maximum that can be recorded by the camera, but not at that maximum nor above. For example, try to get the background intensity to be 245 or 250. The 3rd method is most affected by bad illumination.

对于第二张图像,亮度非常低,导致图像噪点过多.我需要将 bin = dip.Label(bin) 行修改为:

For the second image, the brightness is very low, giving a more noisy image than necessary. I needed to modify the line bin = dip.Label(bin) into:

bin = dip.Label(bin, 2, 500) # Imposing minimum object size rather than filtering

改为进行一些噪声过滤可能更容易.输出是:

It's maybe easier to do some noise filtering instead. The output was:

  |       Size |                                            Radius | 
- | ---------- | ------------------------------------------------- | 
  |            |        Max |       Mean |        Min |     StdDev | 
  |      (m) |       (m) |       (m) |       (m) |       (m) | 
- | ---------- | ---------- | ---------- | ---------- | ---------- | 
1 |  4.023e+06 |      1133. |      1132. |      1125. |     0.4989 | 

method 1: [2263.24621554]
method 2: [2263.22724164]
method 3: [2262.90068056]


快速解释方法#3

该方法在 Lucas van Vliet 的博士论文中有描述(代尔夫特理工大学,1993 年),第 6 章.

这样想:通过孔的光量与孔的面积成正比(实际上它由面积"x光强度"给出).通过将通过孔的所有光相加,我们就知道了孔的面积.该代码将对象的所有像素强度以及对象外部的一些像素相加(我在那里使用 10 个像素,要走多远取决于模糊).

Think of it this way: the amount of light that comes through the hole is proportional to the area of the hole (actually it is given by 'area' x 'light intensity'). By adding up all the light that comes through the hole, we know the area of the hole. The code adds up all pixel intensities for the object as well as some pixels just outside the object (I'm using 10 pixels there, how far out to go depends on the blurring).

erfclip 函数称为软剪辑".函数,它确保孔内的强度一致为 1,孔外的强度一致为 0,并且仅在边缘周围留下中间灰度值.在这种特殊情况下,此软剪辑避免了成像系统中的偏移以及对光强度的不良估计的一些问题.在其他情况下,避免被测物体颜色不均匀的问题更为重要.它还可以减少噪音的影响.

The erfclip function is called a "soft clip" function, it ensures that the intensity inside the hole is uniformly 1, and the intensity outside the hole is uniformly 0, and only around the edges it leaves intermediate gray-values. In this particular case, this soft clip avoids some issues with offsets in the imaging system, and poor estimates of the light intensity. In other cases it is more important, avoiding issues with uneven color of the objects being measured. It also reduces the influence of noise.

这篇关于测量金属零件孔的直径图片,用远心、单色相机和opencv拍摄的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:带虚线或虚线的opencv矩形 下一篇:立体校准 Opencv Python 和视差图

相关文章

最新文章