Re: Flood fill with threshold

"Yury V. Zaytsev" <[email protected]>
Newsgroups gmane.comp.python.image
Message-ID <1271779223.7089.37.camel@mypride>
On Fri, 2010-04-16 at 08:47 -0700, Edward Cannon wrote:

> > Right know I have found Eric Raymond's flood fill code in ImgDraw.py  
> > trying to adapt it to use a threshold, but it's not entirely clear how
> > to define distance in terms of colors. Maybe just as the distance
> > between vectors will do.
> I have found that works pretty well.

FYI, I have finished to code so that it actually works. I want to send
the solution I came up with to the list so that the others can benefit.

Interestingly enough I have found that COMPOSITE and HUE distances work
best for me depending on the circumstances. Enjoy!

import numpy as np
import colorsys as cs

def pixel_difference(col1, col2, threshold, select_criterion = "CRITERION_COMPOSITE"):
    """
    Direct reimplementation of gimp-2.6.8/app/core/gimpimage-contiguous-region.c.
    GIMP software is licensed under GPL.
    """

    if select_criterion == "CRITERION_COMPOSITE":
        max = np.max(np.abs(col1 - col2))
    elif select_criterion == "CRITERION_R":
        max = np.max(np.abs(col1[0] - col2[0]))
    elif select_criterion == "CRITERION_G":
        max = np.max(np.abs(col1[1] - col2[1]))
    elif select_criterion == "CRITERION_B":
        max = np.max(np.abs(col1[2] - col2[2]))
    elif select_criterion in ("CRITERION_H", "CRITERION_S", "CRITERION_V"):
        av0, av1, av2 = cs.rgb_to_hsv( *col1 )
        bv0, bv1, bv2 = cs.rgb_to_hsv( *col2 )
        if select_criterion == "CRITERION_H":
            dist1 = np.abs(av0 - bv0)
            dist2 = np.abs(av0 - 360 - bv0)
            dist3 = np.abs(av0 - bv0 + 360)
            max = np.min( (dist1, dist2) )
            if (max > dist3):
                max = dist3
        elif select_criterion == "CRITERION_S":
            max = np.abs(av1 - bv1)
        elif select_criterion == "CRITERION_V":
            max = np.abs(av2 - bv2)
    else:
        raise ValueError("Invalid select_criterion supplied!")

    if (max < threshold):
        return True
    else:
        return False

def floodfill(pixels, seed, value, threshold, criterion = "CRITERION_COMPOSITE", bitness = 8, return_region = False):
    """
    Fill bounded region, based on an implementation by Eric S. Raymond:
    http://mail.python.org/pipermail/image-sig/2005-September/003559.html

    Licence unspecified, might count as public domain.
    """

    max_color = np.power(2, bitness) - 1
    threshold *= 1. / max_color
    x, y = seed

    # Color normalization, so that the colors are in the range [0, 1]
    def normalize (color):
        return [value * 1. / max_color for value in color]
    
    # Get and normalize the color of the seed point
    seed_normalized = normalize(pixels[x, y])
    pixels[x, y] = value

    if return_region:
        roi = [ (x, y) ]
    
    edge = [ (x, y) ]
    while edge:
        newedge = []
        for (x, y) in edge:
            for (s, t) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
                try:
                    item = pixels[s, t]
                except IndexError:
                    pass
                else:
                    if pixel_difference(seed_normalized, normalize(item), threshold, criterion) and \
                            not np.alltrue(item == value):
                        pixels[s, t] = value
                        newedge.append((s, t))
                        if return_region and not (s, t) in roi:
                            roi.append( (s, t) )
        edge = newedge
    
    if return_region:
        return roi

 
-- 
Sincerely yours,
Yury V. Zaytsev

_______________________________________________
Image-SIG maillist  -  [email protected]
http://mail.python.org/mailman/listinfo/image-sig
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.