How to write simple geometric shapes into numpy arrays
The usual way is to define a coordinate mesh and apply your shape's equations. To do that the easiest way is to use 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 helperxx, yy = numpy.mgrid[:200, :200]# circles contains the squared distance to the (100, 100) point# we are just using the circle equation learnt at schoolcircle = (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 shapedonut = numpy.logical_and(circle < (6400 + 60), circle > (6400 - 60))
Cairo is a modern, flexible and fast 2D graphics library. It has Python bindings and allows creating "surfaces" based on NumPy arrays:
import numpyimport cairoimport mathdata = 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 whitecr.set_source_rgb(1.0, 1.0, 1.0)cr.paint()# draw red circlecr.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 outputprint data[38:48, 38:48, 0]surface.write_to_png("circle.png")
This code prints
[[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]]
showing some random fragment of the circle. It also creates this PNG:
Another possibility is to use scikit-image
. You can use circle_perimeter for a hollow or circle
for a full circle.
You can draw a single stroke circle like so:
import matplotlib.pyplot as pltfrom skimage import drawarr = np.zeros((200, 200))rr, cc = draw.circle_perimeter(100, 100, radius=80, shape=arr.shape)arr[rr, cc] = 1plt.imshow(arr)plt.show()
You can also emulate a stroke by using a loop
. In this case you should use the anti-aliased version to avoid artifacts:
import matplotlib.pyplot as pltfrom skimage import drawarr = 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] = 1plt.imshow(arr)plt.show()
A probably more efficient way is to generate two full circles and "subtract" the inner from the outer one:
import matplotlib.pyplot as pltfrom skimage import drawarr = np.zeros((200, 200))stroke = 3# Create an outer and inner circle. Then subtract the inner from the outer.radius = 80inner_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] = 1arr[ri, ci] = 0plt.imshow(arr)plt.show()
The two methods yield in fact slightly different results.