import numpy as np
from typing import Tuple
def find_baseline(mask: np.ndarray) -> Tuple[
Tuple[int, int], Tuple[int, int]]:
"""Funkcja wyznacza punkty baseline na podstawie carti i bony roof.
Algorytm znajduje najbardziej wysunięty na prawo punkt carti roof i na
jego współrzędnej x +1 definiuje początek prawego punktu. Współrzędna y
prawego punktu to współrzędna y bony roof na górnym obrysie na podanej
współrzędnej x. Jeżeli dochodzi do wypadku gdzie carti roof jest dłuższe
w poziomie od bony roof, to jako prawy punkt ustalany jest najbardziej
wysunięty punkt górnego obrysu bony roof. Następnie szukana jest linia,
posiadająca największe pokrycie z górnym obrysem bony roof. Do obliczania
pokrycia pomijany jest górny obrys znajdujący się po prawo od wyznaczonego
prawego punktu. Jako lewy punkt funkcja obiera ostatni punkt linii
z największym pokryciem znajdujący się na górnym obrysie.
Args:
mask (np.ndarray): maska w formacie png
Returns:
Tuple[Tuple[int, int], Tuple[int, int]]: współrzędne prawego i lewego
punktu baseline
"""
height, width = mask.shape
label_upper_contour_bony = 5 # bony roof
label_upper_contour_carti = 4 # cartilagineous roof
binary_mask_bony = (mask == label_upper_contour_bony).astype(np.uint8)
binary_mask_carti = (mask == label_upper_contour_carti).astype(np.uint8)
upper_contour_bony = [
(x, np.where(binary_mask_bony[:, x])[0][0]) for x in range(width)
if np.any(binary_mask_bony[:, x])
]
x_max_carti = max((x for x in range(width) if np.any(
binary_mask_carti[:, x])), default=-1
)
if x_max_carti == -1:
return (None, None), (None, None)
# Ustalanie prawego punktu na podstawie przesunięcia
rightmost_point_x = x_max_carti + 1
if rightmost_point_x >= width or not upper_contour_bony:
return (None, None), (None, None)
bony_point_y = next(
(y for x, y in upper_contour_bony if x == rightmost_point_x), None
)
if bony_point_y is None:
rightmost_point_x, bony_point_y = upper_contour_bony[-1]
# Wstępny punkt prawy przed przesunięciem
original_rightmost_point = (rightmost_point_x, bony_point_y)
best_line = []
max_overlap = 0
best_shift = 0 # Dodanie zmiennej do śledzenia najlepszego przesunięcia
# Przechodzi przez każde przesunięcie od -10 do +5 pikseli
for y0 in range(bony_point_y - 10, bony_point_y + 5):
if not (0 <= y0 < height):
continue
current_shift = y0 - bony_point_y
for angle in range(360):
angle_rad = np.radians(angle)
sin_angle = np.sin(angle_rad)
cos_angle = np.cos(angle_rad)
n = np.arange(2 * (width + height))
x = rightmost_point_x + n * cos_angle
y = y0 - n * sin_angle
x_rounded = np.round(x).astype(int)
y_rounded = np.round(y).astype(int)
valid_mask = (x_rounded >= 0) & (x_rounded < width) & \
(y_rounded >= 0) & (y_rounded < height)
line_points = list(
zip(x_rounded[valid_mask], y_rounded[valid_mask])
)
overlap = len(
set(line_points) &
set(p for p in upper_contour_bony if p[0] <= rightmost_point_x)
)
if overlap > max_overlap:
max_overlap = overlap
best_line = line_points
best_shift = current_shift
adjusted_rightmost_point = (
original_rightmost_point[0], original_rightmost_point[1] + best_shift
)
leftmost_point = None
if best_line:
for point in best_line:
x, y = point
if mask[y, x] == label_upper_contour_bony:
if leftmost_point is None or x < leftmost_point[0]:
leftmost_point = (x, y)
if leftmost_point is None:
leftmost_point = (None, None)
return adjusted_rightmost_point, leftmost_point