在 PyQt5 中实现洪水填充
Implementing flood fill in PyQt5
我正在尝试实现 Paint 的等效项,为此我需要进行填充。谁能告诉我如何使用 PyQt5 找出像素的颜色,并使用宽度搜索找到相似的像素。然后将所有这些像素更改为新颜色。为此,在 tkinter 中有 getpixel 和 putpixel。我想知道 PyQt5 是否会这样做。如果有,那么我要求展示一些实现这个的例子。
P.s。您可以不寻找像素,只显示如何获取和替换像素。
P.s.s.如果有什么不对,我为我的英语道歉 с:
我有 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 或错过。如果命中,我们会存储该像素以供稍后填充。
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
上实现 reading/writing 对于非常大的图像来说并不可行。在那之后,它将开始花费 > 几秒钟的时间来填充该区域。
我用过的解决方案是将要填充的区域转换成bytes
。每个像素 (RGBA) 有 4 个字节。这为我们提供了一个交互速度更快的数据结构。
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) 值。使用我们的数据结构,我们创建了一个自定义函数来检索我们的 hit/miss 字节。
# 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()
我正在尝试实现 Paint 的等效项,为此我需要进行填充。谁能告诉我如何使用 PyQt5 找出像素的颜色,并使用宽度搜索找到相似的像素。然后将所有这些像素更改为新颜色。为此,在 tkinter 中有 getpixel 和 putpixel。我想知道 PyQt5 是否会这样做。如果有,那么我要求展示一些实现这个的例子。
P.s。您可以不寻找像素,只显示如何获取和替换像素。
P.s.s.如果有什么不对,我为我的英语道歉 с:
我有 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 或错过。如果命中,我们会存储该像素以供稍后填充。
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
上实现 reading/writing 对于非常大的图像来说并不可行。在那之后,它将开始花费 > 几秒钟的时间来填充该区域。
我用过的解决方案是将要填充的区域转换成bytes
。每个像素 (RGBA) 有 4 个字节。这为我们提供了一个交互速度更快的数据结构。
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) 值。使用我们的数据结构,我们创建了一个自定义函数来检索我们的 hit/miss 字节。
# 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()