使用 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)
这是最终结果:
思路是垂直线和水平线的交点就是交点。一种可能的方法是:
获取二值图像。加载图像,转灰度,Gaussian blur, then Otsu's threshold。
获得水平和垂直线掩码。创建水平和垂直结构元素cv2.getStructuringElement
then perform cv2.morphologyEx
以隔离线。
找关节我们cv2.bitwise_and
把两个mask放在一起得到关节
在关节掩码上找到质心。我们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()
我想检测十字的中心。但是由于两个矩形是连在一起的,所以不知道怎么找。例如,我有这些图片:
交叉 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)
这是最终结果:
思路是垂直线和水平线的交点就是交点。一种可能的方法是:
获取二值图像。加载图像,转灰度,Gaussian blur, then Otsu's threshold。
获得水平和垂直线掩码。创建水平和垂直结构元素
cv2.getStructuringElement
then performcv2.morphologyEx
以隔离线。找关节我们
cv2.bitwise_and
把两个mask放在一起得到关节在关节掩码上找到质心。我们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()