2008-10-29 5 views

ответ

11

Кривая Безье не так уж трудно нарисовать. Учитывая три очка A, B, C, вам понадобится три линейных интерполяции для рисования кривой. Мы используем скалярную t в качестве параметра для линейной интерполяции:

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

Это интерполяция между двумя краями, которые мы создали, ребра AB и края BC. Единственное, что мы теперь должны сделать, чтобы вычислить точку, мы должны сделать это интерполировать между P0 и P1 с использованием тех же т, как так:

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

Есть несколько вещей, которые нужно сделать, прежде чем мы на самом деле нарисуйте кривую. Прежде всего, мы будем ходить около dt (delta t), и мы должны знать, что 0 <= t <= 1. Как вы могли бы себе представить, это не даст нам гладкой кривой, вместо этого оно дает только дискретный набор позиций, на которых можно построить график. Самый простой способ решить это - просто нарисовать линию между текущей точкой и предыдущей точкой.

+0

спасибо за ваш ответ, я могу в конечном итоге сделать это в конце. это то, что я имел в виду, когда я сказал: «Думаю, я мог бы рассчитать пиксель за пикселем» ... что я мог бы просто сделать математику, но задавался вопросом, можно ли использовать что-то встроенное. – carrier 2008-10-29 12:54:15

7

Вы можете использовать aggdraw сверху PIL, кривые безье - supported.

EDIT:

Я привел пример только чтобы обнаружить, есть ошибка в Path классе относительно curveto :(

Вот пример в любом случае:

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 должен исправить ошибку, если вы хотите перекомпилировать модуль ...

+0

+1 для ссылки о том, как исправить ошибку Aggdraw bezier, слишком плохо, что привязки Python не были обновлены, чтобы исправить это. – 2014-01-28 19:18:52

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 

Это, к примеру, рисует сердце:

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

Полнота Fro: вам нужно «из изображения импорта PIL» и «из импорта PIL ImageDraw» для этого. – steffen 2012-03-01 20:51:34

4

Хотя Безье curveto дорожки не работают с Aggdraw, как было упомянуто @ ToniRuža, есть другой способ сделать это в Aggdraw. Преимущество использования Aggdraw вместо PIL или ваших собственных функций Безье заключается в том, что Aggdraw будет антиалиасировать изображение, делая его более гладким (см. Рис. Внизу).

Aggdraw Символы

Вместо того, чтобы использовать класс aggdraw.Path(), чтобы рисовать, вы можете использовать aggdraw.Symbol(pathstring) класс, который является в основном то же самое, за исключением вы пишете путь в виде строки. Согласно документам Aggdraw, путь записи вашего пути в виде строки заключается в использовании синтаксиса пути SVG (см.: http://www.w3.org/TR/SVG/paths.html).В основном, каждый сложение (узел) на путь обычно начинается с

  • письмо, представляющее действие рисования (верхний регистр для абсолютного пути, в нижнем регистре для относительного пути), а затем (без пробелов между ними)
  • х координат (предшествующий знак минуса, если это отрицательное число или направление)
  • запятой
  • координаты у (предшествующий знак минуса, если это отрицательное число или направление)

В вашем дорожка строка просто отделяет ваши несколько узлов пробелом. После того, как вы создали свой символ, просто запишите его, передав его как один из аргументов draw.symbol(args).

Безье Кривые в Aggdraw Символы

Специально для кубических кривых Безье вы пишете букву "C" или "C", а затем 6 чисел (3 комплекта ху координаты x1, y1, x2, y2, x3 , y3 с запятыми между цифрами, но не между первым числом и буквой). Согласно документам есть и другие версии безье, используя букву «S (гладкая кубическая безье), Q (квадратичная безье), T (гладкая квадратичная безье)». Вот полный пример кода (требуется PIL и 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!" 

И выход является гладкой вид изогнутой Безье фигура: Result from script above using aggdraw bezier curve symbol

0

Я нашел простой способ при создании кривой Безье (без aggraw и без сложных функций).

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