Brian
Brian

Reputation: 313

Python - How to sort a list of colors based on a color's "distance" from a source color in 3D (r, g, b) space?

Here are the basic steps to an app I'm writing in Python:

  1. Generate a list of random colors
  2. Create a mapping of each of those colors, indexed by that color's distance to a "source color" in 3D (r, g, b) space. (For instance, orange (255, 150, 0) is closer to red(255, 0, 0) than dark blue (0, 0, 100). By now, I have a list of tuples of the format (distance, color).
  3. Sort that list of tuples based on the distances I had calculated (from lowest, to highest).
  4. Retrieve a list of the sorted colors

Here is my function, and I get the following error: TypeError: 'int' object has no attribute '_getitem_' on the line sorted_by_dist = sorted(colorMap, key=lambda tup: tup[0])

# Sorts a list of colors according to distance from the source color
def sort_colors(colors, source):
    colorMap = ()
    sortedColors = list()
    for i in range(len(colors)):
        dist = dist_3d(colors[i], source)
        colorMap = colorMap + (dist, colors[i])

    sorted_by_dist = sorted(colorMap, key=lambda tup: tup[0])

    for (d, c) in sorted_by_dist:
        sortedColors.append(c)

    return sortedColors

Assuming my dist_3d() function is correct and returns an integer value (it is, and does), what am I doing wrong? I don't understand.

Upvotes: 8

Views: 4530

Answers (2)

Phil Frost
Phil Frost

Reputation: 3966

The error is because tup is an int. __getitem__ is the name of the [] operator.

But, it looks like all your function is doing is sorting a list of colors, based on distance from some reference color. And, you already have dist_3d implemented. Given that it works, then sorting your colors is much simpler. Sort them in place:

colors.sort(key=lambda color: dist_3d(color, reference_color))

or as a new list:

sorted_colors = sorted(unsorted_colors, key=lambda color: dist_3d(color, reference_color))

No need for all that tuple-building and looping and stuff in your function, as I can tell.

Upvotes: 3

jdi
jdi

Reputation: 92587

You are building your colorMap as a big single dimensional tuple, with the first index being an int. So your lambda is being passed an int and then you try to index into it.

You probably want a list of tuples:

colorMap = []
...
    dist = dist_3d(colors[i], source)
    colorMap.append((dist, colors[i]))

In terms of an approach to sorting colors, I have actually used a kdtree module for this, loaded up with all my RGB tuples. Then I can ask it for N closest colors to a given color tuple:

from kdtree import KDTree

colors = [(10,10,10),(250,50,5),(100,50,20)]
query_color = (175, 25, 50)

tree = KDTree.construct_from_data(data)
# query the 2 closest colors
nearest = tree.query(query_point=query_color, t=2)

Upvotes: 3

Related Questions