1、完整代码
from __future__ import print_function
from __future__ import division
import cv2 as cv
import numpy as np
# Create an image
r = 100
src = np.zeros((4*r, 4*r), dtype=np.uint8)
# 创建六边形的6个顶点
vert = [None]*6
vert[0] = (3*r//2, int(1.34*r))
vert[1] = (1*r, 2*r)
vert[2] = (3*r//2, int(2.866*r))
vert[3] = (5*r//2, int(2.866*r))
vert[4] = (3*r, 2*r)
vert[5] = (5*r//2, int(1.34*r))
# 根据六个顶点画6边形
for i in range(6):
cv.line(src, vert[i], vert[(i+1)%6], ( 255 ), 3)
# 获取六边形的轮廓
contours, _ = cv.findContours(src, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 计算图上点到六边形的距离(带符号)
raw_dist = np.empty(src.shape, dtype=np.float32)
for i in range(src.shape[0]):
for j in range(src.shape[1]):
raw_dist[i,j] = cv.pointPolygonTest(contours[0], (j,i), True)
# 查找带符号的最大最小值
minVal, maxVal, _, maxDistPt = cv.minMaxLoc(raw_dist)
minVal = abs(minVal)
maxVal = abs(maxVal)
# 用图形表示距离
drawing = np.zeros((src.shape[0], src.shape[1], 3), dtype=np.uint8)
for i in range(src.shape[0]):
for j in range(src.shape[1]):
if raw_dist[i,j] < 0:
drawing[i,j,0] = 255 - abs(raw_dist[i,j]) * 255 / minVal
elif raw_dist[i,j] > 0:
drawing[i,j,2] = 255 - raw_dist[i,j] * 255 / maxVal
else:
drawing[i,j,0] = 255
drawing[i,j,1] = 255
drawing[i,j,2] = 255
cv.circle(drawing,maxDistPt, int(maxVal),(255,255,255), 1, cv.LINE_8, 0)
cv.imshow('Source', src)
cv.imshow('Distance and inscribed circle', drawing)
cv.waitKey()
带符号的最大值,为六边形的中心,到六边形轮廓的距离最远
以此点位中心,画了个圆
六边形外的点,蓝色随着距离变大递减
六边形内的点,红色随着距离变大递减
六边形上的点,白色
2、涉及到的库
cv2.pointPolygonTest
用于判断一个点是否位于一个多边形内部、外部还是边上。这个函数在图像处理、计算机视觉和形状分析等领域中非常有用。以下是关于 cv2.pointPolygonTest() 函数的详细解析:
函数定义
cv2.pointPolygonTest(contour, pt, measureDist=False)
- contour:输入的多边形轮廓,通常是一个由点构成的数组(numpy数组),这些点定义了多边形的边界。这些点可以通过如 cv2.findContours 等函数从图像中提取。
- pt:需要检测的点,通常是一个包含 x 和 y 坐标的元组或 numpy.ndarray。
- measureDist:一个布尔值参数,指定是否计算点到多边形边界的最短距离。
1)如果为 True,则函数返回点到边界的带符号距离。当点在多边形内部时,距离为正;当点在多边形外部时,距离为负;当点在多边形边上时,距离为0。距离的绝对值表示点到多边形边界的实际最短距离。
2)如果为 False(默认值),则函数返回一个整数,表示点相对于多边形的位置。返回值为 -1 表示点在多边形外部,0 表示点在多边形边上,+1 表示点在多边形内部。
使用场景
- 形状分析:通过检测点是否在多边形内部,可以分析形状的属性和特征。
- 物体检测:在图像处理中,可以检测物体边界框内的点是否在多边形内部,从而判断物体是否被完全包含在多边形内。
- 图像分割:在图像分割任务中,可以使用该函数来判断像素点是否属于某个特定的区域或对象。
示例代码
import cv2
import numpy as np
# 定义一个多边形的顶点
pts = np.array([[0, 0], [100, 0], [100, 100], [0, 100]], np.int32)
pts = pts.reshape((-1, 1, 2)) # 将点集转换为 (n, 1, 2) 的形状
# 待检测的点
pt_inside = (50, 50) # 多边形内部的一个点
pt_on_edge = (100, 0) # 多边形边上的一个点
pt_outside = (150, 50) # 多边形外部的一个点
# 调用 pointPolygonTest
dist_inside = cv2.pointPolygonTest(pts, pt_inside, measureDist=True)
dist_on_edge = cv2.pointPolygonTest(pts, pt_on_edge, measureDist=True)
dist_outside = cv2.pointPolygonTest(pts, pt_outside, measureDist=True)
position_inside = cv2.pointPolygonTest(pts, pt_inside, measureDist=False)
position_on_edge = cv2.pointPolygonTest(pts, pt_on_edge, measureDist=False)
position_outside = cv2.pointPolygonTest(pts, pt_outside, measureDist=False)
print(f"Inside distance: {dist_inside}")
print(f"On edge distance: {dist_on_edge}")
print(f"Outside distance: {dist_outside}")
print(f"Inside position: {position_inside}")
print(f"On edge position: {position_on_edge}")
print(f"Outside position: {position_outside}")
output
Inside distance: 50.0
On edge distance: -0.0
Outside distance: -50.0
Inside position: 1.0
On edge position: 0.0
Outside position: -1.0
注意事项
确保传入的轮廓(contour)是正确的,即它应该是一个由点构成的数组,这些点定义了多边形的边界。
当 measureDist 为 True 时,返回的距离值是带符号的,正值表示点在多边形内部,负值表示在多边形外部,绝对值表示最短距离。
cv2.pointPolygonTest() 函数不会直接修改输入的图像或轮廓,它只是返回一个表示点位置的数值或距离。
cv2.minMaxLoc
用于寻找一维数值数组(例如,灰度图像的某一行或列)中的全局最小值和最大值,以及它们的位置。这个函数对于图像处理中的阈值设置、特征检测等场景非常有用。
cv2.minMaxLoc(src, mask=None)
src:输入的单通道数值数组。这可以是一个灰度图像,或者图像的某个部分(如一行或一列)。
mask:可选的与 src 同样大小和类型的操作掩码。如果提供了掩码,则函数仅在没有被掩码标记为忽略的元素中查找最小值和最大值。掩码中的非零值表示对应位置的元素需要考虑在内。
返回值
函数返回一个包含四个元素的元组 (minVal, maxVal, minLoc, maxLoc):
- minVal:找到的最小值。
- maxVal:找到的最大值。
- minLoc:最小值的坐标(以元组形式给出,如 (x, y))。注意,对于一维数组(如图像的单个行或列),坐标将是 (col, 0),因为 y 坐标在这种情况下没有意义。
- maxLoc:最大值的坐标(同样以元组形式给出)。
使用示例
import cv2
import numpy as np
# 加载图像(转换为灰度,如果它还不是)
img = cv2.imread('path_to_image.jpg', cv2.IMREAD_GRAYSCALE)
# 查找最小值和最大值以及它们的位置
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(img)
print(f"Minimum value: {minVal}, at location: {minLoc}")
print(f"Maximum value: {maxVal}, at location: {maxLoc}")
如果你只对图像的某一行或列感兴趣,你可以先通过索引该行或列来创建一个一维数组,然后再调用 cv2.minMaxLoc。
注意:对于二维图像(如灰度图像),返回的坐标 (x, y) 是以图像的左上角为原点((0, 0))的坐标系统。但是,对于一维数组(如通过索引得到的行或列),y 坐标总是 0,因为此时我们实际上是在处理一维数据。