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