15

私は Python の Imaging Library を使用しており、いくつかのベジエ曲線を描画したいと考えています。ピクセルごとに計算できると思いますが、もっと簡単なものがあることを願っています。

4

5 に答える 5

24
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, memo={}):
    # This returns the nth row of Pascal's Triangle
    if n in memo:
        return memo[n]
    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))
    memo[n] = result
    return result

たとえば、これはハートを描きます。

from PIL 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')
于 2010-02-18T22:23:02.333 に答える
13

ベジェ曲線を自分で描くのはそれほど難しくありません。A3 つの点、が与えられた場合BC曲線を描くには 3 つの線形補間が必要です。t線形補間のパラメーターとしてスカラーを使用します。

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

これは、作成した 2 つのエッジ、エッジ AB とエッジ BC の間を補間します。描画するポイントを計算するために今しなければならないことは、次のように同じ t を使用して P0 と P1 の間を補間することだけです。

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

実際に曲線を描く前に、いくつかの作業を行う必要があります。まず、いくつかのdt(デルタ t) を歩きますが、そのことに注意する必要があります0 <= t <= 1。ご想像のとおり、これでは滑らかな曲線が得られず、プロットする位置の離散セットしか得られません。これを解決する最も簡単な方法は、現在のポイントと前のポイントの間に単純に線を引くことです。

于 2008-10-29T12:39:15.877 に答える
8

PIL の上でaggdrawを使用できます。ベジエ曲線がサポートされています。

編集:

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

モジュールを再コンパイルする準備ができている場合、これでバグが修正されるはずです...

于 2008-10-29T14:09:59.750 に答える
6

@ToniRužaが述べたように、ベジエcurvetoパスはAggdrawでは機能しませんが、Aggdrawでこれを行う別の方法があります。PIL や独自のベジエ関数の代わりに Aggdraw を使用する利点は、Aggdraw が画像をアンチエイリアス処理してより滑らかに見えることです (下の写真を参照)。

アグドロー記号

aggdraw.Path() クラスを使用して描画する代わりにaggdraw.Symbol(pathstring)、パスを文字列として記述する以外は基本的に同じクラスを使用できます。Aggdraw docs によると、パスを文字列として記述する方法は、SVG パス構文を使用することです ( http://www.w3.org/TR/SVG/paths.htmlを参照)。基本的に、パスへの各追加 (ノード) は通常、

  • 描画アクションを表す文字 (絶対パスの場合は大文字、相対パスの場合は小文字) の後に続く (間にスペースは入れない)
  • x 座標 (負の数または方向の場合はマイナス記号を前に付けます)
  • コンマ
  • y 座標 (負の数または方向の場合はマイナス記号を前に付けます)

パス文字列では、複数のノードをスペースで区切ります。シンボルを作成したら、それを引数の 1 つとして に渡して描画することを忘れないでくださいdraw.symbol(args)

Aggdraw シンボルのベジエ曲線

具体的には、3 次ベジエ曲線の場合、文字「C」または「c」の後に 6 つの数字を書きます (xy 座標 x1、y1、x2、y2、x3、y3 の 3 つのセットで、数字の間にカンマがありますが、最初の数字と数字の間にはありません)。手紙)。ドキュメントによると、「S (滑らかな 3 次ベジェ)、Q (2 次ベジェ)、T (滑らかな 2 次ベジェ)」という文字を使用した他のベジェ バージョンもあります。以下は完全なサンプル コードです (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!"

そして、出力は滑らかに見える曲線ベジエ図形です: aggdraw ベジェ曲線シンボルを使用した上記のスクリプトの結果

于 2014-01-28T20:58:36.973 に答える