My program turns an image into a sort of mosaic art of IOS emojis. I get the dominant color of every emoji, put it in a dictionary, and when I loop through the original image (downscaled) I match each pixel to the closest emoji from the dictionary. The final result always ends up with very wrong colors. Why would Homer Simpsons face be pink love hearts? The image and the final result are below. Keep in mind the image is downscaled to about 25px wide. I have tried ColorThief dominant color, K-Means clustering, Average color. dominant_colors function is where the problem is I think. Homer Homer made in emoji art, with wrong colors class EmojiArt: def __init__(self): self.directory = os.fsencode("emoji") # Directory self.emoji_colors = {} self.downscaled_img = None self.img = None # Initialize variables for when iterating through pixels self.height = 0 self.width = 0 self.depth = 0 self.bgr_pixel = [] self.rgb_pixel = [] # Store each emoji name for the new image self.new_img_emoji_names = [] self.canvas_width = None self.canvas_height = None self.emoji_size = 32 def dominant_colors(self): """Method does not need to be called during program, it is only for initial setup. The dictionary is stored in dictionary.py""" # Loop through each file, get dominant colour and add to dictionary for file in os.listdir(self.directory): filename = os.fsdecode(file) if filename.endswith(".png"): # Open the emoji image with PIL image = Image.open(f"emoji\\{filename}") if image.mode != 'RGB': image = image.convert('RGB') ar = numpy.asarray(image) shape = ar.shape ar = ar.reshape(numpy.prod(shape[:2]), shape[2]).astype(float) kmeans = sklearn.cluster.MiniBatchKMeans( n_clusters=10, init="k-means++", max_iter=20, random_state=1000 ).fit(ar) codes = kmeans.cluster_centers_ vecs, *dist = scipy.cluster.vq.vq(ar, codes) # assign codes counts, *bins = numpy.histogram(vecs, len(codes)) # count occurrences colors = [] for index in numpy.argsort(counts)[::-1]: colors.append(tuple([int(code) for code in codes[index]])) # Add the most dominant color to emoji_colors dict self.emoji_colors[filename] = colors[0] # Most dominant color print(self.emoji_colors) pyperclip.copy(self.emoji_colors) # Copy for easy pasting in dictionary.py def downscale_image(self, path, new_size, save_filename): """ new_size = (width, height) save_filename = directory and filename """ img = Image.open(path) img.thumbnail(new_size) img.save(save_filename) self.downscaled_img = img def iterate_pixels(self, image): self.img = cv2.imread(image) self.height, self.width, self.depth = self.img.shape # Loop through each pixel and get the rgb value of it for i in range(self.height): for j in range(self.width): self.bgr_pixel = self.img[i, j] # Gets [Blue, Green, Red] values self.rgb_pixel = self.bgr_pixel[::-1] # Unpack into 3 separate variables r, g, b = self.rgb_pixel self.rgb_pixel = (int(r), int(g), int(b)) # self.rgb_pixel is now a tuple and is formatted correctly # Retrieve closest emoji closest_emoji = self.find_closest_emoji(self.rgb_pixel) self.new_img_emoji_names.append(closest_emoji) print(f"Pixel {self.rgb_pixel} matches {closest_emoji}") def find_closest_emoji(self, rgb_pixel): # FIXED: Initialize variables inside the method closest_emoji = None min_distance = float('inf') r1, g1, b1 = rgb_pixel for emoji_filename, emoji_color in emoji_colors.items(): r2, b2, g2 = emoji_color distance = math.sqrt((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) if distance < min_distance: min_distance = distance closest_emoji = emoji_filename return closest_emoji def build_emoji_art(self): # Canvas dimensions self.canvas_width = self.downscaled_img.width * self.emoji_size self.canvas_height = self.downscaled_img.height * self.emoji_size print(self.canvas_width, self.canvas_height) # Create blank canvas canvas = Image.new("RGB", (self.canvas_width, self.canvas_height), (255, 255, 255)) # Iterate through each pos in downscaled image for i in range(self.downscaled_img.height): # rows for j in range(self.downscaled_img.width): # columns # Get emoji name from list index = i * self.downscaled_img.width + j emoji_name = self.new_img_emoji_names[index] # Load and resize emoji emoji_img = Image.open(f"emoji/{emoji_name}") emoji_img = emoji_img.resize((self.emoji_size, self.emoji_size)) # Convert to RGBA to handle transparency if emoji_img.mode != 'RGBA': emoji_img = emoji_img.convert('RGBA') # Calculate where to place emoji x = j * self.emoji_size y = i * self.emoji_size canvas.paste(emoji_img, (x, y), emoji_img) # Save image final_img_name = "homer_emoji_art.png" canvas.save(final_img_name) print(f"Emoji Art saved as {final_img_name}") I tried using lots of tutorials for getting dominant color but nothing gave me colors that are perceived by the eye when you stand back a few meters. Continue reading...