I was writing a simple program that gradually fills (using floodfill) an image (black background with random white rectangles) to become totally white by clicking different areas of the image. Completed without a hitch.
So I thought of making it more interesting by toggling between filling with white color and black color. As in, if the pixel I click on is part of a white region, fill it to become black. Otherwise if it is part of a black region, fill it to become white.
However, after I change some boxes to become white, it refuses to change to black after subsequent clicking (unable to toggle the color back). Furthermore, because my rectangles are drawn using 3 or 4 pixels thick lines, after changing all the lines to black, it seems to still 'remember' that those lines exists, such that when I click on certain dark regions, occasionally the region (bounded by those invisible 'previous' lines) would become white color.
I have tried printing the pixel color to confirm that the color picked up is indeed white or black, but yet the floodfill is not filling it with the correct alternate color (written by my if/else loops)
import numpy as np
import cv2 as cv
import random
width = 800
height = 500
img = np.zeros((height, width), np.uint8)
mask = np.zeros((height+2, width+2), np.uint8)
def click_event(event, x, y, flags, param):
if event == cv.EVENT_LBUTTONDOWN:
font = cv.FONT_HERSHEY_PLAIN
strxy = "X: {0} Y: {1}".format(x,y)
print(strxy)
fillmeup(x, y)
cv.imshow("test", img)
def fillmeup(x, y):
print(img[y,x])
if img[y,x] == 0:
cv.floodFill(img, mask, (x, y), 255)
elif img[y,x] == 255:
cv.floodFill(img, mask, (x, y), 0)
def drawboxes(qty):
global img
for _ in range(qty):
w = int(random.random()*width)
h = int(random.random()*height)
x = random.randrange(0, width-w)
y = random.randrange(0, height-h)
img = cv.rectangle(img, (x, y), (x+w, y+h), 255, 2)
drawboxes(7)
cv.imshow("test", img)
cv.setMouseCallback("test", click_event)
cv.waitKey(0)
cv.destroyAllWindows()
Well, I would expect that every subsequent click on a black region would produce white, and vice versa, but it is not happening. And when it does switch back to white, it seems to be bounded by invisible lines that have been turned black already.
Below is an attached sample outcome. 01_start
03_selecting one of the thin white lines changes them to black: correct outcome
floodFill()
updates not only the image but also the mask
.
On output, pixels in the mask corresponding to filled pixels in the image are set to 1 or to the a value specified in flags as described below. It is therefore possible to use the same mask in multiple calls to the function to make sure the filled areas do not overlap.
def fillmeup(x, y):
print(img[y,x])
mask = np.zeros((height+2, width+2), np.uint8)
if img[y,x] == 0:
cv.floodFill(img, mask, (x, y), 255)
else:
cv.floodFill(img, mask, (x, y), 0)
This works for me as you describe. If you do not need mask at all, you possibly can write
cv.floodFill(img, None, (x, y), ...)
This works for me too, but I didn't find any evidence that None
mask argument is legal for floodFill()
. Please notify my to update the answer if you find if it is legal or not in any authoritative source.