Spaces:
Runtime error
Runtime error
#!/usr/bin/env python3 | |
""" | |
Copyright (c) 2020, Carleton University Biomedical Informatics Collaboratory | |
This source code is licensed under the MIT license found in the | |
LICENSE file in the root directory of this source tree. | |
""" | |
from typing import List | |
import numpy as np | |
from digitizer.report_components.line import Line | |
def compute_deviation_sum(angle: float, lines: List[Line]) -> float: | |
"""Given a candidate angle and a list of lines, computes the sum of the | |
deviation of these lines from the horizontal or vertical axis. | |
Parameters | |
---------- | |
angle : float | |
The candidate angle in degrees. | |
lines : List[Line] | |
All the lines used in computing the sum of deviation. | |
Returns | |
------- | |
float | |
The sum of the deviations from all lines with the vertical or horizontal | |
axis. | |
""" | |
residual_angle_sum = 0 | |
for line in lines: | |
if line.get_angle() > -45 and line.get_angle() < 45: | |
residual = abs((line.get_angle() - angle)) | |
residual_angle_sum += residual | |
else: | |
residual = 90 - abs(line.get_angle() - angle) | |
residual_angle_sum += residual | |
return abs(residual_angle_sum) | |
def compute_rotation_angle(perpendicular_lines: List[Line]) -> float: | |
"""Given a list of lines, returns the angle that must be applied to | |
the image so that lines that all lines that intersect another line | |
at roughly a right angle (+/- some tolerance) as close to vertical | |
or horizontal as possible. | |
Parameters | |
---------- | |
perpendicular_lines : List[Line} | |
The lines extracted from the image. These lines are expected to come | |
from an isolated audiogram grid. | |
tolerance : float | |
Two lines intersecting at 90 +/- `tolerance` degrees are considered | |
perpendicular. | |
Returns | |
------- | |
float | |
The correction angle that must be applied to the image so as to | |
make perpendicular lines as close as possible to horizontal or vertical. | |
""" | |
# Find the angle that minimizes the sum of distances to the nearest axis | |
#angle. | |
angle_range = np.arange(-44, 44, step=0.5) | |
errors = [ | |
compute_deviation_sum(angle, perpendicular_lines) | |
for angle in angle_range | |
] | |
correction_angle = angle_range[np.argmin(errors)] | |
return correction_angle | |
def apply_rotation(point: dict, rotation_angle: float) -> dict: | |
new_x = (np.cos(rotation_angle) * point["x"] | |
- np.sin(rotation_angle) * -point["y"]) | |
new_y = (np.sin(rotation_angle) * point["x"] | |
+ np.cos(rotation_angle) * -point["y"]) | |
return { **point, "x": new_x, "y": -new_y } | |
def get_bounding_box_relative_to_original_report(bounding_box, audiogram_coordinates, correction_angle): | |
absolute_corrected_coordinates = { | |
"x": bounding_box["x"] + audiogram_coordinates["x"], | |
"y": bounding_box["y"] + audiogram_coordinates["y"] | |
} | |
raw_coordinates = apply_rotation(absolute_corrected_coordinates, -correction_angle) | |
correction_angle_rad = np.radians(correction_angle) | |
side_length = bounding_box["width"] * np.sin(correction_angle_rad) + bounding_box["width"] * np.cos(correction_angle_rad) | |
if correction_angle_rad <= 0: | |
return { | |
"x": absolute_corrected_coordinates["x"] - bounding_box["width"] * np.sin(correction_angle_rad), | |
"y": absolute_corrected_coordinates["y"], | |
"width": side_length, | |
"height": side_length | |
} | |
else: | |
return { | |
"x": absolute_corrected_coordinates["x"], | |
"y": absolute_corrected_coordinates["y"] - bounding_box["height"] * np.sin(correction_angle_rad), | |
"width": side_length, | |
"height": side_length | |
} | |