03-24-2023, 05:58 PM
(03-23-2023, 01:28 PM)thetalkietoaster Wrote: Sorry for the very late reply - managed to completely forget about this after I fixed it :/. The code's on GitHub, and it's been revised quite a bit since I posted this. Originally, I was calculating the sum RGB for each pixel and then using that as the dict key, but that was clearly a daft move when I could just do the summing afterwards.
The code I ended up with is below, with changes to account for 1. some palettes having multiple colours of the same sum RGB, and 2. some of the images having 1-2 stray pixels out of the palette. Fixing those meant switching to using pixel RGB as key like you, which then solved the performance issues.
Though looking at your example I can't believe I forgot about defaultdict, I'll fix that. Should also be using actual perceived brightness rather than RGB intensity too.Code:
def extract_sorted_palette(
layer, include_transparent, count_threshold,
current_progress, progress_fraction,
):
"""
Extracts a palette from an image, by finding the discrete RGB values
and then sorting them by total R+G+B value.
"""
palette_counts = {}
progress_step = progress_fraction / layer.height
region = layer.get_pixel_rgn(
0, 0, layer.width, layer.height
)
for index_row in range(0, layer.height):
for pixel in RowIterator(region[0:layer.width, index_row], layer.bpp):
colour_rgb = pixel[0:3]
if layer.has_alpha and pixel[3] == 0 and not include_transparent:
continue
elif colour_rgb not in palette_counts:
palette_counts[colour_rgb] = 1
else:
palette_counts[colour_rgb] += 1
gimp.progress_update(current_progress + progress_step * index_row)
# Now we've counted all the pixel colours, discard outliers and sort
palette = {}
for colour_rgb, colour_count in palette_counts.items():
colour_sum = sum(colour_rgb)
if colour_count > count_threshold:
if colour_sum in palette:
if colour_rgb != palette[colour_sum]:
colour_duplicate = palette[colour_sum]
raise KeyError(
"Multiple colours in layer with same total RGB values: " + \
str(colour_rgb) + "(" + str(colour_count) + " pixels) and " + \
str(colour_duplicate) + "(" + str(palette_counts[colour_duplicate]) + " pixels). "
"Cannot automatically sort colours by brightness. " + \
"Try increasing the 'ignore colours with less than this many pixels' setting " + \
"to drop stray pixels."
)
else:
palette[colour_sum] = colour_rgb
sorted_palette = [
palette[key] for key in sorted(list(palette.keys()))
]
return sorted_palette
I just skimmed the function extract_sorted_palette that you pasted into your post compared to your recent github version. Did you mean to change the logic when reversing the if statement:
From:
if layer.has_alpha and pixel[3] == 0 and not include_transparent:
continue
To:
if include_transparent or layer.has_alpha and pixel[3] > 0:
up the count
On a brief look, it doesn't seem right for a case when include_transparent is False. Would you be wanting something more like this:
if include_transparent or not layer.has_alpha or pixel[3] > 0:
up the count