python - 如何选择由 numpy 数组表示的图像中轮廓内的像素?

标签 python numpy opencv image-processing contour

VI 有一组绘制在图像上的轮廓点,这些轮廓点存储为 2D numpy 数组。轮廓由 2 个浮点值的 numpy 数组表示,每个 x 和 y 坐标。 These coordinates不是整数,也不会与像素完美对齐,但它们确实会告诉您轮廓点相对于像素的位置。

我希望能够选择落在轮廓内的像素。我写了一些与此处给出的答案几乎相同的代码:Access pixel values within a contour boundary using OpenCV in Python

temp_list = []
for a, b in zip(x_pixel_nos, y_pixel_nos):
    temp_list.append([[a, b]]) # 2D array of shape 1x2
temp_array = np.array(temp_list)
contour_array_list = []
contour_array_list.append(temp_array)



lst_intensities = []

# For each list of contour points...
for i in range(len(contour_array_list)):
    # Create a mask image that contains the contour filled in
    cimg = np.zeros_like(pixel_array)
    cv2.drawContours(cimg, contour_array_list, i, color=255, thickness=-1)

# Access the image pixels and create a 1D numpy array then add to list
pts = np.where(cimg == 255)
lst_intensities.append(pixel_array[pts[0], pts[1]])

当我运行它时,出现错误 error: OpenCV(3.4.1)/opt/conda/conda-bld/opencv-suite_1527005509093/work/modules/imgproc/src/drawing.cpp:2515:错误:函数 drawContours 中的 (-215) npoints > 0

我猜测此时 openCV 对我不起作用,因为我的轮廓是 float ,而不是整数,openCV 无法使用 drawContours 处理它。如果我将等高线的坐标转换为整数,我会失去很多精度。

那么我怎样才能得到落在轮廓内的像素呢?

enter image description here

这应该是一项微不足道的任务,但到目前为止我还没有找到一种简单的方法来完成它。

最佳答案

我认为找到落在轮廓内的所有像素的最简单方法如下。

轮廓由一组非整数点描述。我们可以把这些点看成是一个多边形的顶点,轮廓就是一个多边形。

我们首先找到多边形的边界框。此边界框外的任何像素都不在多边形内,不需要考虑。

对于边界框内的像素,我们使用经典测试来测试它们是否在多边形内部:从无限远的某个点到该点追踪一条线,并计算交叉的多边形边(线段)的数量。如果此数字为奇数,则该点位于多边形内。结果是 Matplotlib包含 a very efficient implementation of this algorithm .

我还在习惯 Python 和 Numpy,如果您是 Python 专家,这可能是一个有点笨拙的代码。但我认为它的作用是直截了当的。首先,它计算多边形的边界框,然后创建一个数组 points,其中包含落在该边界框内的所有像素的坐标(我假设像素质心才是最重要的)。它将 matplotlib.path.contains_points 方法应用于该数组,生成一个 bool 数组 mask。最后,它会 reshape 此数组以匹配边界框。

import math
import matplotlib.path
import numpy as np

x_pixel_nos = [...]
y_pixel_nos = [...] # Data from https://gist.github.com/sdoken/173fae1f9d8673ffff5b481b3872a69d

temp_list = []
for a, b in zip(x_pixel_nos, y_pixel_nos):
   temp_list.append([a, b])

polygon = np.array(temp_list)
left = np.min(polygon, axis=0)
right = np.max(polygon, axis=0)
x = np.arange(math.ceil(left[0]), math.floor(right[0])+1)
y = np.arange(math.ceil(left[1]), math.floor(right[1])+1)
xv, yv = np.meshgrid(x, y, indexing='xy')
points = np.hstack((xv.reshape((-1,1)), yv.reshape((-1,1))))

path = matplotlib.path.Path(polygon)
mask = path.contains_points(points)
mask.shape = xv.shape

在这段代码之后,需要做的是定位图像中的边界框,并为像素着色。 left 包含图像中对应于 mask 左上角像素的像素。


可以提高该算法的性能。如果为测试像素而追踪的光线是水平的,您可以想象沿水平线的所有像素都可以从对左侧像素所做的工作中受益。也就是说,与计算单个像素的成本相比,计算图像线上所有像素的输入/输出状态是可能的。

matplotlib.path.contains_points 算法比对所有点执行单点测试更有效,因为适本地对多边形边和顶点进行排序使每个测试成本更低,而且排序仅一次测试多个点时需要一次完成。但是这个算法没有考虑到我们要在同一条线上测试很多点。


这些是我做的时候看到的

pp.plot(x_pixel_nos, y_pixel_nos)
pp.imshow(mask)

在使用您的数据运行上面的代码之后。请注意,y 轴是用 imshow 反转的,因此是垂直镜像的形状。

enter image description here

enter image description here

关于python - 如何选择由 numpy 数组表示的图像中轮廓内的像素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50847827/

相关文章:

python - Python 转换为数组时出现内存错误

python - 如何设置不在矩阵子矩阵中的数字

python - 当我尝试在一个图上同时使用一个(线)图和一个条形图制作一个图时,我得到一个奇怪的图

android - Android Studio 的 OpenCV 库错误

python - 何时关闭/打开 MySQL 连接

python - 由于环境错误,pip3 安装失败

python - Cocotron 与 pyobjc?

python - sympy 矩阵真的那么慢吗?

python - OpenCV的findContours不正常

python - imwrite 16 位 png 深度图像