From 4ec7cb8d7b2755f67caa7efbe385a0408f94d74e Mon Sep 17 00:00:00 2001 From: Matthias Bisping Date: Mon, 13 Feb 2023 18:40:26 +0100 Subject: [PATCH] Fix drawing of lines beyond text box Also: - Extend kwargs for logging decroators - Better line wrapping --- cv_analysis/logging.py | 49 +++++++++++++++++++++++---------- synthesis/segment/text_block.py | 28 ++++++++++++------- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/cv_analysis/logging.py b/cv_analysis/logging.py index c37e3b7..7bcba7b 100644 --- a/cv_analysis/logging.py +++ b/cv_analysis/logging.py @@ -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="{time:YYYY-MM-DD at HH:mm:ss} | {level: <8} | {name}: {message}", + format="{time:YYYY-MM-DD at HH:mm:ss} | {level: <8} | {name}: {message}", level="TRACE", ) -prod_logger = loguru.logger +dev_logger = loguru.logger +dev_logger.add( + sink=sys.stderr, + format="{time:YYYY-MM-DD at HH:mm:ss} | {level: <8} | {name}: {message}", + level="DEBUG", +) +prod_logger = loguru.logger prod_logger.add( sink=sys.stderr, format="{time:YYYY-MM-DD at HH:mm:ss} | {level: <8} | {name}: {message}", @@ -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): diff --git a/synthesis/segment/text_block.py b/synthesis/segment/text_block.py index aeed013..0b80bd2 100644 --- a/synthesis/segment/text_block.py +++ b/synthesis/segment/text_block.py @@ -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