Refactoring: Move

This commit is contained in:
Matthias Bisping 2023-02-01 16:25:50 +01:00
parent fd76933b5a
commit 2fb450943e
2 changed files with 89 additions and 83 deletions

View File

@ -2,7 +2,10 @@ from typing import Tuple
import cv2 as cv
import numpy as np
from PIL import ImageOps
from PIL import ImageOps, Image
from loguru import logger
from cv_analysis.utils.conversion import normalize_image_format_to_pil
Color = Tuple[int, int, int]
@ -49,3 +52,64 @@ def color_shift_array(image: np.ndarray, color: Color):
assert colored.shape == image.shape
return colored
def superimpose(
base_image: Image,
image_to_superimpose: Image,
crop_to_content=True,
pad=True,
) -> Image:
"""Superimposes an image with transparency onto another image.
Args:
base_image: The page image.
image_to_superimpose: The texture image.
crop_to_content: If True, the texture will be cropped to content (i.e. the bounding box of all non-transparent
parts of the texture image).
pad: If True, the texture will be padded to the size of the page.
Returns:
Image where the texture is superimposed onto the page.
"""
base_image = normalize_image_format_to_pil(base_image)
image_to_superimpose = normalize_image_format_to_pil(image_to_superimpose)
if crop_to_content:
image_to_superimpose = image_to_superimpose.crop(image_to_superimpose.getbbox())
if base_image.size != image_to_superimpose.size:
logger.trace(f"Size of page and texture do not match: {base_image.size} != {image_to_superimpose.size}")
if pad:
logger.trace(f"Padding texture before pasting to fit size {base_image.size}")
image_to_superimpose = pad_image_to_size(image_to_superimpose, base_image.size)
else:
logger.trace(f"Resizing texture before pasting to fit size {base_image.size}")
image_to_superimpose = image_to_superimpose.resize(base_image.size)
assert base_image.size == image_to_superimpose.size
assert image_to_superimpose.mode == "RGBA"
base_image.paste(image_to_superimpose, (0, 0), image_to_superimpose)
return base_image
def pad_image_to_size(image: Image, size: Tuple[int, int]) -> Image:
"""Pads an image to a given size."""
if image.size == size:
return image
if image.size[0] > size[0] or image.size[1] > size[1]:
raise ValueError(f"Image size {image.size} is larger than target size {size}.")
padded = Image.new(image.mode, size, color=255)
pasting_coords = compute_pasting_coordinates(image, padded)
assert image.mode == "RGBA"
padded.paste(image, pasting_coords)
return padded
def compute_pasting_coordinates(smaller: Image, larger: Image.Image):
"""Computes the coordinates for centrally pasting a smaller image onto a larger image."""
return abs(larger.width - smaller.width) // 2, abs(larger.height - smaller.height) // 2

View File

@ -23,7 +23,7 @@ from matplotlib.colors import ListedColormap
from cv_analysis.utils import star, rconj, conj
from cv_analysis.utils.conversion import normalize_image_format_to_array, normalize_image_format_to_pil
from cv_analysis.utils.image_operations import blur, sharpen, overlay
from cv_analysis.utils.image_operations import blur, sharpen, overlay, superimpose, compute_pasting_coordinates
from cv_analysis.utils.merging import merge_related_rectangles
from cv_analysis.utils.postprocessing import remove_overlapping, remove_included
from cv_analysis.utils.spacial import area
@ -185,7 +185,7 @@ def normalize_image_function(func):
@pytest.fixture
def texture(tinted_blank_page, base_texture):
texture = superimpose_texture_with_transparency(base_texture, tinted_blank_page)
texture = superimpose(base_texture, tinted_blank_page)
return texture
@ -215,74 +215,6 @@ def size(dpi, orientation):
return size
def superimpose_texture_with_transparency(
page: Image,
texture: Image,
crop_to_content=True,
pad=True,
) -> Image:
"""Superimposes a noise image with transparency onto a page image.
TODO: Rename page and texture to something more generic.
Args:
page: The page image.
texture: The texture image.
crop_to_content: If True, the texture will be cropped to content (i.e. the bounding box of all non-transparent
parts of the texture image).
pad: If True, the texture will be padded to the size of the page.
Returns:
Image where the texture is superimposed onto the page.
"""
page = normalize_image_format_to_pil(page)
texture = normalize_image_format_to_pil(texture)
if crop_to_content:
texture = texture.crop(texture.getbbox())
if page.size != texture.size:
logger.trace(f"Size of page and texture do not match: {page.size} != {texture.size}")
if pad:
logger.trace(f"Padding texture before pasting to fit size {page.size}")
texture = pad_image_to_size(texture, page.size)
else:
logger.trace(f"Resizing texture before pasting to fit size {page.size}")
texture = texture.resize(page.size)
assert page.size == texture.size
assert texture.mode == "RGBA"
page.paste(texture, (0, 0), texture)
return page
def pad_image_to_size(image: Image, size: Tuple[int, int]) -> Image:
"""Pads an image to a given size."""
if image.size == size:
return image
if image.size[0] > size[0] or image.size[1] > size[1]:
raise ValueError(f"Image size {image.size} is larger than target size {size}.")
padded = Image.new(image.mode, size, color=255)
pasting_coords = compute_pasting_coordinates(image, padded)
assert image.mode == "RGBA"
padded.paste(image, pasting_coords)
return padded
def compute_pasting_coordinates(smaller: Image, larger: Image.Image):
"""Computes the coordinates for centrally pasting a smaller image onto a larger image."""
return abs(larger.width - smaller.width) // 2, abs(larger.height - smaller.height) // 2
def to_array(image: Image) -> np.ndarray:
"""Converts a PIL image to a numpy array."""
return np.array(image).astype(np.float32)
class ContentGenerator:
def __init__(self):
self.constrain_layouts = True
@ -613,9 +545,9 @@ class RecursiveRandomTable(RandomContentRectangle):
c2.draw_top_border(width=1)
c2.draw_bottom_border(width=1)
c = superimpose_texture_with_transparency(c1.content, c2.content)
c = superimpose(c1.content, c2.content)
self.content = superimpose_texture_with_transparency(c, self.content)
self.content = superimpose(c, self.content)
yield self
@ -716,7 +648,7 @@ class Cell(ContentRectangle):
def fill(self, color=None):
color = color or self.background_color
image = Image.new("RGBA", (self.width, self.height), color=color)
self.content = superimpose_texture_with_transparency(image, self.content)
self.content = superimpose(image, self.content)
return self
@ -1027,7 +959,7 @@ class RandomPlot(RandomContentRectangle):
image = dump_plt_to_image(rectangle)
assert image.mode == "RGBA"
self.content = image if not self.content else superimpose_texture_with_transparency(self.content, image)
self.content = image if not self.content else superimpose(self.content, image)
def maybe():
@ -1219,7 +1151,7 @@ class TextBlock(ContentRectangle):
return self.__put_content(image)
def __put_content(self, image: Image.Image):
self.content = image if not self.content else superimpose_texture_with_transparency(self.content, image)
self.content = image if not self.content else superimpose(self.content, image)
assert self.content.mode == "RGBA"
return self
@ -1375,11 +1307,6 @@ def drop_small_boxes(boxes: Iterable[Rectangle], page_width, page_height, min_pe
return lremove(small, boxes)
def draw_boxes(page: Image, boxes: Iterable[Rectangle]):
# page = draw_rectangles(page, boxes, filled=False, annotate=True)
show_image(page, backend="pil")
@pytest.fixture
def page_with_opaque_content(
blank_page,
@ -1427,6 +1354,15 @@ def page_with_translucent_content(
texture = random_flip(texture)
texture = texture_fn(texture)
page = blend_by_multiply(page_content, texture)
return page, boxes
def blend_by_multiply(page_content, texture):
def to_array(image: Image) -> np.ndarray:
return np.array(image).astype(np.float32)
texture.putalpha(255)
page_content.putalpha(255)
factor = 1.2
@ -1443,8 +1379,7 @@ def page_with_translucent_content(
),
opacity=1,
).astype(np.uint8)
return page, boxes
return page
@pytest.fixture
@ -1459,3 +1394,10 @@ def page_with_content(
draw_boxes(page, boxes)
return page
def draw_boxes(page: Image, boxes: Iterable[Rectangle]):
from cv_analysis.utils.drawing import draw_rectangles
page = draw_rectangles(page, boxes, filled=False, annotate=True)
show_image(page, backend="pil")