Home > database >  Why am I getting ValueError when using min() to find the closest matching RGB value?
Why am I getting ValueError when using min() to find the closest matching RGB value?

Time:07-17

My code should

  • take an image
  • determine the number of unique colours
  • match each of those colours to the nearest RGB of a pre-defined list of colours

The matching is failing on the 90th (of 113) unique colour with ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

I have an np array of several hundred useable colours in the format

COLOURS = np.array([
[252,252,252],
[255,226,226],
[255,201,201],
[245,173,173],
[241,135,135],
[227,109,109],
[191,45,45],
[254,215,204],
....
[70,5,45]
])

My matching code is


def findClosestRGB(rgb):
    global COLOURS
    print("RGB: ", str(rgb))
    r, g, b = rgb
    color_diffs = []
    for color in COLOURS:
        cr, cg, cb = color
        #print("cr, cg, cb: "  str(cr),"," str(cg),"," str(cb))
        color_diff = sqrt((r - cr)**2   (g - cg)**2   (b - cb)**2)
        color_diffs.append((color_diff, color))
    print(min(color_diffs))
    return min(color_diffs)[1]

The trace is:

RGB:  [176 175 175]
(3.0, array([174, 174, 177]))
uniqueColour:  [176 175 175]  matchedRGB:  [174 174 177]
RGB:  [184 188 178]
(9.848857801796104, array([184, 184, 187]))
uniqueColour:  [184 188 178]  matchedRGB:  [184 184 187]
RGB:  [191 191 191]
Traceback (most recent call last):
  File "CreatePaletteFileV0.1.py", line 1461, in <module>
    matchedRGB = findClosestRGB(uniqueColour)
  File "CreatePaletteFileV0.1.py", line 1429, in findClosestRGB
    print(min(color_diffs))
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

I cannot seem to pinpoint the cause of the error - maybe I am using min() inappropriately.

In case it is useful I want the code to replace the colours in the original image with those from my distinct colour set.

CodePudding user response:

You are using the builtin min() inappropriately. The builtin min() compares each element of the iterable against other elements. Since each element of a numpy array is the entire row, it ends up checking if one row is less than another row. For example:

row1 = np.array([1, 2, 3])
row2 = np.array([-4, 5, -6])

if row1 < row2:
    print("less")

which results in the same error you see. This is because comparing one numpy array to another gives you a numpy array where each element is the result of the element-wise comparison of the two arrays. i.e.

>>> print(row1 < row2)
[ False True False]

An array has an unambiguous boolean value, so cannot be used as an if condition without condensing it down to a single boolean using any() or all(). Of course, the builtin min() doesn't know that it's looking at numpy arrays, so it won't do this any() or all() step, and you get that error.

You could use np.min() to get what you want. However, hen you have a numpy array, you do yourself a disservice by ignoring the amazing powers of numpy, such as broadcasting and SIMD operations.

def findClosestRGB(rgb):
    # This is not needed because you never assign to the variable in this function
    # global COLOURS

    # numpy can broadcast arrays so that when you subtract a (3,) array 
    # from a (N, 3) array, it automatically subtracts the (3,) array from 
    # each row of the (N, 3)
    color_diffs = COLOURS - rgb

    # You can specify the axis argument to many numpy functions to apply them
    # along a specific axis
    color_diffs_norm = np.linalg.norm(color_diffs, axis=1)

    # np.ndarray.argmin() gives the index of the smallest value
    min_color_diff_index = color_diffs_norm.argmin()

    # Finally, return that color
    return COLOURS[min_color_diff_index, :]

CodePudding user response:

I found a piece of code which works (where subjects is my list of colours).

def nearest_colour( subjects, query ):
    return min( subjects, key = lambda subject: sum( (s - q) ** 2 for s, q in zip( subject, query ) )) 

This code reduces the original 113 colours to 53 from my list.

  • Related