Fix drawing of lines beyond text box

Also:
	- Extend kwargs for logging decroators
	- Better line wrapping
This commit is contained in:
Matthias Bisping 2023-02-13 18:40:26 +01:00
parent dcdfe03f43
commit 4ec7cb8d7b
2 changed files with 53 additions and 24 deletions

View File

@ -4,19 +4,26 @@ from operator import attrgetter
from typing import Callable, Any
import loguru
from funcy import log_calls
from funcy import log_calls, log_enters, log_exits
dev_logger = loguru.logger
logger = loguru.logger
logger.remove()
dev_logger.remove()
dev_logger.add(
debug_logger = loguru.logger
debug_logger.add(
sink=sys.stderr,
format="<green>{time:YYYY-MM-DD at HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>: <level>{message}</level>",
format="<blue>{time:YYYY-MM-DD at HH:mm:ss}</blue> | <level>{level: <8}</level> | <cyan>{name}</cyan>: <level>{message}</level>",
level="TRACE",
)
prod_logger = loguru.logger
dev_logger = loguru.logger
dev_logger.add(
sink=sys.stderr,
format="<green>{time:YYYY-MM-DD at HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>: <level>{message}</level>",
level="DEBUG",
)
prod_logger = loguru.logger
prod_logger.add(
sink=sys.stderr,
format="<white>{time:YYYY-MM-DD at HH:mm:ss}</white> | <level>{level: <8}</level> | <cyan>{name}</cyan>: <level>{message}</level>",
@ -24,14 +31,24 @@ prod_logger.add(
enqueue=True,
)
logger = loguru.logger
def __log(logger, level: str) -> Callable:
def __log(logger, level: str, enters=True, exits=True) -> Callable:
print_func = get_print_func(logger, level)
def dec():
if enters and exits:
fn = log_calls
elif enters:
fn = log_enters
elif exits:
fn = log_exits
else:
raise ValueError("Must log either enters or exits")
return fn(print_func=print_func)
def inner(func: Callable) -> Callable:
@log_calls(print_func=print_func)
@dec()
@wraps(func)
def inner(*args, **kwargs) -> Any:
return func(*args, **kwargs)
@ -45,12 +62,16 @@ def get_print_func(logger, level: str):
return attrgetter(level.lower())(logger)
def dev_log(level: str = "TRACE") -> Callable:
return __log(dev_logger, level)
def debug_log(level: str = "TRACE", enters=True, exits=True) -> Callable:
return __log(debug_logger, level, enters=enters, exits=exits)
def prod_log(level: str = "TRACE") -> Callable:
return __log(prod_logger, level)
def dev_log(level: str = "TRACE", enters=True, exits=True) -> Callable:
return __log(dev_logger, level, enters=enters, exits=exits)
def prod_log(level: str = "TRACE", enters=True, exits=True) -> Callable:
return __log(prod_logger, level, enters=enters, exits=exits)
def delay(fn, *args, **kwargs):

View File

@ -1,9 +1,10 @@
from math import ceil
from typing import List
from PIL import Image, ImageDraw, ImageFont
from funcy import first
from funcy import first, take
from cv_analysis.logging import dev_logger, dev_log
from cv_analysis.logging import dev_logger, debug_log
from cv_analysis.utils.image_operations import superimpose
from cv_analysis.utils.rectangle import Rectangle
from synthesis.segment.content_rectangle import ContentRectangle
@ -18,16 +19,17 @@ class TextBlock(ContentRectangle):
self.font = font or ImageFont.load_default() # pick_random_font_available_on_system(size=font_size)
self.text_generator = text_generator or ParagraphGenerator()
@debug_log()
def __call__(self, *args, **kwargs):
pass
@dev_log()
def generate_random_text(self, rectangle: Rectangle, n_sentences=30):
@debug_log()
def generate_random_text(self, rectangle: Rectangle, n_sentences=None):
lines = self.text_generator(rectangle, n_sentences)
image = write_lines_to_image(lines, rectangle, self.font)
return self.__put_content(image)
@dev_log()
@debug_log()
def put_text(self, text: str, rectangle: Rectangle):
text_width, text_height = self.font.getsize(text)
@ -44,18 +46,18 @@ class TextBlock(ContentRectangle):
draw.text((0, 0), text, font=self.font, fill=(0, 0, 0, 255))
return self.__put_content(image)
@dev_log()
@debug_log()
def __put_content(self, image: Image.Image):
self.content = image if not self.content else superimpose(self.content, image)
assert self.content.mode == "RGBA"
return self
@dev_log()
@debug_log()
def write_lines_to_image(lines: List[str], rectangle: Rectangle, font=None) -> Image.Image:
@dev_log()
@debug_log()
def write_line(line, line_number):
draw.text((0, line_number * text_size), line, font=font, fill=(0, 0, 0, 255))
draw.text((0, line_number * text_size), f"{line_number} {line}", font=font, fill=(0, 0, 0, 255))
font = font or pick_random_mono_space_font_available_on_system()
@ -63,7 +65,13 @@ def write_lines_to_image(lines: List[str], rectangle: Rectangle, font=None) -> I
draw = ImageDraw.Draw(image)
text_size = draw.textsize(first(lines), font=font)[1]
for line_number, line in enumerate(lines):
# FIXME: This will sometimes cut off the last line, but it's better than underflowing, since then we would need to
# crop the target box to fit the text, and to the detection algorithm a cut off line makes little difference
# anyway. At least for now. If it should become relevant, we need to find a clean way to fit target box and text
# precisely.
n_lines = min(len(lines), ceil(rectangle.height / text_size))
for line_number, line in enumerate(take(n_lines, lines)):
write_line(line, line_number)
return image