2012-04-05 11 views
22

Vorrei creare una serie numerica di elementi 200x200 in dimensioni e inserire un cerchio centrato in 100.100 coordinate, raggio 80 e larghezza del tratto di 3 pixel. Come fare questo in python 2.7 senza coinvolgere le operazioni sui file? Possibilmente utilizzando la geometria o le librerie di immagini per consentire la generalizzazione ad altre forme.Come scrivere forme geometriche semplici in array numpy

risposta

17

Cairo è un moderno, flessibile e veloce libreria grafica 2D. Ha Python bindings e permette la creazione di "superfici" basati su array 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") 

stampe Questo codice

[[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]] 

mostrano qualche frammento casuale del cerchio. Inoltre, crea questo PNG:

Red circle

+0

Dato che sto lavorando con i dati in scala di grigi (8 bit) I sto usando: \ data = numpy.zeros ((200, 200), dtype = numpy.uint8) \ surface = cairo.ImageSurface.create_for_data (data, cairo.FORMAT_A8, 200, 200) \ #to dipingere una superficie grigia al 50% (viene utilizzato il valore alfa) \ cr.set_source_rgba (0, 0, 0, 0.5) – a1an

+0

Se qualcuno tenta di utilizzare questo esempio con la libreria cairocffi, non funzionerà. Ecco un problema sul repository cairocffi che va più nel dettaglio: https: // github.com/Kozea/cairocffi/issues/51 – neelshiv

6

OpenCV nuovi binding python import cv2 creare array numpy come il formato immagine predefinito

Essi comprendono drawing functions

+0

Che formato sarebbero gli array di uscita avere? Solo un elenco di coordinate o una griglia binaria? –

+0

È un'immagine = cioè tipicamente un array 2p numpy, puoi fare tutte le normali operazioni di numpy su di loro e salvare in qualsiasi formato immagine –

+0

Grande, grazie per l'aggiornamento @Martin –

31

Il solito modo è quello di definire una rete coordinata e applicare le equazioni di Your Shape. Per fare questo il modo più semplice è quello di utilizzare 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)) 
+0

Non ho mai saputo che Numpy potesse farlo! –

+2

@MartinBeckett mgrid consente di valutare le funzioni su un intervallo di valori. Nota che non sei limitato a 2 dimensioni. – Simon

+2

In una nota a margine, trovo 'donut = (circle <(6400 + 60)) & (circle> (6400 - 60))' un po 'più leggibile rispetto a chiamare esplicitamente 'logical_and'. È una questione di preferenze personali, però. Sono esattamente equivalenti. (Si noti che '&' chiamerà 'numpy.logical_and', mentre' and' non può essere sovrascritto, però.) –

2

Un'altra possibilità è quella di utilizzare scikit-image. È possibile utilizzare circle_perimeter per un cavo o circle per un cerchio completo.

è possibile disegnare un unico cerchio corsa in questo modo:

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() 

È inoltre possibile emulare un ictus utilizzando un loop. In questo caso si consiglia di utilizzare la versione anti-aliasing per evitare artefatti:

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 modo probabilmente più efficace è quello di generare due cerchi completi e "sottrarre" l'interno da quello esterno:

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() 

I due metodi danno infatti risultati leggermente diversi.

Circle with <code>stroke=3</code>