如何将简单的几何形状写入 numpy 数组

Posted

技术标签:

【中文标题】如何将简单的几何形状写入 numpy 数组【英文标题】:How to write simple geometric shapes into numpy arrays 【发布时间】:2012-04-19 08:51:06 【问题描述】:

我想生成一个大小为 200x200 元素的 numpy 数组,并在其中放入一个以 100,100 坐标为中心、半径为 80 且笔划宽度为 3 像素的圆。如何在不涉及文件操作的情况下在 python 2.7 中执行此操作?可能使用几何或成像库来泛化到其他形状。

【问题讨论】:

【参考方案1】:

opencv new python bindings import cv2 创建 numpy 数组作为默认图像格式

它们包括drawing functions

【讨论】:

输出数组有什么格式?只是坐标列表还是二进制网格? 这是一个图像 = 即通常是一个 2d numpy 数组,您可以对它们进行所有正常的 numpy 操作并以任何图像格式保存【参考方案2】:

Cairo 是一个现代、灵活、快速的 2D 图形库。它有 Python bindings 并允许基于 NumPy 数组创建“表面”:

import numpy
import cairo
import math
data = numpy.zeros((200, 200, 4), dtype=numpy.uint8)
surface = cairo.ImageSurface.create_for_data(
    data, cairo.FORMAT_ARGB32, 200, 200)
cr = cairo.Context(surface)

# fill with solid white
cr.set_source_rgb(1.0, 1.0, 1.0)
cr.paint()

# draw red circle
cr.arc(100, 100, 80, 0, 2*math.pi)
cr.set_line_width(3)
cr.set_source_rgb(1.0, 0.0, 0.0)
cr.stroke()

# write output
print data[38:48, 38:48, 0]
surface.write_to_png("circle.png")

此代码打印

[[255 255 255 255 255 255 255 255 132   1]
 [255 255 255 255 255 255 252 101   0   0]
 [255 255 255 255 255 251  89   0   0   0]
 [255 255 255 255 249  80   0   0   0  97]
 [255 255 255 246  70   0   0   0 116 254]
 [255 255 249  75   0   0   0 126 255 255]
 [255 252  85   0   0   0 128 255 255 255]
 [255 103   0   0   0 118 255 255 255 255]
 [135   0   0   0 111 255 255 255 255 255]
 [  1   0   0  97 254 255 255 255 255 255]]

显示圆圈的一些随机片段。它还创建了这个 PNG:

【讨论】:

由于我正在使用灰度(8 位)数据,因此我使用以下内容:\ data = numpy.zeros((200, 200), dtype=numpy.uint8) \surface = cairo. ImageSurface.create_for_data(data,cairo.FORMAT_A8, 200, 200) \ #绘制一个50%灰度的表面(使用alpha值) \ cr.set_source_rgba(0, 0, 0, 0.5) 如果有人试图将此示例与 cairocffi 库一起使用,它将无法正常工作。这是关于 cairocffi 回购的一个更详细的问题:github.com/Kozea/cairocffi/issues/51【参考方案3】:

通常的方法是定义一个坐标网格并应用你的形状方程。要做到这一点,最简单的方法是使用numpy.mgrid

http://docs.scipy.org/doc/numpy/reference/generated/numpy.mgrid.html

# xx and yy are 200x200 tables containing the x and y coordinates as values
# mgrid is a mesh creation helper
xx, yy = numpy.mgrid[:200, :200]
# circles contains the squared distance to the (100, 100) point
# we are just using the circle equation learnt at school
circle = (xx - 100) ** 2 + (yy - 100) ** 2
# donuts contains 1's and 0's organized in a donut shape
# you apply 2 thresholds on circle to define the shape
donut = numpy.logical_and(circle < (6400 + 60), circle > (6400 - 60))

【讨论】:

我从来不知道 numpy 可以做到这一点! @MartinBeckett mgrid 允许您在一系列值上评估函数。请注意,您不仅限于二维。 在旁注中,我发现donut = (circle &lt; (6400 + 60)) &amp; (circle &gt; (6400 - 60)) 比显式调用logical_and 更具可读性。不过,这是个人喜好问题。它们完全等价。 (请注意,&amp; 将调用 numpy.logical_and,而 and 不能被覆盖。) 这很好也很有用,因为它允许“绘制”任意函数,但是对于众所周知的几何形状,它也需要显式方程。为此,我认为图书馆会更受欢迎【参考方案4】:

另一种可能性是使用scikit-image。您可以使用circle_perimeter 表示空心或使用circle 表示完整圆。

你可以像这样画一个单笔画圆:

import matplotlib.pyplot as plt
from skimage import draw
arr = np.zeros((200, 200))
rr, cc = draw.circle_perimeter(100, 100, radius=80, shape=arr.shape)
arr[rr, cc] = 1
plt.imshow(arr)
plt.show()

您还可以使用loop 模拟笔划。在这种情况下,您应该使用抗锯齿版本来避免伪影:

import matplotlib.pyplot as plt
from skimage import draw
arr = np.zeros((200, 200))
stroke = 3
# Create stroke-many circles centered at radius. 
for delta in range(-(stroke // 2) + (stroke % 2), (stroke + 1) // 2):
    rr, cc, _ = draw.circle_perimeter_aa(100, 100, radius=80+delta, shape=arr.shape)
    arr[rr, cc] = 1
plt.imshow(arr)
plt.show()

一种可能更有效的方法是生成两个完整的圆并从外部“减去”内部:

import matplotlib.pyplot as plt
from skimage import draw
arr = np.zeros((200, 200))
stroke = 3
# Create an outer and inner circle. Then subtract the inner from the outer.
radius = 80
inner_radius = radius - (stroke // 2) + (stroke % 2) - 1 
outer_radius = radius + ((stroke + 1) // 2)
ri, ci = draw.circle(100, 100, radius=inner_radius, shape=arr.shape)
ro, co = draw.circle(100, 100, radius=outer_radius, shape=arr.shape)
arr[ro, co] = 1
arr[ri, ci] = 0
plt.imshow(arr)
plt.show()

这两种方法实际上产生的结果略有不同。

【讨论】:

【参考方案5】:

仅使用numpy 的一种方法(类似于@Simon 的回答)如下:

import numpy as np

def draw_circle(radius, dim=None):
    if dim == None:
        dim = (radius * 2, radius * 2)
    circle = np.zeros(dim)
    x, y = np.meshgrid(np.arange(dim[0]), np.arange(dim[1]))
    r = np.abs((x - dim[0] / 2)**2 + (y - dim[1] / 2)**2 - radius**2)

    m1 = r.min(axis=1, keepdims=True)
    m2 = r.min(axis=0, keepdims=True)
    rr = np.logical_or(r == m1, r == m2)
    l_x_lim = int(dim[0] / 2 - radius)
    u_x_lim = int(dim[0] / 2 + radius + 1)
    l_y_lim = int(dim[0] / 2 - radius)
    u_y_lim = int(dim[0] / 2 + radius + 1)

    circle[l_x_lim:u_x_lim, l_y_lim:u_y_lim][rr[l_x_lim:u_x_lim, l_y_lim:u_y_lim]] = 1
    return circle

gen_circle(20) # draw a circle of radius 20 pixels

【讨论】:

以上是关于如何将简单的几何形状写入 numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV中几何形状识别与测量

OpenCV中几何形状识别与测量

识别脊的几何形状

任何java包来绘制简单的几何形状?

2d几何推送算法

有一个整数矩阵 MxN 如何将它们分组为具有增强几何形状的多边形?