FloodFill OpenCV

El algoritmo floodfill es usado en muchos programas de edición de imágenes, como: Paint, GIMP, etc., para cambiar el color de relleno de una figura, en OpenCV disponemos de la función cv::floodFill() que implementa dicho algoritmo, podemos usarlo para rellenar una figura con un determinado color, o para seleccionar una área que cumpla con el rango de colores establecido.

Como primera demostración vamos crear una aplicación que nos permitirá seleccionar un conjunto de pixeles continuos del mismo color, al hacer clic con el ratón se toma la posición actual y le se pasa a la función, esta analizara los pixeles vecinos y seleccionará todos aquellos que sean de igual color que el pixel en la posición inicial.

import cv2
import numpy as np

def mouse_clic(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONUP:
        src = param.copy()

        cv2.floodFill(src, None, (x, y), (0, 255, 255))
        cv2.imshow('Fill zone', src)

def main():
    winname = 'Flood fill'
    img = cv2.imread('data/opencv-logo.png')

    cv2.namedWindow(winname)
    cv2.setMouseCallback(winname, mouse_clic, img)

    while(1):
        cv2.imshow(winname, img)
        if cv2.waitKey(20) & 0xFF == 27:
            break

    cv2.destroyAllWindows()
    
if __name__ == '__main__':
    main()

Este código tiene dos partes importantes, primero cargamos la imagen y añadimos el callback para responder a los eventos del ratón, luego iniciamos el bucle que nos permitirá actualizar la ventana cada vez que se detecte un clic, todo esto ya lo hemos visto en tutoriales anteriores, por lo que nos vamos a lo que nos interesa:

def mouse_clic(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONUP:
        src = param.copy()

        cv2.floodFill(src, None, (x, y), (0, 255, 255))
        cv2.imshow('Fill zone', src)

A la función cv2.floodFill le pasamos la imagen fuente, una mascará, de momento no la utilizaremos, seguido de el punto de origen para aplicar el algoritmo y luego el nuevo color que se aplica a todos los pixeles conectados al punto de origen.

algortimo floodfill

A la derecha la imagen original, al hacer clic sobre cualquier área del fondo de la imagen, se muestra el resultado de la derecha, puedes probar y hacer clic sobre otras áreas y ver como son seleccionadas las mismas.

En caso de que el color que deseemos extraer no sea uniforme podemos establecer un rango de tolerancia, es decir un mínimo y máximo permito de cercanía al color original, para esto añadiremos un trackbar que nos permitirá seleccionar el nivel de tolerancia entre 0 – 100, veamos como se hace:

import cv2
import numpy as np

tolerancia = 1
point = (0, 0)

def floodFill():
    src = img.copy()

    connectivity = 4
    flags = connectivity
    flags |= cv2.FLOODFILL_FIXED_RANGE

    cv2.floodFill(src, None, point, (0, 255, 255), (tolerancia,) * 3, (tolerancia,) * 3, flags)
    cv2.imshow('relleno', src)

def mouse_clic(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONUP:
        global point
        point = (x, y)
        floodFill()

def trackbar_value(value):
    global tolerancia
    tolerancia = value
    floodFill()

def main():
    global img

    winname = 'Flood fill'
    img = cv2.imread('data/image.png')

    cv2.namedWindow(winname)
    cv2.setMouseCallback(winname, mouse_clic, img)
    cv2.createTrackbar('Tolerancia', winname, tolerancia, 100, trackbar_value)

    while(1):
        cv2.imshow(winname, img)
        if cv2.waitKey(20) & 0xFF == 27:
            break

    cv2.destroyAllWindows()
    
if __name__ == '__main__':
    main()

La parte que nos interesa de este código en la siguiente:

def floodFill():
    src = img.copy()

    connectivity = 4
    flags = connectivity
    flags |= cv2.FLOODFILL_FIXED_RANGE

    cv2.floodFill(src, None, point, (0, 255, 255), (tolerancia,) * 3, (tolerancia,) * 3, flags)

Primero la variable connectivity nos permite definir el nivel de conectividad a tomar en cuenta al analizar a los pixeles vecinos, puede ser 4 o 8, al establecer el flag cv2.FLOODFILL_FIXED_RANGE se tomará en cuenta la distancia entre los vecinos, finalmente en nuestra función indicamos el nivel mínimo y máximo de tolerancia, representado por la variable del mismo nombre.

Ejemplo, con tolerancia = 1, tenemos:

floodfill tolencia 1

Ejemplo, con tolerancia = 25, tenemos:

opencv floodfill

La barra para seleccionar la tolerancia aparece en la parte superior de la aplicación, en las imágenes no se observa, también puedes hacer clic en otra área, el sombrero por ejemplo, y modificar la tolerancia hasta seleccionar todos los pixeles que lo conforman, otra cosa que puedes hacer es agregar otras barras que te permitan establecer los canales RGB del nuevo color que deseas aplicar.

Descargar: opencv-floodfill.zip

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API