82 lines
3.5 KiB
Python
82 lines
3.5 KiB
Python
import numpy as np
|
||
from PIL import Image, ImageOps, ImageFilter, ImageDraw, ImageEnhance
|
||
import random
|
||
|
||
class MapStyler:
|
||
@staticmethod
|
||
def apply_parchment_effect(pil_image, output_path=None):
|
||
"""
|
||
Применяет эффект старинного свитка к изображению PIL
|
||
Args:
|
||
pil_image: PIL Image object
|
||
output_path: необязательный путь для сохранения
|
||
Returns:
|
||
PIL Image с эффектом
|
||
"""
|
||
# Текстура пергамента
|
||
width, height = pil_image.size
|
||
parchment = MapStyler._create_parchment_texture(width, height)
|
||
|
||
# Эффект старения
|
||
result_img = Image.blend(parchment, pil_image, 0.6)
|
||
|
||
# Эффекты старения
|
||
result_img = result_img.filter(ImageFilter.SMOOTH_MORE)
|
||
result_img = ImageEnhance.Contrast(result_img).enhance(1.2)
|
||
result_img = ImageEnhance.Color(result_img).enhance(0.9)
|
||
|
||
# Декоративные элементы
|
||
result_img = MapStyler._add_decorations(result_img)
|
||
|
||
if output_path:
|
||
result_img.save(output_path, quality=95)
|
||
|
||
return result_img
|
||
|
||
@staticmethod
|
||
def _create_parchment_texture(width, height):
|
||
"""Создает текстуру пергамента"""
|
||
# Цвет пергамента
|
||
base = Image.new('RGB', (width, height), color=(240, 230, 200))
|
||
|
||
# Добавляем шум и неравномерность
|
||
noise = np.random.normal(0, 0.1, (height, width, 3)) * 255
|
||
noise = np.clip(noise, -30, 30)
|
||
noise_img = Image.fromarray(noise.astype(np.uint8), 'RGB')
|
||
|
||
return Image.blend(base, noise_img, 0.3)
|
||
|
||
@staticmethod
|
||
def _add_decorations(img):
|
||
"""Добавляет декоративные элементы"""
|
||
draw = ImageDraw.Draw(img)
|
||
width, height = img.size
|
||
|
||
# Рамка в стиле старинной книги
|
||
border_color = (139, 69, 19) # Коричневый
|
||
border_width = max(width, height) // 100
|
||
draw.rectangle([(0, 0), (width-1, height-1)], outline=border_color, width=border_width)
|
||
|
||
# Угловые узоры
|
||
|
||
corner_size = min(width, height) // 8
|
||
corners = [(0, 0, 180, 270),(width-corner_size, 0, 270, 360),(0, height-corner_size, 90, 180),(width-corner_size, height-corner_size, 0, 90)]
|
||
for x, y, start, end in corners:
|
||
draw.arc([x, y, x+corner_size, y+corner_size], start, end, fill=border_color, width=2)
|
||
|
||
# Эффект потертости по краям
|
||
mask = Image.new('L', (width, height), 255)
|
||
edge_width = min(width, height) // 15
|
||
for i in range(edge_width):
|
||
alpha = int(255 * (i / edge_width) ** 0.5)
|
||
for side in ['top', 'bottom', 'left', 'right']:
|
||
if side in ['top', 'bottom']:
|
||
box = [0, i if side == 'top' else height-1-i,
|
||
width, i+1 if side == 'top' else height-i]
|
||
else:
|
||
box = [i if side == 'left' else width-1-i, 0,
|
||
i+1 if side == 'left' else width-i, height]
|
||
mask_draw = ImageDraw.Draw(mask)
|
||
mask_draw.rectangle(box, fill=alpha)
|
||
|
||
return Image.composite(img, Image.new('RGB', img.size, (220, 210, 180)), mask) |