【问题标题】:OpenCV Detect Very Small LinesOpenCV 检测非常小的线条
【发布时间】:2018-10-21 09:17:10
【问题描述】:

这是我正在使用的示例图片:

每张图片上都有一条测量条。测量条的比例和角度可能会有所不同。我已经确定了与测量条的一些交叉点,现在需要确定它对应的数字(例如 256、192、128 ...)。所以我需要识别像素范围并将它们中的每一个映射到一个数字。要识别这些范围,似乎唯一的方法是检测每个数字旁边的小线并将它们连接成一条大线。

我的计划是隔离这些小测量线,然后使用 HoughTransform 连接它们之间的线,但是我发现很难隔离这些小线。我尝试过 Canny 边缘检测,但小测量线总是被检测为垂直边缘的一部分。我尝试了许多不同的阈值并进行了升级,但均未成功。

img = cv2.imread('example.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
resized = cv2.resize(gray,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)
blur_gray = cv2.GaussianBlur(resized,(5, 5),0)
edges = cv2.Canny(blur_gray, 100, 200)

升级 x2升级 x10

这甚至是正确的方法还是我可以使用其他方法来提取这些测量线?

【问题讨论】:

  • 我认为如果你找到两条长长的垂直线,然后沿着它们走,找到小的水平标记会更容易。
  • 也试试线段检测器
  • 忽略垂直线超过 N 个像素的像素列。删除垂直线。然后在水平维度上组合最近的两个非黑色像素。
  • 感谢您的建议。我会尝试检测长的垂直线,然后从那里开始工作。当我得到一些工作时,我会发布一个答案。
  • 如果图像中使用的测量条始终是相同的(具有相同的形状和相同的标记)并且总是刚性变形,则可能更容易找到测量条的边界边缘每张图像,获取沿条带的交叉点的投影 y 坐标(例如向上的 10%),并使用一些查找表将这些百分比映射到条带上的实际数字。

标签: python opencv image-processing edge-detection canny-operator


【解决方案1】:

我会采取以下方法:

  1. 在 Canny 边缘检测后使用 cv2.HoughLinesP() 检测长直线
  2. 删除错误检测到的线条,只保留与条带长边相对应的 2 条。
  3. 确保两条线都沿着条带上的黑线很好地定位。
  4. 通过对两条线之间的图像进行采样来抓取感兴趣区域。
  5. 沿条带宽度分析平均强度。使用 find_peaksdistance 参数来检测标记/文本之间的白色区域。

以下代码适用于您的示例。

import cv2
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import find_peaks
from skimage.draw import line

NOF_MARKERS = 30

# Show input image
img = cv2.imread("mPIXY.jpg")
img_orig = img.copy()
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
fig, axs = plt.subplots(2, 2)

# Detect long lines in the image
img_edg = cv2.Canny(img, 50, 120)
img_edg = cv2.morphologyEx(img_edg, cv2.MORPH_CLOSE, (5, 5), iterations=1)
img_edg = cv2.cvtColor(img_edg, cv2.COLOR_GRAY2RGB)
axs[0, 0].set_title("Canny edges + morphology closed")
axs[0, 0].imshow(img_edg)
lines = cv2.HoughLinesP(
    img_edg[:, :, 0].copy(),
    rho=1,
    theta=np.pi / 360,
    threshold=70,
    minLineLength=300,
    maxLineGap=15,
)
lines = lines.squeeze()
for x1, y1, x2, y2 in lines:
    cv2.line(img_edg, (x1, y1), (x2, y2), (255, 0, 0))
axs[0, 0].imshow(img_edg, aspect="auto")


def optimize_line_alignment(img_gray, line_end_points):
    # Shift endpoints to find optimal alignment with black line in the  origial image
    opt_line_mean = 255
    x1, y1, x2, y2 = line_end_points
    for dx1 in range(-3, 4):
        for dy1 in range(-3, 4):
            for dx2 in range(-3, 4):
                for dy2 in range(-3, 4):
                    line_discrete = np.asarray(
                        list(zip(*line(*(x1 + dx1, y1 + dy1), *(x2 + dx2, y2 + dy2))))
                    )
                    line_pixel_values = img_gray[
                        line_discrete[:, 1], line_discrete[:, 0]
                    ]
                    line_mean = np.mean(line_pixel_values)
                    if line_mean < opt_line_mean:
                        opt_line_end_points = np.array(
                            [x1 + dx1, y1 + dy1, x2 + dx2, y2 + dy2]
                        )
                        opt_line_discrete = line_discrete
                        opt_line_mean = line_mean
    return opt_line_end_points, opt_line_discrete


# Optimize alignment for the 2 outermost lines
dx = np.mean(abs(lines[:, 2] - lines[:, 0]))
dy = np.mean(abs(lines[:, 3] - lines[:, 1]))
if dy > dx:
    lines = lines[np.argsort(lines[:, 0]), :]
else:
    lines = lines[np.argsort(lines[:, 1]), :]
line1, line1_discrete = optimize_line_alignment(img_gray, lines[0, :])
line2, line2_discrete = optimize_line_alignment(img_gray, lines[-1, :])
cv2.line(img, (line1[0], line1[1]), (line1[2], line1[3]), (255, 0, 0))
cv2.line(img, (line2[0], line2[1]), (line2[2], line2[3]), (255, 0, 0))
axs[0, 1].set_title("Edges of the strip")
axs[0, 1].imshow(img, aspect="auto")

# Take region of interest from image
dx = round(0.5 * (line2[0] - line1[0]) + 0.5 * (line2[2] - line1[2]))
dy = round(0.5 * (line2[1] - line1[1]) + 0.5 * (line2[3] - line1[3]))
strip_width = len(list(zip(*line(*(0, 0), *(dx, dy)))))
img_roi = np.zeros((strip_width, line1_discrete.shape[0]), dtype=np.uint8)
for idx, (x, y) in enumerate(line1_discrete):
    perpendicular_line_discrete = np.asarray(
        list(zip(*line(*(x, y), *(x + dx, y + dy))))
    )
    img_roi[:, idx] = img_gray[
        perpendicular_line_discrete[:, 1], perpendicular_line_discrete[:, 0]
    ]

axs[1, 0].set_title("Strip analysis")
axs[1, 0].imshow(img_roi, cmap="gray")
extra_ax = axs[1, 0].twinx()
roi_mean = np.mean(img_roi, axis=0)
extra_ax.plot(roi_mean, label="mean")
extra_ax.plot(np.min(roi_mean, axis=0), label="min")
plt.legend()

# Locate the markers within region of interest
black_bar = np.argmin(roi_mean)
length = np.max([img_roi.shape[1] - black_bar, black_bar])
if black_bar < img_roi.shape[1] / 2:
    roi_mean = np.append(roi_mean, 0)
    peaks, _ = find_peaks(roi_mean[black_bar:], distance=length / NOF_MARKERS * 0.75)
    peaks = peaks + black_bar
else:
    roi_mean = np.insert(roi_mean, 0, 0)
    peaks, _ = find_peaks(roi_mean[:black_bar], distance=length / NOF_MARKERS * 0.75)
    peaks = peaks - 1
extra_ax.vlines(
    peaks,
    extra_ax.get_ylim()[0],
    extra_ax.get_ylim()[1],
    colors="green",
    linestyles="dotted",
)
axs[1, 1].set_title("Midpoints between markings")
axs[1, 1].imshow(img_orig, aspect="auto")
axs[1, 1].plot(line1_discrete[peaks, 0], line1_discrete[peaks, 1], "r+")
fig.show()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 1970-01-01
    相关资源
    最近更新 更多