Реализация заливки в PyQt5 - PullRequest
0 голосов
/ 22 сентября 2018

Я пытаюсь реализовать эквивалент Paint, и для этого мне нужно сделать заполнение.Может кто-нибудь сказать, как использовать PyQt5, чтобы узнать цвет пикселя, и использовать поиск по ширине, чтобы найти похожие пиксели.А затем измените все эти пиксели на новый цвет.Просто в ткинтере для этого были getpixel и putpixel.Мне интересно, если PyQt5 делает это.Если есть, то я прошу показать некоторые примеры реализации этого.

Ps Можно, не ища пиксели, просто показать, как брать и заменять пиксели.

Pss Прошу прощения за мой английский, если что-то не так с:

1 Ответ

0 голосов
/ 22 сентября 2018

У меня есть реализация программы Paint , которая включает в себя пример заливки.

К сожалению, это немного сложнее, чем вы можете себе представить.Чтение пикселя из QImage в Qt возможно, вы можете сделать это следующим образом -

QImage.pixel(x, y)       # returns a QRgb object
QImage.pixelColor(x, y)  # returns a QColor object

Базовый алгоритм

Базовый алгоритм заполнения Forest Fire с использованием QImage.pixel(x,y) показан ниже.Мы начинаем с преобразования нашего растрового изображения в QImage (если необходимо).

    image = self.pixmap().toImage()
    w, h = image.width(), image.height()
    x, y = e.x(), e.y()

    # Get our target color from origin.
    target_color = image.pixel(x,y)

Затем мы определяем функцию, которая для данной позиции просматривает все окружающие позиции - если они не рассматривалисьеще - и проверяет, является ли это хитом или промахом .Если это попадание, мы сохраняем этот пиксель для заполнения позже.

    def get_cardinal_points(have_seen, center_pos):
        points = []
        cx, cy = center_pos
        for x, y in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
            xx, yy = cx + x, cy + y
            if (xx >= 0 and xx < w and
                yy >= 0 and yy < h and
                (xx, yy) not in have_seen):

                points.append((xx, yy))
                have_seen.add((xx, yy))

        return points

Чтобы выполнить заливку, мы создаем QPainter для записи в наше исходное растровое изображение.Затем, начиная с нашего начального x,y, мы выполняем итерацию, проверяем кардинальные точки и - если у нас есть совпадение - помещаем эти новые квадраты в нашу очередь.Мы заполняем любые совпадающие точки по ходу.

    # Now perform the search and fill.
    p = QPainter(self.pixmap())
    p.setPen(QPen(self.active_color))

    have_seen = set()
    queue = [(x, y)]

    while queue:
        x, y = queue.pop()
        if image.pixel(x, y) == target_color:
            p.drawPoint(QPoint(x, y))
            queue.extend(get_cardinal_points(have_seen, (x, y)))

    self.update()

Производительность

QImage.pixel() может быть медленным, так что чтение / запись вышеописанной реализации непосредственно на QImage не совсемвыполнимо для очень больших изображений.После этой точки заполнение области займет> несколько секунд.

Решение, которое я использовал, заключается в преобразовании области, которую нужно заполнить, в bytes.На пиксель приходится 4 байта (RGBA).Это дает нам структуру данных, с которой гораздо быстрее взаимодействовать.

    image = self.pixmap().toImage() # Convert to image if you have a QPixmap
    w, h = image.width(), image.height()
    s = image.bits().asstring(w * h * 4)

Далее нам нужно найти 3-байтовое (RGB) значение текущего местоположения.С нашей структурой данных мы создаем пользовательскую функцию для извлечения наших байтов попадания / пропуска.

    # Lookup the 3-byte value at a given location.
    def get_pixel(x, y):
        i = (x + (y * w)) * 4
        return s[i:i+3]

    x, y = e.x(), e.y()
    target_color = get_pixel(x, y)

Фактический цикл, чтобы выполнить поиск всех точек в наших входных данных и записать их в QPixmap, еслимы находим совпадение.

    # Now perform the search and fill.
    p = QPainter(self.pixmap())
    p.setPen(QPen(self.active_color))

    have_seen = set()
    queue = [(x, y)]

    while queue:
        x, y = queue.pop()
        if get_pixel(x, y) == target_color:
            p.drawPoint(QPoint(x, y))
            queue.extend(get_cardinal_points(have_seen, (x, y)))

    self.update()
...