Je souhaite générer un tableau numpy de 200 x 200 éléments et y placer un cercle centré sur 100 100 coordonnées, un rayon de 80 et une largeur de trait de 3 pixels. Comment faire cela en python 2.7 sans impliquer d'opérations sur les fichiers? Peut-être en utilisant des bibliothèques de géométrie ou d'imagerie pour permettre la généralisation à d'autres formes.
Cairo est une bibliothèque graphique 2D moderne, flexible et rapide. Il a des liaisons Python et permet de créer des "surfaces" basées sur des tableaux 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")
Ce code imprime
[[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]]
montrant un fragment aléatoire du cercle. Il crée également ce PNG:
La méthode habituelle consiste à définir un maillage de coordonnées et à appliquer les équations de votre forme. Pour ce faire, le moyen le plus simple consiste à utiliser 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))
opencv new python bindings import cv2
crée des tableaux numpy au format d'image par défaut
Ils incluent fonctions de dessin
Une autre possibilité consiste à utiliser scikit-image
. Vous pouvez utiliser circle_perimeter pour un creux ou circle
pour un cercle complet.
Vous pouvez dessiner un cercle simple comme ceci:
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()
Vous pouvez également émuler un trait en utilisant une loop
. Dans ce cas, vous devez utiliser la version anti-aliasing pour éviter les artefacts:
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()
Un moyen probablement plus efficace consiste à générer deux cercles complets et à "soustraire" les cercles intérieur et extérieur:
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()
Les deux méthodes donnent en fait des résultats légèrement différents.
Une façon de faire cela en utilisant seulement numpy
(semblable à la réponse de @ Simon) est la suivante:
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