Estoy usando la biblioteca de imágenes de Python y me gustaría dibujar algunas curvas de bezier. Supongo que podría calcular píxel por píxel, pero espero que haya algo más simple.¿Cómo puedo dibujar una curva bezier usando el PIL de Python?
Respuesta
Una curva bezier no es tan difícil de dibujar. Dado tres puntos A
, B
, C
necesita tres interpolaciones lineales para dibujar la curva. Utilizamos el escalar t
como parámetro para la interpolación lineal:
P0 = A * t + (1 - t) * B
P1 = B * t + (1 - t) * C
Este interpola entre dos bordes que hemos creado, borde AB y AC borde. Lo único que ahora tenemos que hacer para calcular el punto de que tenemos que dibujar es interpolar entre P0 y P1 utilizando el mismo t modo:
Pfinal = P0 * t + (1 - t) * P1
Hay un par de cosas que hay que hacer antes de que realmente dibuja la curva. Primero vamos a caminar algunos dt
(delta t) y tenemos que tener en cuenta que 0 <= t <= 1
. Como podrán imaginar, esto no nos proporcionará una curva suave, sino que produce solo un conjunto discreto de posiciones para trazar. La forma más fácil de resolver esto es simplemente dibujar una línea entre el punto actual y el punto anterior.
Puede usar el aggdraw en la parte superior de PIL, las curvas de bezier son supported.
EDIT:
hice un ejemplo sólo para descubrir que hay un error en la clase Path
respecto curveto
:(
Aquí está el ejemplo de todos modos:
from PIL import Image
import aggdraw
img = Image.new("RGB", (200, 200), "white")
canvas = aggdraw.Draw(img)
pen = aggdraw.Pen("black")
path = aggdraw.Path()
path.moveto(0, 0)
path.curveto(0, 60, 40, 100, 100, 100)
canvas.path(path.coords(), path, pen)
canvas.flush()
img.save("curve.png", "PNG")
img.show()
This debe solucione el error si está listo para recompilar el módulo ...
+1 para el enlace sobre cómo solucionar el error de Aggdraw bezier, lástima que los enlaces de Python no se hayan actualizado para solucionarlo. –
def make_bezier(xys):
# xys should be a sequence of 2-tuples (Bezier control points)
n = len(xys)
combinations = pascal_row(n-1)
def bezier(ts):
# This uses the generalized formula for bezier curves
# http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization
result = []
for t in ts:
tpowers = (t**i for i in range(n))
upowers = reversed([(1-t)**i for i in range(n)])
coefs = [c*a*b for c, a, b in zip(combinations, tpowers, upowers)]
result.append(
tuple(sum([coef*p for coef, p in zip(coefs, ps)]) for ps in zip(*xys)))
return result
return bezier
def pascal_row(n):
# This returns the nth row of Pascal's Triangle
result = [1]
x, numerator = 1, n
for denominator in range(1, n//2+1):
# print(numerator,denominator,x)
x *= numerator
x /= denominator
result.append(x)
numerator -= 1
if n&1 == 0:
# n is even
result.extend(reversed(result[:-1]))
else:
result.extend(reversed(result))
return result
Esto, por ejemplo, dibuja un corazón:
from PILL import Image
from PIL import ImageDraw
if __name__ == '__main__':
im = Image.new('RGBA', (100, 100), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
ts = [t/100.0 for t in range(101)]
xys = [(50, 100), (80, 80), (100, 50)]
bezier = make_bezier(xys)
points = bezier(ts)
xys = [(100, 50), (100, 0), (50, 0), (50, 35)]
bezier = make_bezier(xys)
points.extend(bezier(ts))
xys = [(50, 35), (50, 0), (0, 0), (0, 50)]
bezier = make_bezier(xys)
points.extend(bezier(ts))
xys = [(0, 50), (20, 80), (50, 100)]
bezier = make_bezier(xys)
points.extend(bezier(ts))
draw.polygon(points, fill = 'red')
im.save('out.png')
Aunque trazados Curva curveTo no funcionan con Aggdraw, como se ha mencionado por @ ToniRuža, hay otra manera de hacer esto en Aggdraw. El beneficio de usar Aggdraw en lugar de PIL o sus propias funciones de bezier es que Aggdraw antialias la imagen haciendo que se vea más suave (ver foto en la parte inferior).
Símbolos Aggdraw
En lugar de utilizar la clase aggdraw.Path() para dibujar, se puede utilizar la clase aggdraw.Symbol(pathstring)
que es básicamente el mismo, excepto que escribe la ruta como una cadena. De acuerdo con los documentos de Aggdraw, la forma de escribir su ruta como una cadena es usar la sintaxis de la ruta SVG (consulte: http://www.w3.org/TR/SVG/paths.html).Básicamente, cada adición (nodo) a la trayectoria normalmente comienza con
- una letra que representa la acción de estirado (mayúsculas para ruta absoluta, minúsculas para la ruta relativa), seguido de (sin espacios en el medio)
- la x coordenadas (preceder por un signo menos si se trata de un número negativo o dirección)
- una coma
- la coordenada y (preceder por un signo menos si se trata de un número negativo o dirección)
En su camino string solo separa tus múltiples nodos con un espacio. Una vez que haya creado su símbolo, simplemente recuerde dibujarlo pasándolo como uno de los argumentos al draw.symbol(args)
.
curvas de Bézier en Símbolos Aggdraw
Específicamente para las curvas de Bezier cúbicos que escribe la letra "C" o "c" seguido de 6 números (3 series de coordenadas XY x1, y1, x2, y2, x3 , y3 con comas entre los números pero no entre el primer número y la letra). Según los documentos, también hay otras versiones de bezier con la letra "S" (bezier cúbico liso), Q (bezier cuadrático), T (bezier cuadrático liso) ". Aquí es un ejemplo de código completa (requiere PIL y aggdraw):
print "initializing script"
# imports
from PIL import Image
import aggdraw
# setup
img = Image.new("RGBA", (1000,1000)) # last part is image dimensions
draw = aggdraw.Draw(img)
outline = aggdraw.Pen("black", 5) # 5 is the outlinewidth in pixels
fill = aggdraw.Brush("yellow")
# the pathstring:
#m for starting point
#c for bezier curves
#z for closing up the path, optional
#(all lowercase letters for relative path)
pathstring = " m0,0 c300,300,700,600,300,900 z"
# create symbol
symbol = aggdraw.Symbol(pathstring)
# draw and save it
xy = (20,20) # xy position to place symbol
draw.symbol(xy, symbol, outline, fill)
draw.flush()
img.save("testbeziercurves.png") # this image gets saved to same folder as the script
print "finished drawing and saved!"
Y la salida es una figura Bézier curva de aspecto suave:
he encontrado una manera más sencilla de crear una curva de Bezier (sin aggraw y sin funciones complejas).
import math
from PIL import Image
from PIL import ImageDraw
image = Image.new('RGB',(1190,841),'white')
draw = ImageDraw.Draw(image)
curve_smoothness = 100
#First, select start and end of curve (pixels)
curve_start = [(167,688)]
curve_end = [(678,128)]
#Second, split the path into segments
curve = []
for i in range(1,curve_smoothness,1):
split = (curve_end[0][0] - curve_start[0][0])/curve_smoothness
x = curve_start[0][0] + split * i
curve.append((x, -7 * math.pow(10,-7) * math.pow(x,3) - 0.0011 * math.pow(x,2) + 0.235 * x + 682.68))
#Third, edit any other corners of polygon
other =[(1026,721), (167,688)]
#Finally, combine all parts of polygon into one list
xys = curve_start + curve + curve_end + other #putting all parts of the polygon together
draw.polygon(xys, fill = None, outline = 256)
image.show()
- 1. Usando una Curva de Bezier para dibujar una espiral
- 2. Cómo dibujar una curva de Bezier programáticamente en WPF?
- 3. Curva y lienzo de Bezier
- 4. Objetivo C Bezier Curva Reesformar
- 5. ¿Cómo compensar una curva de bezier cúbica?
- 6. Dibujar texto multilingüe usando PIL
- 7. Arrastre una curva bezier para editarlo
- 8. convertir curva de bezier a cadena poligonal?
- 9. Curva de Bezier cuadrática: Calcular la tangente
- 10. Recreación de transiciones CSS3 Curva cúbica-Bezier
- 11. Ángulo de un punto dado en una curva de Bezier?
- 12. ¿Cómo dibujar NSString en una trayectoria curva?
- 13. ¿Mover un objeto en la dirección de una curva bezier?
- 14. Dibujar un sobre alrededor de una curva
- 15. Posición de un punto relativo a una curva de Bezier
- 16. ¿Cómo calcular el punto más cercano de una línea y curva? .. o curva y curva?
- 17. ¿Has encontrado Y dado X en una Curva Bezier cúbica?
- 18. Python PIL: ¿Cómo dibujar una elipse en el medio de una imagen?
- 19. Una forma de dibujar la curva equidistante
- 20. Esquema de la curva de bezier cúbica trazo
- 21. Contorno de imagen usando python/PIL
- 22. Cocos2D curva de Bezier alrededor del objeto como por gravedad
- 23. ¿Cómo dibujar una línea curva suave en WPF?
- 24. ¿Cómo puedo dibujar una curva que varía en grosor a lo largo de su trayectoria?
- 25. python PIL dibujar texto multilínea en la imagen
- 26. Dibujar una flecha en un segmento de Bezier cuadrático usando xaml
- 27. Dibujar una curva perfecta conexión de tres puntos
- 28. Recortar la imagen usando PIL en python
- 29. ¿cómo puedo dibujar una jerarquía de árbol usando JUNG?
- 30. ¿Cómo puedo dibujar un semicírculo usando CoreGraphics
gracias por su respuesta, puedo terminar haciendo esto al final. eso es lo que quise decir cuando dije "supongo que podría calcular píxel por píxel" ... que podría hacer los cálculos pero me preguntaba si se podría usar algo incorporado. – carrier