我正在使用Python的圖像庫,我想繪製一些貝塞爾曲線。 我想我可以按像素來計算像素,但我希望有更簡單的東西。如何使用Python的PIL繪製貝塞爾曲線?
回答
貝塞爾曲線並不是很難繪製自己。給定三點A
,B
,C
您需要三個線性插值才能繪製曲線。我們使用標t
作爲線性插值參數:
P0 = A * t + (1 - t) * B
P1 = B * t + (1 - t) * C
這插值兩個邊緣,我們已經創建,邊AB和BC邊緣之間。我們現在唯一要做的計算我們得出使用相同的T像這樣P0和P1之間的插值點:
Pfinal = P0 * t + (1 - t) * P1
有一對夫婦的需要被我們其實之前做過的事繪製曲線。首先我們會走一些dt
(德耳塔t),我們需要知道0 <= t <= 1
。正如你可以想象的那樣,這不會給我們一條平滑的曲線,而只會產生一組離散的位置。解決這個問題的最簡單方法是簡單地在當前點和前一點之間劃一條線。
您可以在PIL上面使用aggdraw,貝塞爾曲線爲supported。
編輯:
我做出了榜樣,才發現有在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應修復錯誤,如果你想重新編譯模塊...
對於如何修復Aggdraw bezier錯誤的鏈接+1,太糟糕了,Python綁定尚未更新以修復它。 – 2014-01-28 19:18:52
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')
Fro完整性:您需要`從PIL導入Image`和`從PIL導入ImageDraw`才能工作。 – steffen 2012-03-01 20:51:34
雖然貝塞爾curveto路徑不Aggdraw工作,通過@ToniRuža提到的,還有另一種方式在Aggdraw做到這一點。使用Aggdraw代替PIL或您自己的貝塞爾函數的好處是,Aggdraw可以使圖像抗鋸齒,使圖像更加平滑(請參見底部的圖片)。
Aggdraw符號
除了使用aggdraw.Path()類繪製的,你可以使用aggdraw.Symbol(pathstring)
類是基本相同的,除非你寫的路徑作爲一個字符串。根據Aggdraw文檔,將路徑作爲字符串編寫的方式是使用SVG路徑語法(請參閱:http://www.w3.org/TR/SVG/paths.html)。基本上,每個加法(節點)的路徑通常與
- 開始表示牽伸動作(大寫爲絕對路徑,小寫用於相對路徑)的信,接着(沒有空格之間)
- 在x座標(由減號之前,如果它是一個負數或方向)
- 逗號
- y座標(由減號之前,如果它是一個負數或方向)
在你路徑字符串只是用空格分隔多個節點。一旦你創建了你的符號,只要記住把它作爲draw.symbol(args)
的參數之一來傳遞就可以了。
Bezier曲線在Aggdraw符號
具體地爲你寫的字母 「C」 或 「c」,接着6位數字(3套的xy座標X1,Y1,X2,Y2,X3三次Bezier曲線,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!"
和輸出是一個平滑的外觀曲線貝塞爾曲線圖:
我發現了一個更簡單的方法創建一個貝塞爾曲線(不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()
- 1. 繪製橢圓與二次貝塞爾曲線和三次貝塞爾曲線
- 2. 如何使用貝塞爾曲線繪製B樣條?
- 3. 使用谷歌地圖折線繪製貝塞爾曲線
- 4. 如何動態繪製「漂亮」的貝塞爾曲線?
- 5. OpenGL:如何繪製度數高於8的貝塞爾曲線?
- 6. 使用貝塞爾曲線繪製螺旋
- 7. 如何在UIView中繪製貝塞爾曲線
- 8. 如何在Matlab中繪製貝塞爾曲線
- 9. 如何在處理中繪製垂直貝塞爾曲線?
- 10. 如何在光柵中繪製反鋸齒貝塞爾曲線?
- 11. 用我的手指在iOS中繪製貝塞爾曲線?
- 12. HTML5畫布:用負載繪製的貝塞爾曲線
- 13. n階貝塞爾曲線?
- 14. 平滑貝塞爾曲線
- 15. 貝塞爾曲線和法國曲線
- 16. 如何使用不帶ctx.bezierCurveTo的原生Javascript代碼繪製貝塞爾曲線?
- 17. 如何使用iText繪製拋物線的一部分?或者我如何從三次貝塞爾曲線創建二次貝塞爾曲線?
- 18. 在OpenGL/GLSL中繪製貝塞爾曲線的內部陰影
- 19. 繪製貝塞爾曲線的最佳方法
- 20. 在貝塞爾曲線繪製的曲線上實現觸摸方法
- 21. 在現有網格上繪製貝塞爾曲線
- 22. 畫布:動畫貝塞爾曲線繪製
- 23. 基於三點繪製貝塞爾曲線?
- 24. 在Actionscript中繪製三次貝塞爾曲線?
- 25. 如何用wxPython繪製一個給定四點的貝塞爾曲線?
- 26. 如何在bing地圖wpf c#上繪製貝塞爾曲線折線?
- 27. 繪製貝塞爾函數
- 28. 如何在SVG/raphael的貝塞爾曲線的末端繪製箭頭?
- 29. 如何添加額外的控制點到貝塞爾曲線?
- 30. 通過三點的貝塞爾曲線
感謝您的回答,我最終可能會這樣做。這就是我的意思,當我說「我想我可以逐像素地計算像素......」,我可以做數學,但想知道是否可以使用內置的東西。 – carrier 2008-10-29 12:54:15