|
import cv2 |
|
import numpy as np |
|
|
|
|
|
def create(image_path: str, number_of_color_clusters: int) -> list: |
|
|
|
# Read in the image. |
|
img = cv2.imread(image_path) |
|
|
|
# Get the image's height, width, and channels. |
|
img_height, img_width, img_channels = np.shape(img) |
|
|
|
# Convert the image into a list of pixels, with each pixel representing a color. |
|
pixels = np.reshape(img, (img_height * img_width, img_channels)) |
|
|
|
# Turn each number in the pixel array into a float32, needed for the kmeans() function. |
|
pixels = np.float32(pixels) |
|
|
|
# Specify kmeans clustering settings. |
|
kmeans_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) # How clustering should work |
|
kmeans_flags = cv2.KMEANS_RANDOM_CENTERS # Start clustering with a random set of samples |
|
|
|
# Cluster colors and set pixel_labels to the cluster label for each pixel, and color_clusters to the colors we've clustered pixels into. |
|
_, pixel_labels, color_clusters = cv2.kmeans(pixels, number_of_color_clusters, None, kmeans_criteria, 10, kmeans_flags) |
|
|
|
# Calculate the percentage of pixels that each color cluster represents. |
|
(pixel_label_counts, _) = np.histogram(pixel_labels, bins=number_of_color_clusters) |
|
pixel_label_percentages = pixel_label_counts / pixel_label_counts.sum() |
|
|
|
# Create a canvas on which to display the colors in percentages. |
|
color_bar_width = img_width # Because we stack the original image on top of the color bar, so they need to be the same height |
|
color_bar_height = int(img_height * .2) # Make the height proportional |
|
color_bar = np.zeros((color_bar_height, color_bar_width, 3), dtype = "uint8") |
|
|
|
# Merge the colours and percentages they represent. Then order by percentage in descending order so more prominent color clusters come first. |
|
colors_and_percentages = list(zip(color_clusters, pixel_label_percentages)) |
|
colors_and_percentages.sort(key=lambda tup: tup[1], reverse=True) |
|
|
|
# Create a variable to represent the starting point of each color in the bar. |
|
start_x = 0 |
|
|
|
# For each color and percent... |
|
for (color, percent) in colors_and_percentages: |
|
|
|
# Plot the width of the color in the bar. |
|
end_x = start_x + (percent * color_bar_width) |
|
|
|
# Create the rectangle with OpenCV. |
|
cv2.rectangle(color_bar, (int(start_x), 0), (int(end_x), color_bar_height), color.astype("uint8").tolist(), -1) |
|
|
|
# Update the starting point for the next colour. |
|
start_x = end_x |
|
|
|
# Merge the color bar and original image. |
|
combined = np.zeros((img_height + color_bar_height, img_width, 3), np.uint8) |
|
combined[:img_height, :img_width, :3] = img |
|
combined[img_height:img_height+color_bar_height, :img_width, :3] = color_bar |
|
|
|
# Return the new image. |
|
return combined |