Skip to content

Commit

Permalink
Start of adding methods for generating image from sem_seg and adding …
Browse files Browse the repository at this point in the history
…baselines and regions to page + color utils based on distinctipy
  • Loading branch information
stefanklut committed Feb 7, 2024
1 parent 3f2eca1 commit 93161a6
Show file tree
Hide file tree
Showing 2 changed files with 280 additions and 0 deletions.
42 changes: 42 additions & 0 deletions page_xml/output_pageXML.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,48 @@ def link_image(self, image_path: Path):
image_output_path = self.output_dir.joinpath(image_path.name)

copy_mode(image_path, image_output_path, mode="link")

def generate_image_from_sem_seg(self, sem_seg: torch.Tensor, image_path: Path, old_height: int, old_width: int):
"""
Generate image from sem_seg
Args:
sem_seg (torch.Tensor): sem_seg as tensor
image_path (Path): Image path, used for path name
old_height (int): height of the original image
old_width (int): width of the original image
Raises:
TypeError: Output dir has not been set
"""
if self.output_dir is None:
raise TypeError("Output dir is None")

sem_seg = torch.nn.functional.interpolate(
sem_seg[None], size=(old_height, old_width), mode="bilinear", align_corners=False
)[0]
sem_seg_image = torch.argmax(sem_seg, dim=-3).cpu().numpy()

return sem_seg_image


def add_baselines_to_page():

def add_regions_to_page(sem_seg):


def process_tensor(self, sem_seg: torch.Tensor, image_path: Path, old_height, old_width, external_processing: bool = False,):
if external_processing:
sem_seg_output_path = self.output_dir.joinpath(image_path.stem + ".png")
sem_seg_image = generate_image_from_sem_seg(sem_seg, image_path, old_height, old_width)
with AtomicFileName(file_path=sem_seg_output_path) as path:
save_image_array_to_path(str(path), sem_seg_image.astype(np.uint8))


if self.xml_regions.mode == "region":




def generate_single_page(
self,
Expand Down
238 changes: 238 additions & 0 deletions utils/color_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Based on: distinctipy https://github.com/alan-turing-institute/distinctipy

import random

import numpy as np

# pre-define interesting colours/points at corners, edges, faces and interior of
# r,g,b cube


CORNERS = [
(255, 255, 255),
(0, 0, 0),
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(0, 255, 255),
(255, 255, 0),
(255, 0, 255),
]

MID_FACE = [
(0, 127, 0),
(0, 0, 127),
(0, 255, 127),
(0, 127, 255),
(0, 127, 127),
(127, 0, 0),
(127, 127, 0),
(127, 255, 0),
(127, 0, 127),
(127, 0, 255),
(127, 255, 127),
(127, 255, 255),
(127, 127, 255),
(255, 127, 0),
(255, 0, 127),
(255, 127, 127),
(255, 255, 127),
(255, 127, 255),
]

INTERIOR = [
(127, 127, 127),
(191, 127, 127),
(63, 127, 127),
(127, 191, 127),
(127, 63, 127),
(127, 127, 191),
(127, 127, 63),
]

POINTS_OF_INTEREST = CORNERS + MID_FACE + INTERIOR
_SEED_MAX = int(2**32 - 1)


def _ensure_rng(rng):
"""
Returns a random.Random state based on the input
"""
if rng is None:
rng = random._inst
elif isinstance(rng, int):
rng = random.Random(int(rng) % _SEED_MAX)
elif isinstance(rng, float):
rng = float(rng)
# Coerce the float into an integer
a, b = rng.as_integer_ratio()
if b == 1:
seed = a
else:
s = max(a.bit_length(), b.bit_length())
seed = (b << s) | a
rng = random.Random(seed % _SEED_MAX)
elif isinstance(rng, random.Random):
rng = rng
else:
raise TypeError(type(rng))
return rng


def color_distance_squared(color1: tuple[int, int, int], color2: tuple[int, int, int]) -> float:
"""
Calculate the distance squared between two colors in RGB space, see https://www.compuphase.com/cmetric.htm
Args:
color1 (np.ndarray): first color in RGB
color2 (np.ndarray): second color in RGB
Returns:
float: distance between the two colors
"""

r1, g1, b1 = color1
r2, g2, b2 = color2

mean_r = (r1 + r2) / 2
delta_r = (r1 - r2) ** 2
delta_g = (g1 - g2) ** 2
delta_b = (b1 - b2) ** 2

distance = (2 + mean_r / 256) * delta_r + 4 * delta_g + (2 - (255 - mean_r) / 256) * delta_b

return distance


def distinct_grayscale(exclude_colors: list[int], n_attempts=1000, rng=None) -> int:
rng = _ensure_rng(rng)
if not exclude_colors:
return rng.randint(0, 255)

max_distance = None
best_color = None

# try black and white first
for color in [0, 255]:
if color not in exclude_colors:
distance_to_nearest = min([abs(color - c) for c in exclude_colors])
if max_distance is None or (distance_to_nearest > max_distance):
max_distance = distance_to_nearest
best_color = color

# try n_attempts randomly generated colors (or all if n_attempts > 256)
if n_attempts > 256:
number_generator = range(256)
else:
number_generator = rng.sample(range(256), n_attempts)
for color in number_generator:
if not exclude_colors:
return color

else:
distance_to_nearest = min([abs(color - c) for c in exclude_colors])

if (not max_distance) or (distance_to_nearest > max_distance):
max_distance = distance_to_nearest
best_color = color

assert best_color is not None, f"Failed to find a distinct color from {exclude_colors}"

return best_color


def get_random_color(rng=None) -> tuple[int, int, int]:
rng = _ensure_rng(rng)
return rng.randint(0, 255), rng.randint(0, 255), rng.randint(0, 255)


def distinct_color(exclude_colors: list[tuple[int, int, int]], n_attempts=1000, rng=None) -> tuple[int, int, int]:
rng = _ensure_rng(rng)

if not exclude_colors:
return get_random_color(rng=rng)

max_distance = None
best_color = None

# try pre-defined corners, edges, interior points first
for color in POINTS_OF_INTEREST:
if color not in exclude_colors:

distance_to_nearest = min([color_distance_squared(color, c) for c in exclude_colors])

if max_distance is None or (distance_to_nearest > max_distance):
max_distance = distance_to_nearest
best_color = color

# try n_attempts randomly generated colors
for _ in range(n_attempts):
color = get_random_color(rng=rng)

if not exclude_colors:
return color

else:

distance_to_nearest = min([color_distance_squared(color, c) for c in exclude_colors])

if (not max_distance) or (distance_to_nearest > max_distance):
max_distance = distance_to_nearest
best_color = color

assert best_color is not None, f"Failed to find a distinct color from {exclude_colors}"

return best_color


def n_distinct_colors(
n_colors: int,
exclude_colors=None,
return_excluded=False,
n_attempts=1000,
grayscale: bool = False,
rng=None,
) -> list[np.ndarray] | list[int]:
rng = _ensure_rng(rng)

if grayscale:
if exclude_colors is None:
exclude_colors = [0]
colors = exclude_colors.copy()
for i in range(n_colors):
colors.append(distinct_grayscale(colors, n_attempts=n_attempts, rng=rng))
else:
if exclude_colors is None:
exclude_colors = [(0, 0, 0), (255, 255, 255)]
colors = exclude_colors.copy()
for i in range(n_colors):
colors.append(distinct_color(colors, n_attempts=n_attempts, rng=rng))

if return_excluded:
return colors
else:
return colors[len(exclude_colors) :]


if __name__ == "__main__":
colors = n_distinct_colors(10, grayscale=True, rng=0)
print(colors)
colors = n_distinct_colors(10, grayscale=True, rng=0)
print(colors)
colors = n_distinct_colors(11, grayscale=True, rng=0)
print(colors)
colors = n_distinct_colors(14, grayscale=True, rng=0)
print(colors)
colors = n_distinct_colors(10, grayscale=True, rng=5)
print(colors)

colors = n_distinct_colors(10, grayscale=False, rng=0)
print(colors)
colors = n_distinct_colors(10, grayscale=False, rng=0)
print(colors)
colors = n_distinct_colors(11, grayscale=False, rng=0)
print(colors)
colors = n_distinct_colors(14, grayscale=False, rng=0)
print(colors)
colors = n_distinct_colors(10, grayscale=False, rng=5)
print(colors)

0 comments on commit 93161a6

Please sign in to comment.