this repo has no description
at main 6.4 kB view raw
1import json 2import numpy as np 3from PIL import Image, ImageDraw, ImageFont 4import os 5import argparse 6 7# Load configuration settings from JSON file 8with open("./config/generation.json", "r") as file: 9 config = json.load(file) 10 sky_colours = config["sky_colours"] 11 name = config["name"] 12 13def interpolate_colour(hour): 14 """Interpolate sky colour based on the given hour.""" 15 hours = sorted(map(int, sky_colours.keys())) 16 17 for i in range(len(hours) - 1): 18 if hours[i] <= hour <= hours[i + 1]: 19 t = (hour - hours[i]) / (hours[i + 1] - hours[i]) 20 c1, c2 = np.array(sky_colours[str(hours[i])]), np.array(sky_colours[str(hours[i + 1])]) 21 return tuple((1 - t) * c1 + t * c2) + (255,) 22 23 return tuple(sky_colours[str(hour)]) + (255,) 24 25def create_gradient(hour, width, height): 26 """Generate gradient with fade effect and appropriate RGBA handling.""" 27 colour = interpolate_colour(hour) 28 29 average_colour = np.mean(colour[:3]) 30 fade_ratio = 0.1 + (0.5 - 0.1) * (1 - average_colour / 255) 31 gradient_height = int(height * fade_ratio) 32 33 monochrome_colour = (int(average_colour), int(average_colour), int(average_colour), 255) 34 35 gradient_rgb = np.vstack([ 36 np.full((height - gradient_height, width, 3), colour[:3], dtype=np.uint8), 37 np.linspace(colour[:3], monochrome_colour[:3], gradient_height) 38 .astype(np.uint8) 39 .reshape(gradient_height, 1, 3) 40 .repeat(width, axis=1), 41 ]) 42 43 alpha_channel = np.full((gradient_rgb.shape[0], gradient_rgb.shape[1], 1), 255, dtype=np.uint8) 44 gradient_rgba = np.concatenate((gradient_rgb, alpha_channel), axis=2) 45 return gradient_rgba 46 47def get_available_folder(base_folder): 48 """Check if the folder already exists, and if so, create a new folder with a counter.""" 49 counter = 1 50 folder = base_folder 51 while os.path.exists(folder): 52 folder = f"{base_folder}_{counter}" 53 counter += 1 54 return folder 55 56def get_max_font_size(draw, text, font_path, max_width, max_height): 57 """Calculate the maximum font size that fits within the image dimensions.""" 58 font_size = 1 59 font = ImageFont.truetype(font_path, font_size) 60 text_width, text_height = draw.textbbox((0, 0), text, font=font)[2:4] 61 62 while text_width <= max_width and text_height <= max_height: 63 font_size += 1 64 font = ImageFont.truetype(font_path, font_size) 65 text_width, text_height = draw.textbbox((0, 0), text, font=font)[2:4] 66 67 return font_size - 1 68 69# Parse command-line arguments 70parser = argparse.ArgumentParser(description="Generate profile images or banners.") 71parser.add_argument( 72 "-p", "--profile", action="store_true", help="Generate profile image (400x400)." 73) 74parser.add_argument( 75 "-b", "--banner", action="store_true", help="Generate banner image (1500x500)." 76) 77parser.add_argument( 78 "-c", "--custom", action="store_true", help="Generate custom image with custom width and height." 79) 80parser.add_argument("-w", "--width", type=int, help="Custom image width (required with -c).") 81parser.add_argument("-H", "--height", type=int, help="Custom image height (required with -c).") 82args = parser.parse_args() 83 84# Set image dimensions based on the flags 85if args.profile: 86 width, height = 400, 400 87 output_folder = "./src/profile_pics" 88elif args.banner: 89 width, height = 1500, 500 90 output_folder = "./src/banners" 91elif args.custom: 92 if not args.width or not args.height: 93 print("Error: Custom dimensions (-w and -H) are required with the -c flag.") 94 exit(1) 95 width, height = args.width, args.height 96 output_folder = f"./src/custom_{width}x{height}" 97else: 98 print("Error: You must specify either -p, -b, or -c.") 99 exit(1) 100 101# Define font path 102font_path = "./config/fonts/madecarvingsoft.ttf" 103 104# Create output folder 105output_folder = get_available_folder(output_folder) 106os.makedirs(output_folder) 107 108# Determine which hours need image generation 109images_to_generate = [] 110for hour in range(24): 111 image_path = f"{output_folder}/{str(hour).zfill(2)}.png" 112 if not os.path.exists(image_path): 113 images_to_generate.append(hour) 114 115# Generate images for the specified hours 116for hour in images_to_generate: 117 # Create gradient 118 gradient = create_gradient(hour, width, height) 119 # Create image from gradient array 120 img = Image.fromarray(gradient, 'RGBA') 121 draw = ImageDraw.Draw(img) 122 123 # Text color calculation (adjusting based on background) 124 text_colour = ( 125 min(255, int(255 - gradient[0][0][0] * 1.2)), 126 min(255, int(255 - gradient[0][0][1] * 1.2)), 127 min(255, int(255 - gradient[0][0][2] * 1.2)), 128 255, # Full opacity for text 129 ) 130 131 # Calculate text placement, font size, and draw text 132 horizontal_padding = width * 0.1 133 usable_width = width - 2 * horizontal_padding 134 vertical_padding = height * 0.1 135 usable_height = height - 2 * vertical_padding 136 font_size = get_max_font_size(draw, name, font_path, usable_width, usable_height) 137 font = ImageFont.truetype(font_path, font_size) 138 139 # Get text bounding box and position 140 bbox = draw.textbbox((0, 0), name, font=font) 141 text_width = bbox[2] - bbox[0] 142 text_height = bbox[3] - bbox[1] 143 position_x = (width - text_width) // 2 144 position_y = (height - text_height) // 2 145 146 # Ensure text fits within padding and margins 147 position_x = max(position_x, horizontal_padding) 148 position_y = max(position_y, vertical_padding) 149 min_bottom_margin = 20 150 max_position_y = height - text_height - min_bottom_margin 151 position_y = min(position_y, max_position_y - text_height) 152 153 # Add text to image 154 text_img = Image.new("RGBA", img.size, (255, 255, 255, 0)) 155 text_draw = ImageDraw.Draw(text_img) 156 text_draw.text((position_x, position_y), name, font=font, fill=text_colour) 157 img = Image.alpha_composite(img, text_img) 158 159 # Optionally add noise to image 160 noise = np.random.normal(0, 25, (height, width, 3)).astype(np.uint8) 161 noise_img = Image.fromarray(noise, mode="RGB") 162 img = Image.blend(img.convert("RGB"), noise_img, alpha=0.1) 163 164 # Save the generated image 165 image_path = f"{output_folder}/{str(hour).zfill(2)}.png" 166 img.save(image_path) 167 168# Print success message 169print(f"Images generated successfully.") 170print(f"Images saved to: {output_folder}")