目标是找到代表 "hole" 和外边缘之间距离的线。 Canny 边缘检测是最好的方法吗?

The goal is to find the line that represents the distance between the "hole" and the outer edge. Is Canny edge detection the best approach?

目标是找到表示“孔”和外边缘(从黑色到白色的过渡)之间距离的线。我能够成功地将这张照片二值化并获得非常干净的黑白图像。下一步是找到它上面的(几乎)垂直线,并计算到这条垂直线和孔的中点的垂直距离。

original picture hole - zoomed in

ps:我说的“洞”就是影子。我正在将激光射入一个洞中。所以我们看到的线条是钢材,没有线条的黑色部分是一个洞。 2条白线作为测量距离的参考。

Canny 边缘检测是最好的方法吗?如果是这样,A、B 和 C 参数的合适值是多少?我无法调整它。我的噪音太大了。

你不能使用 fft 吗?边缘通常具有低频,这在高通滤波和反 fft 后会突出。

这还不完整;您必须花时间才能达到最终结果。但这个想法可能会对你有所帮助。

预处理器:

import os
import cv2
import numpy as np

主要代码:

# Read original image
dir = os.path.abspath(os.path.dirname(__file__))
im = cv2.imread(dir+'/'+'im.jpg')
h, w = im.shape[:2]
print(w, h)

# Convert image to Grayscale
imGray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
cv2.imwrite(dir+'/im_1_grayscale.jpg', imGray)

# Eliminate noise and display laser light better
imHLine = imGray.copy()
imHLine = cv2.GaussianBlur(imHLine, (0, 9), 21)  # 5, 51
cv2.imwrite(dir+'/im_2_h_line.jpg', imHLine)

# Make a BW mask to find the ROI of laser array
imHLineBW = cv2.threshold(imHLine, 22, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite(dir+'/im_3_h_line_bw.jpg', imHLineBW)

# Remove noise with mask and extract just needed area
imHLineROI = imGray.copy()
imHLineROI[np.where(imHLineBW == 0)] = 0
imHLineROI = cv2.GaussianBlur(imHLineROI, (0, 3), 6)
imHLineROI = cv2.threshold(imHLineROI, 25, 255, cv2.THRESH_BINARY)[1]  # 22
cv2.imwrite(dir+'/im_4_ROI.jpg', imHLineROI)

# Found laser array and draw box around it
cnts, _ = cv2.findContours(
    imHLineROI, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts.sort(key=lambda x: cv2.boundingRect(x)[0])
pts = []
for cnt in cnts:
    x2, y2, w2, h2 = cv2.boundingRect(cnt)
    if h2 < h/10:
        cv2.rectangle(im, (x2, y2), (x2+w2, y2+h2), (0, 255, 0), 1)
        pts.append({'x': x2, 'y': y2, 'w': w2, 'h': h2})

circle = {
    'left': (pts[0]['x']+pts[0]['w'], pts[0]['y']+pts[0]['h']/2),
    'right': (pts[1]['x'], pts[1]['y']+pts[1]['h']/2),
}
circle['center'] = calculateMiddlePint(circle['left'], circle['right'])
circle['radius'] = (circle['right'][0]-circle['left'][0])//2

# Draw line and Circle inside it
im = drawLine(im, circle['left'], circle['right'], color=(27, 50, 120))
im = cv2.circle(im, circle['center'], circle['radius'], (255, 25, 25), 3)

# Remove pepper/salt noise to find metal edge
imVLine = imGray.copy()
imVLine = cv2.medianBlur(imVLine, 17)
cv2.imwrite(dir+'/im_6_v_line.jpg', imVLine)

# Remove remove the shadows to find metal edge
imVLineBW = cv2.threshold(imVLine, 50, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite(dir+'/im_7_v_bw.jpg', imVLineBW)

# Finding the right vertical edge of metal
y1, y2 = h/5, h-h/5
x1 = horizantalDistance(imVLineBW, y1)
x2 = horizantalDistance(imVLineBW, y2)
pt1, pt2 = (x1, y1), (x2, y2)
imVLineBW = drawLine(imVLineBW, pt1, pt2)
cv2.imwrite(dir+'/im_8_v_bw.jpg', imVLineBW)

# Draw lines
im = drawLine(im, pt1, pt2)
im = drawLine(im, calculateMiddlePint(pt1, pt2), circle['center'])

# Draw final image
cv2.imwrite(dir+'/im_8_output.jpg', im)

额外功能:
找到一行图片中的第一个白色像素:

# This function only processes on a horizontal line of the image
# Its job is to examine the pixels one by one from the right and
# report the distance of the first white pixel from the right of
# the image.
def horizantalDistance(im, y):
    y = int(y)
    h, w = im.shape[:2]
    for i in range(0, w):
        x = w-i-1
        if im[y][x] == 255:
            return x
    return -1

在opencv中画一条线:

def drawLine(im, pt1, pt2, color=(128, 0, 200), thickness=2):
    return cv2.line(
        im,
        pt1=(int(pt1[0]), int(pt1[1])),
        pt2=(int(pt2[0]), int(pt2[1])),
        color=color,
        thickness=thickness,
        lineType=cv2.LINE_AA  # Anti-Aliased
    )

计算两个二维点的中点:

def calculateMiddlePint(p1, p2):
    return (int((p1[0]+p2[0])/2), int((p1[1]+p2[1])/2))

输出:
原图:

消除噪声并处理以更好地查看激光阵列:

找到激光提取孔的区域:

处理另一个图像副本以找到金属物体的右侧:

移除阴影以更好地看到右边缘:

最终输出:


我首先定义了一个ROI区域。我后来更改了代码,但没有更改变量的名称。如果你被问到。

您可以尝试二值化 Canny 或直接二值化图像 (Otsu) 之一。在这两种情况下,您都将获得一条准直线,您可以从中提取每行左右最右边的白色像素。

然后对这些点进行线拟合(取决于图像质量,拟合是否稳健)。


根据地面实况校准方法是明智的,因为您正在寻找的边缘与渐变区域接壤,并且边缘的位置可能会偏移几个像素。