advent22/api/advent22_api/routers/_image.py

136 lines
3.4 KiB
Python
Raw Normal View History

2022-10-14 23:35:17 +00:00
import colorsys
2022-10-14 22:09:23 +00:00
from dataclasses import dataclass
import numpy as np
from PIL import Image, ImageDraw, ImageFont
@dataclass
class AdventImage:
img: Image.Image
@classmethod
async def load_standard(cls, fp) -> "AdventImage":
"""
Bild laden und einen quadratischen Ausschnitt
aus der Mitte nehmen
"""
# Bild laden
img = Image.open(fp=fp)
# Größen bestimmen
width, height = img.size
square = min(width, height)
# Bild zuschneiden und skalieren
img = img.crop(box=(
int((width - square)/2),
int((height - square)/2),
int((width + square)/2),
int((height + square)/2),
))
img = img.resize(
size=(500, 500),
resample=Image.ANTIALIAS,
)
# Farbmodell festlegen
return cls(img=img.convert("RGB"))
async def get_text_box(
self,
xy: tuple[float, float],
text: str | bytes,
font: "ImageFont._Font",
anchor: str | None = "mm",
**text_kwargs,
) -> tuple[int, int, int, int] | None:
"""
Koordinaten (links, oben, rechts, unten) des betroffenen
2022-10-27 22:32:20 +00:00
Rechtecks bestimmen, wenn das Bild mit einem Text
2022-10-14 22:09:23 +00:00
versehen wird
"""
# Neues 1-Bit Bild, gleiche Größe
mask = Image.new(mode="1", size=self.img.size, color=0)
# Text auf Maske auftragen
ImageDraw.Draw(mask).text(
xy=xy,
text=text,
font=font,
anchor=anchor,
fill=1,
**text_kwargs,
)
# betroffenen Pixelbereich bestimmen
return mask.getbbox()
async def get_average_color(
self,
box: tuple[int, int, int, int],
) -> tuple[int, int, int]:
"""
Durchschnittsfarbe eines rechteckigen Ausschnitts in
2022-10-27 22:32:20 +00:00
einem Bild berechnen
2022-10-14 22:09:23 +00:00
"""
pixel_data = self.img.crop(box).getdata()
mean_color: np.ndarray = np.mean(pixel_data, axis=0)
return tuple(mean_color.astype(int).tolist())
2022-10-14 23:35:17 +00:00
async def hide_text(
self,
xy: tuple[float, float],
text: str | bytes,
font: "ImageFont._Font",
anchor: str | None = "mm",
**text_kwargs,
) -> None:
2022-10-27 22:32:20 +00:00
"""
Text `text` in Bild an Position `xy` verstecken.
Weitere Parameter wie bei `ImageDraw.text()`.
"""
2022-10-14 23:35:17 +00:00
# betroffenen Bildbereich bestimmen
text_box = await self.get_text_box(
xy=xy,
text=text,
font=font,
anchor=anchor,
**text_kwargs
)
if text_box is not None:
# Durchschnittsfarbe bestimmen
text_color = await self.get_average_color(
box=text_box,
)
# etwas heller/dunkler machen
tc_h, tc_s, tc_v = colorsys.rgb_to_hsv(*text_color)
tc_v = int((tc_v - 127) * 0.97) + 127
if tc_v < 127:
tc_v += 3
else:
tc_v -= 3
text_color = colorsys.hsv_to_rgb(tc_h, tc_s, tc_v)
text_color = tuple(int(val) for val in text_color)
# Buchstaben verstecken
ImageDraw.Draw(self.img).text(
xy=xy,
text=text,
font=font,
fill=text_color,
anchor=anchor,
**text_kwargs
)