Created
June 4, 2025 08:36
-
-
Save makeevrserg/799415b0560716f52de9dcf10a559caf to your computer and use it in GitHub Desktop.
Revisions
-
makeevrserg created this gist
Jun 4, 2025 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,105 @@ from PIL import Image, ImageDraw import os import random import math import numpy as np # === НАСТРОЙКИ === ICONS_FOLDER = "icons" OUTPUT_IMAGE = "final_pattern.png" WIDTH, HEIGHT = 2000, 4000 ICON_COUNT = 160 MIN_SPACING = 15 ROTATION_RANGE = 30 SCALE_MIN, SCALE_MAX = 0.4, 1.0 # === ФУНКЦИИ === def rotate_and_paste(base, img, position, angle): rotated = img.rotate(angle, expand=True) x, y = position x -= rotated.width // 2 y -= rotated.height // 2 base.paste(rotated, (x, y), rotated) def check_overlap(x, y, w, h, placed, spacing): for px, py, pw, ph in placed: dx = x - px dy = y - py dist = math.hypot(dx, dy) if dist < ((w + pw) / 2 + spacing): return True return False def draw_decor(draw, kind, x, y, size): if kind == "circle": draw.ellipse([x - size//2, y - size//2, x + size//2, y + size//2], outline=(149, 149, 158,255), width = 2) elif kind == "square": draw.rectangle([x - size//2, y - size//2, x + size//2, y + size//2], outline=(149, 149, 158,255), width = 2) elif kind == "star": r = size / 2 points = [] for i in range(5): angle = math.radians(i * 144) sx = x + r * math.cos(angle) sy = y + r * math.sin(angle) points.append((sx, sy)) draw.polygon(points, outline=(149, 149, 158, 255), width = 2) # === ЗАГРУЗКА === canvas = Image.new("RGBA", (WIDTH, HEIGHT), (23, 23, 33, 255)) icon_paths = [os.path.join(ICONS_FOLDER, f) for f in os.listdir(ICONS_FOLDER) if f.endswith(".png")] icons = [Image.open(p).convert("RGBA") for p in icon_paths] placed = [] # === РАЗМЕЩЕНИЕ ИКОНКИ === for _ in range(ICON_COUNT): for attempt in range(100): icon = random.choice(icons) scale = random.uniform(SCALE_MIN, SCALE_MAX) new_size = (int(icon.width * scale), int(icon.height * scale)) resized = icon.resize(new_size, Image.Resampling.LANCZOS) angle = random.uniform(-ROTATION_RANGE, ROTATION_RANGE) w, h = resized.size cx = random.randint(w//2, WIDTH - w//2) cy = random.randint(h//2, HEIGHT - h//2) if not check_overlap(cx, cy, w, h, placed, MIN_SPACING): rotate_and_paste(canvas, resized, (cx, cy), angle) placed.append((cx, cy, w, h)) break # === ДЕКОРАТИВНЫЕ ЭЛЕМЕНТЫ === # draw = ImageDraw.Draw(canvas) # decor_kinds = ["circle", "square", "star"] # for _ in range(DECOR_COUNT): # for attempt in range(100): # size = random.randint(5, 14) # x = random.randint(size, WIDTH - size) # y = random.randint(size, HEIGHT - size) # if not check_overlap(x, y, size, size, placed, MIN_SPACING): # kind = random.choice(decor_kinds) # draw_decor(draw, kind, x, y, size) # placed.append((x, y, size, size)) # break # Преобразуем в массив array = np.array(canvas) # Добавим зернистость noise = np.random.normal(0, 15, array.shape[:2]) # стандартное отклонение регулирует силу шума noise = np.clip(noise, -30, 30).astype(np.int16) # Применим шум к каждому каналу (R, G, B) for i in range(3): # не трогаем альфа-канал array[..., i] = np.clip(array[..., i].astype(np.int16) + noise, 0, 255) # Вернем в изображение canvas = Image.fromarray(array.astype(np.uint8), "RGBA") # === СОХРАНЕНИЕ === canvas.save(OUTPUT_IMAGE) print(f"Готово: {OUTPUT_IMAGE}")