【问题标题】: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?目标是找到代表“孔”和外边缘之间距离的线。 Canny 边缘检测是最好的方法吗?
【发布时间】:2021-10-16 19:06:21
【问题描述】:

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

original picture hole - zoomed in

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

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

【问题讨论】:

  • 很难理解你所说的“洞”和“线”。也许参考在图像上绘制的形状和颜色,以便我们知道您在说哪些部分?
  • 不,Canny 通常是错误的工具。新手总是在不应该的时候尝试用 Canny 锤炼所有东西。你应该解释那张照片并退后一步。问如何解决问题的部分(找边,找洞)。为什么说有洞?您刚刚在黑色区域的中间画了一个红色圆圈。
  • 图像中的彩色线条是否存在或已知,或者您想找到它们吗?也许同时显示,可视化和原始图像
  • @ChristophRackwitz:我完全支持你的评论。

标签: python opencv image-processing edge-detection


【解决方案1】:

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

预处理器:

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 区域。我后来更改了代码,但没有更改变量的名称。如果有人问你。

【讨论】:

    【解决方案2】:

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

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


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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2014-03-30
        • 1970-01-01
        • 1970-01-01
        • 2021-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 2017-04-22
        相关资源
        最近更新 更多