100 lines
4.3 KiB
Python
100 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Делает обои: размытый затемнённый фон + исходник в квадрате со скруглёнными углами.
|
||
|
||
Установка зависимости:
|
||
pip install pillow
|
||
|
||
Использование:
|
||
python make_wallpaper.py photo.jpg
|
||
python make_wallpaper.py photo.jpg -o wall.jpg --size 600
|
||
python make_wallpaper.py photo.jpg -W 2560 -H 1440 --size 800 --offset 0.35
|
||
"""
|
||
|
||
import argparse
|
||
from PIL import Image, ImageFilter, ImageEnhance, ImageDraw
|
||
|
||
|
||
def make_wallpaper(src_path, out_path, W=1920, H=1080, square=600,
|
||
radius_ratio=0.178, offset=0.5, blur=40, dim=0.6,
|
||
rim=True, shadow=True):
|
||
src = Image.open(src_path).convert("RGB")
|
||
sw, sh = src.size
|
||
|
||
# --- фон: масштабируем с запасом, обрезаем по центру, размываем и затемняем
|
||
scale = max(W / sw, H / sh) * 1.15
|
||
bg = src.resize((int(sw * scale), int(sh * scale)), Image.LANCZOS)
|
||
left, top = (bg.width - W) // 2, (bg.height - H) // 2
|
||
bg = bg.crop((left, top, left + W, top + H))
|
||
bg = bg.filter(ImageFilter.GaussianBlur(blur))
|
||
bg = ImageEnhance.Brightness(bg).enhance(dim)
|
||
|
||
# --- квадратный кроп из исходника
|
||
side = min(sw, sh)
|
||
if sh >= sw: # вертикальное фото — двигаем по вертикали
|
||
y = int((sh - side) * offset)
|
||
box = (0, y, side, y + side)
|
||
else: # горизонтальное — по горизонтали
|
||
x = int((sw - side) * offset)
|
||
box = (x, 0, x + side, side)
|
||
|
||
S = min(square, W, H)
|
||
R = int(S * radius_ratio)
|
||
fg = src.crop(box).resize((S, S), Image.LANCZOS)
|
||
fg = fg.filter(ImageFilter.UnsharpMask(radius=2, percent=40, threshold=3))
|
||
|
||
# --- маска со скруглением (рисуем крупно и уменьшаем = гладкие края)
|
||
SS = 4
|
||
mask = Image.new("L", (S * SS, S * SS), 0)
|
||
ImageDraw.Draw(mask).rounded_rectangle(
|
||
[0, 0, S * SS - 1, S * SS - 1], radius=R * SS, fill=255)
|
||
mask = mask.resize((S, S), Image.LANCZOS)
|
||
|
||
px, py = (W - S) // 2, (H - S) // 2
|
||
|
||
# --- мягкая тень
|
||
if shadow:
|
||
sh_layer = Image.new("L", (W, H), 0)
|
||
sh_layer.paste(mask.point(lambda v: int(v * 0.55)), (px, py + max(4, S // 40)))
|
||
sh_layer = sh_layer.filter(ImageFilter.GaussianBlur(max(8, S // 22)))
|
||
bg = Image.composite(Image.new("RGB", (W, H), (0, 0, 8)), bg, sh_layer)
|
||
|
||
bg.paste(fg, (px, py), mask)
|
||
|
||
# --- тонкая светлая рамка
|
||
if rim:
|
||
layer = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||
ImageDraw.Draw(layer).rounded_rectangle(
|
||
[px, py, px + S - 1, py + S - 1], radius=R,
|
||
outline=(255, 255, 255, 45), width=2)
|
||
bg = Image.alpha_composite(bg.convert("RGBA"), layer).convert("RGB")
|
||
|
||
bg.save(out_path, quality=95)
|
||
return out_path
|
||
|
||
|
||
def main():
|
||
p = argparse.ArgumentParser(description="Обои с размытым фоном и скруглённым квадратом")
|
||
p.add_argument("input")
|
||
p.add_argument("-o", "--output", default="wallpaper.jpg")
|
||
p.add_argument("-W", "--width", type=int, default=1920)
|
||
p.add_argument("-H", "--height", type=int, default=1080)
|
||
p.add_argument("--size", type=int, default=600, help="сторона квадрата в пикселях")
|
||
p.add_argument("--radius", type=float, default=0.178,
|
||
help="радиус скругления как доля стороны (0.178 ≈ стиль iOS)")
|
||
p.add_argument("--offset", type=float, default=0.5,
|
||
help="сдвиг кропа 0..1 (0 — верх/лево, 1 — низ/право)")
|
||
p.add_argument("--blur", type=int, default=40)
|
||
p.add_argument("--dim", type=float, default=0.6, help="яркость фона, 1.0 = без затемнения")
|
||
p.add_argument("--no-rim", action="store_true")
|
||
p.add_argument("--no-shadow", action="store_true")
|
||
a = p.parse_args()
|
||
|
||
out = make_wallpaper(a.input, a.output, a.width, a.height, a.size,
|
||
a.radius, a.offset, a.blur, a.dim,
|
||
rim=not a.no_rim, shadow=not a.no_shadow)
|
||
print("Сохранено:", out)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |