使用 OpenCV 查找多边形的交点

Find the intersection point of a polygon with OpenCV

我想检测十字的中心。但是由于两个矩形是连在一起的,所以不知道怎么找。例如,我有这些图片:

交叉 1

交叉 2

我想找到“红点”。

这是一个可能的解决方案。它基于我在这里的回答:How can i get the inner contour points without redundancy in OpenCV - Python。主要思想是将图像与识别交叉点的特殊内核进行卷积。完成此操作后,您将创建一个包含可能交点的蒙版,应用一些形态学并获取坐标。

您没有提供输入图像,我正在使用您 post 编辑的 "cross" image 测试此算法。这是代码:

# Imports:
import cv2
import numpy as np
# Image path

path = "D://opencvImages//"
fileName = "cross.png" # Your "cross" image

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Prepare a deep copy of the input for results:
inputImageCopy = inputImage.copy()

# Grayscale conversion:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

现在,卷积必须接收形状已缩小到 1 pixel 宽度的图像。这可以通过计算图像的骨架来完成。 skeleton 是二进制图像的一个版本,其中线条已被标准化为具有 1 pixel 的宽度。然后我们可以用 3 x 3 内核对图像进行卷积并寻找特定的像素模式。

在计算骨架之前,我们将在图像周围添加边框。如果形状一直延伸到图像的边界,这可以防止骨架产生一些伪影:

# Add borders to prevent skeleton artifacts:
borderThickness = 1
borderColor = (0, 0, 0)
grayscaleImage = cv2.copyMakeBorder(grayscaleImage, borderThickness, borderThickness, borderThickness, borderThickness,
                                    cv2.BORDER_CONSTANT, None, borderColor)
# Compute the skeleton:
skeleton = cv2.ximgproc.thinning(grayscaleImage, None, 1)

这是骨架,没有人工制品:

现在,让我们找到交叉点。该方法基于 Mark Setchell's info on this post。 post 主要展示了查找形状 end-points 的方法,但我将其扩展为也可以识别线的交叉点。主要思想是卷积产生一个非常具体的值,其中在输入图像中发现黑白像素的图案。请参阅 post 了解此想法背后的理论,但在这里,我们正在寻找 130:

的值
# Threshold the image so that white pixels get a value of 10 and
# black pixels a value of 0:
_, binaryImage = cv2.threshold(skeleton, 128, 10, cv2.THRESH_BINARY)

# Set the intersections kernel:
h = np.array([[1, 1, 1],
              [1, 10, 1],
              [1, 1, 1]])

# Convolve the image with the kernel:
imgFiltered = cv2.filter2D(binaryImage, -1, h)

# Prepare the final mask of points:
(height, width) = binaryImage.shape
pointsMask = np.zeros((height, width, 1), np.uint8)

# Perform convolution and create points mask:
thresh = 130
# Locate the threshold in the filtered image:
pointsMask = np.where(imgFiltered == thresh, 255, 0)

# Convert and shape the image to a uint8 height x width x channels
# numpy array:
pointsMask = pointsMask.astype(np.uint8)
pointsMask = pointsMask.reshape(height, width, 1)

这是 pointsMask 图片:

我应用了一些形态学,我们可以将单个像素连接成斑点。在这里,扩张会做:

# Set kernel (structuring element) size:
kernelSize = 7
# Set operation iterations:
opIterations = 3
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))

# Perform Dilate:
pointsMask = cv2.morphologyEx(pointsMask, cv2.MORPH_DILATE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101)

这是应用膨胀的结果:

现在,我们可以找到白色像素的坐标并计算它们的平均值(或质心):

# Get the coordinates of the end-points:
(Y, X) = np.where(pointsMask == 255)

# Get the centroid:
y = int(np.mean(Y))
x = int(np.mean(X))

让我们使用这些坐标在原始图像上画一个圆:

# Draw the intersection point:
# Set circle color:
color = (0, 0, 255)

# Draw Circle
cv2.circle(inputImageCopy, (x, y), 3, color, -1)

# Show Image
cv2.imshow("Intersections", inputImageCopy)
cv2.waitKey(0)

这是最终结果:

思路是垂直线和水平线的交点就是交点。一种可能的方法是:

  1. 获取二值图像。加载图像,转灰度,Gaussian blur, then Otsu's threshold

  2. 获得水平和垂直线掩码。创建水平和垂直结构元素cv2.getStructuringElement then perform cv2.morphologyEx以隔离线。

  3. 找关节我们cv2.bitwise_and把两个mask放在一起得到关节

  4. 在关节掩码上找到质心。我们find contours then calculate the centroid得到交点。


输入图像->水平蒙版->垂直蒙版->关节

检测到交叉路口为绿色

另一张图片的结果

输入图像->水平蒙版->垂直蒙版->关节

检测到交叉路口为绿色

代码

import cv2
import numpy as np

# Load image, grayscale, Gaussian blur, Otsus threshold
image = cv2.imread('4.PNG')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find horizonal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (150,5))
horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)

# Find vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,150))
vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)

# Find joints
joints = cv2.bitwise_and(horizontal, vertical)

# Find centroid of the joints
cnts = cv2.findContours(joints, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Find centroid and draw center point
    M = cv2.moments(c)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])
    cv2.circle(image, (cx, cy), 15, (36,255,12), -1)

cv2.imshow('horizontal', horizontal)
cv2.imshow('vertical', vertical)
cv2.imshow('joints', joints)
cv2.imshow('image', image)
cv2.waitKey()