I got this image. The image is PNG, in mode P, palette is mode RGB. I need to stay with 16 colors, as I want the image as 4bpp. And I need to change his palette, making the color pink (255, 192, 203) its first index.
The image palette is:
{(255, 255, 232): 0, (255, 192, 203): 1, (210, 204, 147): 2, (62, 214, 108): 3, (59, 193, 95): 4, (209, 174, 99): 5, (194, 164, 92): 6, (130, 186, 185): 7, (180, 148, 83): 8, (95, 152, 121): 9, (49, 161, 88): 10, (157, 123, 59): 11, (118, 97, 55): 12, (52, 128, 119): 13, (73, 80, 63): 14, (63, 59, 47): 15}
And I want:
{(255, 192, 203): 0, (255, 255, 232): 1, (210, 204, 147): 2, (62, 214, 108): 3, (59, 193, 95): 4, (209, 174, 99): 5, (194, 164, 92): 6, (130, 186, 185): 7, (180, 148, 83): 8, (95, 152, 121): 9, (49, 161, 88): 10, (157, 123, 59): 11, (118, 97, 55): 12, (52, 128, 119): 13, (73, 80, 63): 14, (63, 59, 47): 15}
I made this code for it:
def change_palette(im):
colors = im.palette.colors #get colors from palette
if(PINK not in colors): #check there is pink (could be possible that there is not)
return
first = list(colors.keys())[list(colors.values()).index(0)] #colors is a dict so I get the key of the first index
colors[first] = colors[PINK]
colors[PINK] = 0 #change the value of the colors
newcolors = {}
newcolors[PINK] = colors.pop(PINK)
for key in colors:
newcolors[key] = colors[key] #reorder the dict so it is in order
colors = newcolors
newcolors = []
for key in colors:
for c in key:
newcolors.append(c) #make it a list
im.putpalette(newcolors) #change palette
But it makes the image look like this.
I tried it with other methods, like not using the putpalette and changing only the colors of the palette, but that does not make any change when I save the image. I understand the problem, but I can't seem to find a solution. I want it to look exactly the same, but with the color pink (255, 192, 203) in the first index.
If I understand correctly, you want to keep the image unchanged, and replace the index of the pink color to be 0
.
When we modify the palette, we are switching between the two colors:
All the pixels with color (255, 255, 232)
are switched to pink color (255, 192, 203)
, and all the pixels with pink color (255, 192, 203)
are switched to color (255, 255, 232)
.
The reason is that the (index) values of the pixels are not changed.
After applying the new palette, all the pixels with (index) value 0
turned to be pink, and all the (index) value 1
turned to be (255, 255, 232)
.
For fixing that, we have to switch the image data as well (assuming pink color index equals 1):
0
should be modified to be value 1
.1
should be modified to be value 0
.For convenience we may convert the data to NumPy array, and convert back to PIL Image:
pink_index = colors[PINK] # Original index of pink color
...
indexed = np.array(im) # Convert to NumPy array to easier access.
new_indexed = indexed.copy() # Make a copy of the NumPy array
new_indexed[indexed == 0] = pink_index # Replace all original 0 pixels with pink_index
new_indexed[indexed == pink_index] = 0 # Replace all original pink_index pixels with 0
new_im = Image.fromarray(new_indexed) # Convert from NumPy array to Image.
new_im.putpalette(newcolors) # Set the palette
Complete code sample:
from PIL import Image
import numpy as np
PINK = (255, 192, 203)
def change_palette(im):
colors = im.palette.colors #get colors from palette
if(PINK not in colors): #check there is pink (could be possible that there is not)
return
first = list(colors.keys())[list(colors.values()).index(0)] #colors is a dict so I get the key of the first index
pink_index = colors[PINK]
colors[first] = colors[PINK]
colors[PINK] = 0 #change the value of the colors
newcolors = {}
newcolors[PINK] = colors.pop(PINK)
for key in colors:
newcolors[key] = colors[key] #reorder the dict so it is in order
colors = newcolors
newcolors = []
for key in colors:
for c in key:
newcolors.append(c) #make it a list
#im.putpalette(newcolors) #change palette
indexed = np.array(im) # Convert to NumPy array to easier access https://stackoverflow.com/a/33023875/4926757
new_indexed = indexed.copy() # Make a copy of the NumPy array
new_indexed[indexed == 0] = pink_index # Replace all original 0 pixels with pink_index
new_indexed[indexed == pink_index] = 0 # Replace all original pink_index pixels with 0
new_im = Image.fromarray(new_indexed) # Convert from NumPy array to Image https://stackoverflow.com/a/39258561/4926757
new_im.putpalette(newcolors) # Set the palette
return new_im
img = Image.open('original_image.png')
new_image = change_palette(img)
new_image.save('changed_image.png')