2008-10-29 9 views

Odpowiedz

11

Krzywa Beziera nie jest trudna do narysowania. Biorąc pod uwagę trzy punkty: A, B, C potrzebne są trzy liniowe interpolacje w celu narysowania krzywej. Używamy skalarne t jako parametr dla interpolacji liniowej:

P0 = A * t + (1 - t) * B 
P1 = B * t + (1 - t) * C 

Ten interpoluje między dwiema krawędziami Utworzyliśmy, EDGE AB i krawędzi BC. Jedyną rzeczą, którą mamy teraz zrobić, aby obliczyć punkt musimy zwrócić się interpolacji pomiędzy P0 i P1 przy użyciu tego samego t tak:

Pfinal = P0 * t + (1 - t) * P1 

Istnieje kilka rzeczy, które trzeba zrobić, zanim rzeczywiście narysuj krzywą. Po pierwsze, przejdziemy około dt (delta t) i musimy mieć świadomość, że 0 <= t <= 1. Jak moglibyście sobie wyobrazić, nie da to nam gładkiej krzywej, zamiast tego daje tylko dyskretny zestaw pozycji do wykreślenia. Najprostszym sposobem rozwiązania tego problemu jest po prostu narysowanie linii między bieżącym punktem a poprzednim punktem.

+0

dziękuję za odpowiedź, może skończyć to w końcu. To właśnie miałem na myśli, gdy powiedziałem: "Myślę, że mógłbym obliczyć piksel po pikselu" ... że mógłbym po prostu zrobić matematykę, ale zastanawiałem się, czy można użyć czegoś wbudowanego. – carrier

7

Możesz użyć aggdraw na górze PIL, krzywe beziera są supported.

EDIT:

zrobiłem przykład tylko do odkrycia, że ​​jest to błąd w klasie Path dotyczące curveto :(

Oto przykład tak czy owak:

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 powinny napraw błąd, jeśli masz ochotę na ponowną kompilację modułu ...

+0

+1 dla linku, jak naprawić błąd Aggdraw beziera, szkoda, że ​​powiązania w Pythonie nie zostały zaktualizowane, aby to naprawić. –

17
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 

to, na przykład, rysuje serca

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') 
+0

Dla kompletności: potrzebujesz 'z PIL import Image' i' from PIL import ImageDraw' aby to działało. – steffen

+0

@steffen: Dziękuję za poprawkę. – unutbu

4

Choć ścieżki Beziera curveTo nie działają z Aggdraw, jak wspomniano przez @ ToniRuža, istnieje inny sposób to zrobić w Aggdraw. Zaletą korzystania z Aggdraw zamiast z PIL lub z własnych funkcji beziera jest to, że Aggdraw antyaliasuje obraz, sprawiając, że wygląda on gładko (patrz rysunek u dołu).

Aggdraw Symbole

Zamiast aggdraw.Path() klasy rysować, można użyć klasy aggdraw.Symbol(pathstring) która jest zasadniczo taka sama, z wyjątkiem napisać ścieżkę jako ciąg znaków. Zgodnie z dokumentami Aggdraw sposobem napisania ścieżki jako łańcucha jest użycie składni ścieżki SVG (patrz: http://www.w3.org/TR/SVG/paths.html).Zasadniczo, każdy dodatek (node) do ścieżki zwykle zaczyna

  • literą wskazującą działanie zasysające (wielkie litery dla ścieżki bezwzględnej, małymi względnego toru), a następnie (bez przerw pomiędzy nimi)
  • x współrzędnych (poprzedzać znakiem minus, jeśli jest to liczba ujemna lub kierunek)
  • przecinek
  • współrzędna y (poprzedzać znakiem minus, jeśli jest to liczba ujemna lub kierunek)

w twojej ścieżka ciąg po prostu oddzielaj wiele węzłów spacją. Po utworzeniu symbolu pamiętaj, aby go narysować, przekazując go jako jeden z argumentów do draw.symbol(args).

Beziera Krzywe symbolami Aggdraw

szczególności do sześciennych krzywych beziera można napisać literę "c" lub "c", a następnie przez 6 cyfr (3 zestawy xy współrzędnych x1, y1, X2, Y2, X3 , y3 z przecinkami pomiędzy liczbami, ale nie między pierwszą literą a literą). Zgodnie z dokumentami istnieją również inne wersje beziera za pomocą litery "S (gładki sześcienny bezier), Q (kwadratowy bezier), T (gładki kwadratowy bezier)". Powyżej znajduje się pełna przykładowy kod (wymaga PIL i 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!" 

a wyjście jest gładka wyglądający zakrzywione bezier postać: Result from script above using aggdraw bezier curve symbol

0

znalazłem prostszy sposób tworząc krzywą Beziera (bez aggraw i bez złożonych funkcji).

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() 
Powiązane problemy