Skip to content

Commit

Permalink
feature: use cached embed colors
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroquinc committed Nov 11, 2024
1 parent 611daf9 commit 36a4020
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/achievements.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def create_achievement_embed(game, user, achievement, profile, current, total):
completion = game.total_achievements_earned - total + current
percentage = (completion / game.total_achievements) * 100
unlock_percentage = (game.achievements[achievement.title]['NumAwardedHardcore'] / game.total_players_hardcore) * 100 if game.total_players_hardcore else 0
most_common_color = get_discord_color(achievement.badge_url)
most_common_color = get_discord_color(achievement.game_icon)

# Load emoji mappings
with open('emoji.json') as f:
Expand Down
92 changes: 66 additions & 26 deletions utils/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,87 @@
import requests
from io import BytesIO
import numpy as np
import os
import json
from sklearn.cluster import KMeans

def get_discord_color(image_url, num_colors=5, crop_percentage=0.5):
def cache_color(image_url, cache_file='image_cache.json', num_clusters=3):
"""
Get the most distinct color from the center of an image for Discord usage.
Cache the most vibrant and colorful area of the image to avoid recalculating it.
Args:
image_url (str): The URL of the image to analyze.
num_colors (int): Number of dominant colors to extract (default is 5).
crop_percentage (float): Percentage of the image to keep in the center (default is 0.5).
image_url (str): The URL of the image to cache.
cache_file (str): Path to the JSON file used to store the cached color.
num_clusters (int): Number of color clusters to detect.
Returns:
int: The most distinct color in hexadecimal format.
Raises:
(Exception): If there is an issue with fetching or processing the image.
int: The most vibrant and colorful color in hexadecimal format.
"""
# Check if the cache file exists
if os.path.exists(cache_file):
with open(cache_file, 'r') as f:
cached_data = json.load(f)
if image_url in cached_data:
# If the color is already cached, return it
return cached_data[image_url]

# If color not cached, fetch and calculate it
response = requests.get(image_url)
img = Image.open(BytesIO(response.content))
img = img.convert("RGB") # Ensure the image is in RGB format

# Calculate the crop dimensions
width, height = img.size
crop_width = int(width * crop_percentage)
crop_height = int(height * crop_percentage)
left = (width - crop_width) // 2
top = (height - crop_height) // 2
right = left + crop_width
bottom = top + crop_height
# Resize the image for faster processing
img = img.resize((img.width // 2, img.height // 2))

img = img.crop((left, top, right, bottom))
# Convert the image to a NumPy array
img_data = np.array(img)

img = img.resize((img.width // 2, img.height // 2)) # Resize for faster processing
# Reshape the image data into a 2D array of pixels for clustering
pixels = img_data.reshape((-1, 3))

img = img.convert("RGB") # Ensure the image is in RGB format
# Remove colors that are too close to black/gray/white
mask = np.linalg.norm(pixels, axis=1) > 50 # A simple threshold to remove very dark colors
filtered_pixels = pixels[mask]

# Perform KMeans clustering to find color clusters
kmeans = KMeans(n_clusters=num_clusters, random_state=0)
kmeans.fit(filtered_pixels)

# Get the centers of the clusters (the most common colors)
cluster_centers = kmeans.cluster_centers_

# Calculate the distance from each cluster center to the black/white axis (i.e., evaluate vibrancy)
distances = np.linalg.norm(cluster_centers - np.array([0, 0, 0]), axis=1)

# Get the most vibrant cluster (the one that is farthest from black)
most_vibrant_cluster_idx = np.argmax(distances)
vibrant_color = cluster_centers[most_vibrant_cluster_idx]

img_array = np.array(img)
img_flattened = img_array.reshape(-1, 3)
# Convert the RGB color to hexadecimal format
vibrant_color_hex = int('0x{:02x}{:02x}{:02x}'.format(*vibrant_color.astype(int)), 16)

kmeans = KMeans(n_clusters=num_colors)
kmeans.fit(img_flattened)
# Cache the color
if not os.path.exists(cache_file):
cached_data = {}

dominant_color = kmeans.cluster_centers_[np.argmax(np.bincount(kmeans.labels_))]
cached_data[image_url] = vibrant_color_hex

return int('0x{:02x}{:02x}{:02x}'.format(*dominant_color.astype(int)), 16)
# Save the color to the cache file
with open(cache_file, 'w') as f:
json.dump(cached_data, f)

return vibrant_color_hex

def get_discord_color(image_url, cache_file='image_cache.json', num_clusters=3):
"""
Get the most vibrant and colorful color (Discord color) from the image for caching.
Args:
image_url (str): The URL of the image to analyze.
cache_file (str): Path to the JSON file used to store cached color.
num_clusters (int): Number of color clusters to detect.
Returns:
int: The most vibrant and colorful color in hexadecimal format.
"""
return cache_color(image_url, cache_file, num_clusters)

0 comments on commit 36a4020

Please sign in to comment.