Creating an arc with a given thickness using PIL's Imagedraw
I am trying to create a segmented arc using PIL and Imagedraw. The arc funct开发者_运维知识库ion allows me to draw an arc easily, but it is just a line. I need to be able to place an arc of given radius and thickness(ID to OD), but AI cannot find any type of thickness or width setting. Is there a way to do this? If not, is there some other way to do this using PIL?
Snippet:
import Image
import ImageDraw
conv = 0.1
ID = 15
OD = 20
image = Image.new('1',(int(ceil(OD/conv))+2,int(ceil(OD/conv))+1), 1)
draw = ImageDraw.Draw(image)
diam = OD-ID
box=(1, 1, int(ceil(diam/conv)), int(ceil(diam/conv))) #create bounding box
draw.arc(box, 0, 90, 0) #draw circle in black
I created the following arc replacement function based on Mark's suggestion:
https://gist.github.com/skion/9259926
Probably not pixel perfect (nor fast), but seems to come close for what I need it for. If you have a better version please comment in the Gist.
def arc(draw, bbox, start, end, fill, width=1, segments=100):
"""
Hack that looks similar to PIL's draw.arc(), but can specify a line width.
"""
# radians
start *= math.pi / 180
end *= math.pi / 180
# angle step
da = (end - start) / segments
# shift end points with half a segment angle
start -= da / 2
end -= da / 2
# ellips radii
rx = (bbox[2] - bbox[0]) / 2
ry = (bbox[3] - bbox[1]) / 2
# box centre
cx = bbox[0] + rx
cy = bbox[1] + ry
# segment length
l = (rx+ry) * da / 2.0
for i in range(segments):
# angle centre
a = start + (i+0.5) * da
# x,y centre
x = cx + math.cos(a) * rx
y = cy + math.sin(a) * ry
# derivatives
dx = -math.sin(a) * rx / (rx+ry)
dy = math.cos(a) * ry / (rx+ry)
draw.line([(x-dx*l,y-dy*l), (x+dx*l, y+dy*l)], fill=fill, width=width)
PIL can't draw wide arcs, but Aggdraw can, and works well with PIL (same author).
Simulate the arc using straight line segments and put the coordinates of those segments into a list. Use draw.line
with a width
option to draw the arc.
A trick I found that can be pulled is to make a white circle inside the black circle. You can use the pieslice method to break it up as needed. The rendering is sequential, so you just have to get the ordering correct. The hard part is getting the positioning correct, due to Imagedraw's use of bounding boxes as opposed to center and radius coordinates. You have to make sure that the centers of everything end up exactly on each other.
THIS SOLUTION IS GOOD ONLY IN A LIMITED CASE, see comment.
精彩评论