У меня есть реализация программы 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()