Skip to content

Instantly share code, notes, and snippets.

@Seb105
Last active June 20, 2022 11:30
Show Gist options
  • Save Seb105/6496847f43f13cffca505ef64f4b1778 to your computer and use it in GitHub Desktop.
Save Seb105/6496847f43f13cffca505ef64f4b1778 to your computer and use it in GitHub Desktop.
Converts 1 solid colour to another, handling transitions. Good for recoloring camos.
from concurrent.futures import ProcessPoolExecutor, as_completed
from math import sqrt
from multiprocessing import cpu_count
import numpy as np
from PIL import Image
COLOUR_SET = {
(60, 59, 55): np.asarray([28, 54, 99]),
(93, 98, 69): np.asarray([74, 106, 154]),
(151, 141, 122): np.asarray([169, 190, 168]),
(187, 175, 163): np.asarray([220, 216, 120]),
}
def blend(colours_weights):
output = np.asarray([0, 0, 0])
for key, weight in colours_weights.items():
colour = COLOUR_SET[key]
output = output + weight * colour
return output.astype(np.uint8)
def process_pixel(pixel, x, y, array):
try:
key = tuple(pixel)
return COLOUR_SET[key]
except KeyError:
radius = 0
keys = set(COLOUR_SET.keys())
colours_weights = get_weights(
x, y, array, radius, keys)
while len(colours_weights) < 2:
radius += 1
colours_weights = get_weights(
x, y, array, radius, keys)
new_colour = blend(colours_weights)
return new_colour
def get_weights(x, y, array, radius, keys):
colours = {}
for x_offset in range(-radius, radius + 1):
for y_offset in range(-radius, radius + 1):
try:
distance = sqrt(x_offset**2 + y_offset**2)
if distance >= radius:
continue
neighbour_pixel = tuple(array[x + x_offset, y + y_offset])
if neighbour_pixel not in keys:
continue
weight = radius-distance
if neighbour_pixel not in colours.keys():
colours[neighbour_pixel] = weight
else:
colours[neighbour_pixel] += weight
except IndexError:
pass
weight_sum = sum(colours.values())
for key, value in colours.items():
colours[key] = value / weight_sum
return colours
def main():
camo_img = Image.open('camo_old.png').convert("RGB")
pixels_array = np.array(camo_img)
new_array = np.zeros(pixels_array.shape)
futures = []
with ProcessPoolExecutor(max_workers=cpu_count()//2) as executor:
for x, row in enumerate(pixels_array):
future = executor.submit(new_row, pixels_array, new_array, x, row)
futures.append(future)
length = pixels_array.shape[0]
for i, future in enumerate(as_completed(futures)):
x, row = future.result()
new_array[x] = row
print(f"{i}/{length}")
new_image = Image.fromarray(new_array.astype(np.uint8))
new_image.save('camo_new.png')
def new_row(pixels_array, new_array, x, row):
for y, pixel in enumerate(row):
processed_pixel = process_pixel(pixel, x, y, pixels_array)
new_array[x, y] = processed_pixel
return x, new_array[x]
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment