Рисование линии со сглаживанием с помощью библиотеки изображений Python

Я рисую кучу линий с помощью ImageDraw.line() из Python Imaging Library, но они выглядят ужасно, так как я не могу найти способ сгладить их. Как я могу сгладить строки в PIL?


person dieki    schedule 25.06.2010    source источник
comment
Возможный ответ: stackoverflow.com/questions/1828345/   -  person unutbu    schedule 26.06.2010
comment
Ну, я полагаю, что это одно из решений, хотя и медленное.   -  person dieki    schedule 26.06.2010
comment
Этот вопрос следует открыть повторно, потому что он спрашивает, как сглаживать строки в определенной библиотеке. Я отредактировал, чтобы удалить строку с просьбой о других рекомендациях.   -  person dieki    schedule 02.10.2020


Ответы (2)


Это действительно быстро собранная функция для рисования сглаженной линии с помощью PIL, которую я написал после того, как погуглил по той же проблеме, увидел этот пост, не смог установить aggdraw и был в сжатые сроки. Это реализация линейного алгоритма Сяолиня Ву. Я надеюсь, что это поможет любому, кто ищет в Google одно и то же!

:)

"""Library to draw an antialiased line."""
# http://stackoverflow.com/questions/3122049/drawing-an-anti-aliased-line-with-thepython-imaging-library
# https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
import math


def plot(draw, img, x, y, c, col, steep, dash_interval):
    """Draws an antiliased pixel on a line."""
    if steep:
        x, y = y, x
    if x < img.size[0] and y < img.size[1] and x >= 0 and y >= 0:
        c = c * (float(col[3]) / 255.0)
        p = img.getpixel((x, y))
        x = int(x)
        y = int(y)
        if dash_interval:
            d = dash_interval - 1
            if (x / dash_interval) % d == 0 and (y / dash_interval) % d == 0:
                return
        draw.point((x, y), fill=(
            int((p[0] * (1 - c)) + col[0] * c),
            int((p[1] * (1 - c)) + col[1] * c),
            int((p[2] * (1 - c)) + col[2] * c), 255))


def iround(x):
    """Rounds x to the nearest integer."""
    return ipart(x + 0.5)


def ipart(x):
    """Floors x."""
    return math.floor(x)


def fpart(x):
    """Returns the fractional part of x."""
    return x - math.floor(x)


def rfpart(x):
    """Returns the 1 minus the fractional part of x."""
    return 1 - fpart(x)


def draw_line_antialiased(draw, img, x1, y1, x2, y2, col, dash_interval=None):
    """Draw an antialised line in the PIL ImageDraw.

    Implements the Xialon Wu antialiasing algorithm.

    col - color
    """
    dx = x2 - x1
    if not dx:
        draw.line((x1, y1, x2, y2), fill=col, width=1)
        return

    dy = y2 - y1
    steep = abs(dx) < abs(dy)
    if steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2
        dx, dy = dy, dx
    if x2 < x1:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
    gradient = float(dy) / float(dx)

    # handle first endpoint
    xend = round(x1)
    yend = y1 + gradient * (xend - x1)
    xgap = rfpart(x1 + 0.5)
    xpxl1 = xend    # this will be used in the main loop
    ypxl1 = ipart(yend)
    plot(draw, img, xpxl1, ypxl1, rfpart(yend) * xgap, col, steep,
         dash_interval)
    plot(draw, img, xpxl1, ypxl1 + 1, fpart(yend) * xgap, col, steep,
         dash_interval)
    intery = yend + gradient  # first y-intersection for the main loop

    # handle second endpoint
    xend = round(x2)
    yend = y2 + gradient * (xend - x2)
    xgap = fpart(x2 + 0.5)
    xpxl2 = xend    # this will be used in the main loop
    ypxl2 = ipart(yend)
    plot(draw, img, xpxl2, ypxl2, rfpart(yend) * xgap, col, steep,
         dash_interval)
    plot(draw, img, xpxl2, ypxl2 + 1, fpart(yend) * xgap, col, steep,
         dash_interval)

    # main loop
    for x in range(int(xpxl1 + 1), int(xpxl2)):
        plot(draw, img, x, ipart(intery), rfpart(intery), col, steep,
             dash_interval)
        plot(draw, img, x, ipart(intery) + 1, fpart(intery), col, steep,
             dash_interval)
        intery = intery + gradient
person toastie    schedule 18.04.2011
comment
Что такое кол? Что такое крутой? Небольшое объяснение было бы очень признательно. - person Matt S; 17.07.2013
comment
@RamenRecon, мне тоже было интересно узнать о col и крутом. Основываясь на моем прочтении кода, крутой — это логическое значение, указывающее, меньше ли xdelta, чем ydelta, и, следовательно, если линия крутая, что, вероятно, влияет на алгоритм, но он используется только внутри функцией drawLine и ее вспомогательной функцией построения графика, так что это ничего не значит. пользователь должен беспокоиться. Col, с другой стороны, должен задаваться пользователем и представляется как цвет RGBA с четырьмя кортежами (красный, зеленый, синий, альфа), который используется для определения значения альфа-прозрачности каждой точки (т. е. эффекта сглаживания). Любопытно попробовать и посмотреть, насколько это быстро... - person Karim Bahgat; 18.03.2014
comment
Кстати, @toastie your — единственная ручная реализация Python для сглаживания, которую я где-либо видел. Я удивлен, что ваш ответ не получил больше голосов, но я уверен, что он помог многим людям. +1 - person Karim Bahgat; 18.03.2014
comment
Спасибо за этот ответ. @MattN.D.Hat, «крутой», как упомянул Карим, является логическим значением, указывающим, ближе ли линия к вертикали, чем к горизонтали. Алгоритм рисования линии Ву работает, беря пиксели по обе стороны от линии и взвешивая их (по сути, придавая им непрозрачность или альфа-значение) в зависимости от того, насколько близко они лежат к линии. Если линия ближе к вертикали, то эти пиксели горизонтальны, иначе — вертикальны. Я надеюсь, что это полезно. Этот код, по-видимому, адаптирован из статьи в Википедии по адресу en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm. - person Sam Sharp; 05.05.2015
comment
Вам не нужно знать, что такое с или крутой. Просто позвоните, draw_line_antialiased(draw, img, x1, y1, x2, y2, col) col — это цвет, который должен включать RGB и A - person gabbahey; 28.09.2020

У меня была похожая проблема, у моих линий были шероховатые края, когда менялось направление. Я взял подсказку из того, как линии рисуются в IOS, и придумал этот код. Он помещает закругленные концы строк на концы строк и действительно убирает вещи. Не совсем сглаживание, но я совершенно новичок в PIL, и мне было так трудно найти ответ, которым я решил поделиться. Нуждается в некоторой настройке, и, вероятно, есть лучший способ, но он делает то, что мне нужно :)


    from PIL import Image
    import ImageDraw

    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y

    class DrawLines:
        def draw(self, points, color, imageName):
            img = Image.new("RGBA", [1440,1080], (255,255,255,0))
            draw  =  ImageDraw.Draw(img)
            linePoints = []
            for point in points:
                draw.ellipse((point.x-7, point.y-7, point.x+7, point.y+7), fill=color)
                linePoints.append(point.x)
                linePoints.append(point.y)
            draw.line(linePoints, fill=color, width=14)
            img.save(imageName)

    p1 = Point(100,200)
    p2 = Point(190,250)
    points = [p1,p2]

    red = (255,0,0)
    drawLines = DrawLines()
    drawLines.draw(points, red, "C:\\test.png")
person Dave_750    schedule 23.10.2012