【问题标题】:Implementing flood fill in PyQt5在 PyQt5 中实现洪水填充
【发布时间】:2019-02-26 19:50:12
【问题描述】:

我正在尝试实现 Paint 的等价物,为此我需要进行填充。谁能告诉如何使用 PyQt5 找出像素的颜色,并使用宽度搜索来查找相似的像素。然后将所有这些像素更改为新颜色。就在 tkinter 中有 getpixel 和 putpixel。我想知道 PyQt5 是否这样做。如果有,那么我要求展示一些实现的例子。

附:您可以不寻找像素,只需展示如何获取和替换像素。

附:如果有问题,我为我的英语道歉с:

【问题讨论】:

  • 这有点太宽泛了。看起来您只是在要求某人为您编写代码,但这可能是一个轻微的语言障碍,使我无法理解。但最好展示您尝试过的方法以及您遇到问题的地方。
  • @mrunion 我想从 tkinter 研究 putpixel 和 getpixel 的类似物。无论如何,它们是如何工作的。您可以重置对信息的引用。我只是找不到它。如有必要,例如,我可以从 tkinter 发送一个示例,以便清楚我的意思。
  • 您可以在QImage 上使用.pixelpixelColor,但速度相当慢。我发布了一个更快的工作示例。

标签: python python-3.x user-interface pyqt5 paint


【解决方案1】:

我有an implementation of a Paint program here,其中包括一个洪水填充的示例。

不幸的是,它比您想象的要复杂一些。在 Qt 中从QImage 中读取像素是可能的,您可以按如下方式进行 -

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

基本算法

使用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)

然后我们定义一个函数,对于给定的位置,它会查看所有周围的位置——如果它们还没有被查看——并测试它是 hit 还是 miss 。如果是命中,我们会存储该像素以供以后填充。

    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()

【讨论】:

  • 非常感谢您提供本教程!我去尝试在我的代码中做这样的事情с:
猜你喜欢
  • 2013-01-16
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
相关资源
最近更新 更多