Sign In

ComfyUI, Python and Webcolors

0
ComfyUI, Python and Webcolors

Introduction

While working on my ComfyUI nodes for the generation of QR codes I figured out an old and also new problem. The problem came also back while preparing a experimental Turtle Graphics Demo node. Figure 1 shows what I mean. The qrcode based node is using color tuples and the segno based node is using web color names.

A conversion if the names and the allowed color tuples or hex codes are known is easy. The problem occurs if one is using a color tuple to which no color name is assigned. In such a case one needs the closest color for solving the problem.

Motivation

As I do not like to reinvent the wheel a second time, I first search the Internet for ideas, solutions or suggestions. What I found was a Python package called webcolors. Unfortunately the examples I found to this packages were not working for me.

For this reason, I wrote myself a Python to experiment and to find an approach that works. The result can be found here. The Python script tells me next to other things (if I need) which module names available in webcolors to check this list against the documentation.

ComfyUI Node Preview

Figure 1 shows two of my ComfyUI nodes. There one can see, that one is using color tuples whereas the other node is using color names.Figure 1: QR codes nodes

What the Python Code Does

I defined a set of color tuples for testing. First it is checked if the related color name is existing. If not the script finds the closest color name to the requested color.

To do this one needs a dictionary with color names and hex values. Such a dictionary is created using the allowed specs of the python package webcolors.

Python Code Example

Code Block 1 shows the Python script I wrote. It is more or less quick and dirty written, but it works and shows what I want to see.

#!/usr/bin/python
'''Web color demo.'''

# Import the standard Python modules.
from math import sqrt

# Import the third party Python modules.
import webcolors

# Set the colors of interest.
COLOR_LIST = [(255,0,0), (0,255,0), (0,0,255),
              (0,255,255), (255,255,0), (255,0,255),
              (128,128,128), (10,32,10), (194,192,190),
              (64,64,64), (32,100,32), (150,100,150)]

# Print the package names from webcolors.
#print("*"*80, end="\n")
#mod_dict = webcolors.__dict__
#for mod_name in mod_dict:
#    if not mod_name.startswith("__") and not mod_name.startswith("_"):
#        print(mod_name)
#print("*"*80, end="\n")

# Set color spec.
SPEC = webcolors.CSS2
#SPEC = webcolors.CSS21
#SPEC = webcolors.CSS3
#SPEC = webcolors.HTML4

# Set up a the color dictionary.
SPEC_DICT = webcolors.names(spec=SPEC)
COLOR_DICT = {}
for col_name in SPEC_DICT:
    hex_val = webcolors.name_to_hex(col_name)
    COLOR_DICT[hex_val] = col_name

# ***************************
# Get closest color function.
# ***************************
def get_closest_color(request_color):
    '''Get the closest color.'''
    # Initalise the closed color name.
    closest_color_name = None
    # Create a new color dictionary.
    min_cols = {}
    # Loop over the webcolor names.
    for _, (key, name) in enumerate(COLOR_DICT.items()):
        # Get the RGB values from the color.
        rc, gc, bc = webcolors.hex_to_rgb(key)
        # Calculate the square of the color value differences.
        rd = (rc - request_color[0]) ** 2
        gd = (gc - request_color[1]) ** 2
        bd = (bc - request_color[2]) ** 2
        # Calculate the Euclidean distance.
        color_diff = int(sqrt(rd + gd + bd))
        # Add the name and the color difference value to dictionary.
        min_cols[color_diff] = name
    # Print best match for control purposes.
    print("Distance:", min(min_cols.keys()), 
          "Color name:", min_cols[min(min_cols.keys())])
    # Get closest color name.
    closest_color_name = min_cols[min(min_cols.keys())]
    # Return the closest color.
    return closest_color_name

# ************************
# Get color name function.
# ************************
def get_color_name(rgb_tuple):
    '''Get the color name.'''
    # Initialise the color name.
    color_name = None
    # Try to get the color name.
    try:
        # Convert the RGB tuple to a hex value.
        hex_value = webcolors.rgb_to_hex(rgb_tuple)
        # Get the color name if possible.
        color_name = webcolors.hex_to_name(hex_value)
    except ValueError:
        # If exact match could not be found, find the closest color.
        color_name = get_closest_color(rgb_tuple)
    # Return the color name.
    return color_name

# +++++++++++++++++++++
# Main script function.
# +++++++++++++++++++++
def main():
    '''Main script function.'''
    # Loop over the color tuples.
    for col in COLOR_LIST:
        print("Request color (rgb):", col)
        print("Request color (hex):", webcolors.rgb_to_hex(col))
        col_name = get_color_name(col)
        print(col_name)
    # return None
    return None

# Execute when the module is not initialized from an import statement.
if __name__ == '__main__':
    # Call main script function.
    main()

Code Block 1: Python code for testing web colors.

Result

An output of the script looks like:

Known colors:

Request color (rgb): (255, 0, 0)
Request color (hex): #ff0000 
red
Request color (rgb): (0, 255, 0)
Request color (hex): #00ff00
lime
Request color (rgb): (0, 0, 255)
Request color (hex): #0000ff
blue
Request color (rgb): (0, 255, 255)
Request color (hex): #00ffff
cyan
Request color (rgb): (255, 255, 0)
Request color (hex): #ffff00
yellow
Request color (rgb): (255, 0, 255) Request color (hex): #ff00ff 
magenta
Request color (rgb): (128, 128, 128)
Request color (hex): #808080
gray

Unknown (requested) colors:

Request color (rgb): (10, 32, 10)
Request color (hex): #0a200a
Distance: 34 Colorname: black
black
Request color (rgb): (194, 192, 190)
Request color (hex): #c2c0be
Distance: 2 Colorname: silver
silver
Request color (rgb): (64, 64, 64) 
Request color (hex): #404040
Distance: 27 Colorname: darkslategrey
darkslategrey 
Request color (rgb): (32, 100, 32)
Request color (hex): #206420 
Distance: 39 Colorname: forestgreen
forestgreen 
Request color (rgb): (150, 100, 150)
Request color (hex): #966496
Distance: 41 Colorname: grey
grey

I would like to point out two things in this context. Green is recognized as lime and using the CSS3 specs there is a mixture of gray/grey in recognition.

Euclidean distance calculation is used in my last version of the script. This is better working than the one approach without using a square root.

Visualisation

I used following color tuples for testing:

  • (194, 192, 190)

  • (64, 64, 64)

  • (32, 100, 32)

  • (150, 100, 150)

Figure 2 shows the color representation before and after running the script.

Figure 2: Original color and closest color

First and third example are what one expect. In the first example both are in shades of gray. In the third example both images are in greenish. In second and third example the closest color differs extremely. Dark gray comes out in a more greenish color and gray in a more reddish color.

To Do

I have to check the specs against my needs. It could be that the same web color is not the same web color all the time related to name and color representation. For example there is a color name list from TCL/TK which works in the past well for me. If all the names exist in the webcolors we are talking about is not clear. For this reason I have to read the specs and have to check webcolors against different sets of color names which are available.

Conclusion

It is necessary to check whether there is an approach that better reflects the desired result in the coloring. The approach is working but the color representations are not as good as I expected it. Especially an Octree approach has to be checked, if it could result in an improvement.

Since I was primarily interested in replacing unknown colors with meaningful known colors, a further investigation in the topic is not a priority for me. That would be a project in its own right in terms of the effort involved.

References

[1] https://github.com/zentrocdot/ComfyUI-Simple_QR_Codes

[2] https://pypi.org/project/webcolors/

[3] https://webcolors.readthedocs.io/en/latest/

[4] https://github.com/ubernostrum/webcolors/tree/trunk/tests


QR Code is a registered trademark of the DENSO WAVE INCORPORATED.

0

Comments