Refactoring: Move
This commit is contained in:
parent
fd76933b5a
commit
2fb450943e
@ -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
|
||||
|
||||
106
test/fixtures/page_generation/page.py
vendored
106
test/fixtures/page_generation/page.py
vendored
@ -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")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user