开发者

Creating an arc with a given thickness using PIL's Imagedraw

开发者 https://www.devze.com 2023-03-28 19:51 出处:网络
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 radi

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号