Last active
June 20, 2022 11:30
-
-
Save Seb105/6496847f43f13cffca505ef64f4b1778 to your computer and use it in GitHub Desktop.
Converts 1 solid colour to another, handling transitions. Good for recoloring camos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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