【问题标题】:Draw dotted or dashed rectangle from PIL从 PIL 绘制虚线或虚线矩形
【发布时间】:2021-01-24 07:50:24
【问题描述】:

我想用 PIL 画一个虚线矩形。

我知道我可以画出 4 条不同的虚线,让它看起来像一个矩形:

for x in range(0, Width_of_image, 5):
    d.line([(x, 30), (x + 2, 30)],fill ="black", width =2) 

但是有没有办法使用draw.rectangle 绘制一个矩形,即不绘制 4 条不同的线?

【问题讨论】:

标签: python python-3.x python-imaging-library


【解决方案1】:

就像其他用户所说,PIL 库(特别是 PIL.ImageDraw.ImageDraw)不提供绘制虚线的功能。我查看了有关 PIL 中虚线的 StackOverflow 问题(和其他地方),但我还没有找到一种令人满意的方法来在 PIL 中绘制虚线。我找到的答案都没有以预期的方式绘制虚线(其他 Python 库提供虚线的方式,例如使用Tkinter canvas)。

所以我编写了类DashedImageDraw(扩展PIL.ImageDraw.ImageDraw),它具有绘制虚线和虚线矩形的功能。以下是代码和如何使用它的示例:

from PIL import Image, ImageDraw
import math


class DashedImageDraw(ImageDraw.ImageDraw):

    def thick_line(self, xy, direction, fill=None, width=0):
        #xy – Sequence of 2-tuples like [(x, y), (x, y), ...]
        #direction – Sequence of 2-tuples like [(x, y), (x, y), ...]
        if xy[0] != xy[1]:
            self.line(xy, fill = fill, width = width)
        else:
            x1, y1 = xy[0]            
            dx1, dy1 = direction[0]
            dx2, dy2 = direction[1]
            if dy2 - dy1 < 0:
                x1 -= 1
            if dx2 - dx1 < 0:
                y1 -= 1
            if dy2 - dy1 != 0:
                if dx2 - dx1 != 0:
                    k = - (dx2 - dx1)/(dy2 - dy1)
                    a = 1/math.sqrt(1 + k**2)
                    b = (width*a - 1) /2
                else:
                    k = 0
                    b = (width - 1)/2
                x3 = x1 - math.floor(b)
                y3 = y1 - int(k*b)
                x4 = x1 + math.ceil(b)
                y4 = y1 + int(k*b)
            else:
                x3 = x1
                y3 = y1 - math.floor((width - 1)/2)
                x4 = x1
                y4 = y1 + math.ceil((width - 1)/2)
            self.line([(x3, y3), (x4, y4)], fill = fill, width = 1)
        return   
        
    def dashed_line(self, xy, dash=(2,2), fill=None, width=0):
        #xy – Sequence of 2-tuples like [(x, y), (x, y), ...]
        for i in range(len(xy) - 1):
            x1, y1 = xy[i]
            x2, y2 = xy[i + 1]
            x_length = x2 - x1
            y_length = y2 - y1
            length = math.sqrt(x_length**2 + y_length**2)
            dash_enabled = True
            postion = 0
            while postion <= length:
                for dash_step in dash:
                    if postion > length:
                        break
                    if dash_enabled:
                        start = postion/length
                        end = min((postion + dash_step - 1) / length, 1)
                        self.thick_line([(round(x1 + start*x_length),
                                          round(y1 + start*y_length)),
                                         (round(x1 + end*x_length),
                                          round(y1 + end*y_length))],
                                        xy, fill, width)
                    dash_enabled = not dash_enabled
                    postion += dash_step
        return

    def dashed_rectangle(self, xy, dash=(2,2), outline=None, width=0):
        #xy - Sequence of [(x1, y1), (x2, y2)] where (x1, y1) is top left corner and (x2, y2) is bottom right corner
        x1, y1 = xy[0]
        x2, y2 = xy[1]
        halfwidth1 = math.floor((width - 1)/2)
        halfwidth2 = math.ceil((width - 1)/2)
        min_dash_gap = min(dash[1::2])
        end_change1 = halfwidth1 + min_dash_gap + 1
        end_change2 = halfwidth2 + min_dash_gap + 1
        odd_width_change = (width - 1)%2        
        self.dashed_line([(x1 - halfwidth1, y1), (x2 - end_change1, y1)],
                         dash, outline, width)       
        self.dashed_line([(x2, y1 - halfwidth1), (x2, y2 - end_change1)],
                         dash, outline, width)
        self.dashed_line([(x2 + halfwidth2, y2 + odd_width_change),
                          (x1 + end_change2, y2 + odd_width_change)],
                         dash, outline, width)
        self.dashed_line([(x1 + odd_width_change, y2 + halfwidth2),
                          (x1 + odd_width_change, y1 + end_change2)],
                         dash, outline, width)
        return
        


image = Image.new('RGB', (300, 200), (255, 255, 255))
d = DashedImageDraw(image)

d.dashed_rectangle([(20, 20), (280, 180)],
                   dash = (5, 3), outline  = 'black', width = 2)

image.save("image.png", "PNG")

这条线画了长角:

d.dashed_rectangle([(20, 20), (280, 180)], dash = (5, 3), outline  = 'black', width = 2)

(20, 20) 是左上角,(280, 180) 是右下角,outline 是线条的颜色,width 是线条的宽度,dash 是表示虚线模式的元组。例如:

  • dash = (5, 3) 将画线:

    ----- ----- ----- ----- ----- -----

  • dash = (4, 2, 3, 1) 将画线:

    ---- --- ---- --- ---- --- ---- --- ----

这是上面代码生成的虚线矩形的图像:

【讨论】:

    【解决方案2】:

    我在 PIL 方面没有经验,但总体上使用了很多 python,如果您确实想使用 PIL,PIL 中的虚线上有一个问题的链接。 您可以使用其他 Python 模块来绘制虚线,例如:TurtlePygame,或者,如果使用 Tkinter,您可以将 self.canvas.itemconfig(line, dash=(4, 4)) 添加到您的工作中,使其成为虚线。

    【讨论】:

      【解决方案3】:

      这是不可能的。您可以创建自己的def 并在此处绘制虚线

      【讨论】:

        【解决方案4】:

        即使我没有使用 PIL"d" 的经验,我还是建议您创建一个接受width_of_image 和其他可能参数并执行您在问题中描述的新函数

        def dotted_rect(width_of_image, **kwargs):
            for x in range(0, width_of_image, 5):
                d.line([(x, 30), (x+2,30)], **kwargs)
        

        【讨论】:

          猜你喜欢
          • 2014-12-28
          • 1970-01-01
          • 2022-11-08
          • 2013-04-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多