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 PIL.ImageDraw | |
import numpy as np | |
class Line(object): | |
def __init__(self, x1, y1, x2, y2, color="rgb(255,0,0)", label=None): | |
self.p1 = { "x": x1, "y": y1 } | |
self.p2 = { "x": x2, "y": y2 } | |
self.color = color | |
self.label = label | |
def get_angle(self) -> float: | |
"""Returns the angle of the line in degrees in the range [0, 180[. | |
Returns | |
------- | |
float | |
The angle of the line in degrees. | |
""" | |
dx = self.p2["x"] - self.p1["x"] | |
dy = self.p2["y"] - self.p1["y"] | |
angle = np.degrees(np.arctan2(abs(dy), abs(dx))) | |
if self.p2["y"] - self.p1["y"] < 0: | |
angle = 180 - angle | |
return angle | |
def has_a_perpendicular_line(self, lines: List["Line"], tolerance: float = 1) -> bool: | |
"""Checks if a line has at least one perpendicular line among a list | |
of lines. | |
Parameters | |
---------- | |
lines : List[Line] | |
A list of lines. | |
tolerance : float | |
A difference of `tolerance` from a 90 degrees angle between the two lines | |
is still considered perpendicular. | |
Returns | |
------- | |
bool | |
`True` if the line has a perpendicular line in the list, `False` otherwise. | |
""" | |
assert tolerance >= 0 | |
line_angle1 = self.get_angle() | |
for other_line in lines: | |
line_angle2 = other_line.get_angle() | |
angle = abs(line_angle1 - line_angle2) | |
if abs(angle - 90) < tolerance: | |
return True | |
return False | |
def get_x(self) -> int: | |
"""Return the middle x pixel coordinate of a vertical line. | |
Only applicable to vertical lines. | |
Returns | |
------- | |
int | |
The middle x coordinate of a vertical line. | |
""" | |
assert self.is_vertical() | |
return int((self.p1["x"] + self.p2["x"]) / 2) | |
def get_y(self) -> int: | |
"""Return the middle y pixel coordinate of a horizontal line. | |
Only applicable to horizontal lines. | |
Returns | |
------- | |
int | |
The middle y coordinate of a horizontal line. | |
""" | |
assert self.is_horizontal() | |
return (self.p1["y"] + self.p2["y"]) / 2 | |
def is_vertical(self, tolerance: float = 1) -> bool: | |
"""Returns true if the line is vertical. | |
Parameters | |
---------- | |
tolerance : float | |
A deviation of `tolerance` degrees from the vertical line is still | |
considered vertical. | |
Returns | |
------- | |
bool | |
True if the line is vertical, False otherwise. | |
""" | |
assert tolerance >= 0 | |
angle = self.get_angle() | |
return angle <= (-90 + tolerance) or angle >= (90 - tolerance) | |
def is_horizontal(self, tolerance: float = 1) -> bool: | |
"""Returns true if the line is horizontal. | |
Parameters | |
---------- | |
tolerance : float | |
A deviation of `tolerance` degrees from the horizontal line is still | |
considered horizontal. | |
Returns | |
------- | |
bool | |
True if the line is horiontal, False otherwise. | |
""" | |
assert tolerance >= 0 | |
angle = self.get_angle() | |
return angle >= -tolerance and angle <= tolerance | |
def crosses_label(self, labels: List["Label"]) -> bool: | |
"""Checks if the line intersects one of the labels passed. | |
Parameters | |
---------- | |
labels : List[Label] | |
A list of labels that the line could possibly cross. | |
Returns | |
------- | |
bool | |
True if the line crosses at least one Label among the ones provided, | |
False otherwise. | |
""" | |
for label in labels: | |
if self.is_vertical(): | |
x = (self.p1["x"] + self.p2["x"]) / 2 | |
if x >= label.p1["x"] and x <= label.p2["x"]: | |
return True | |
elif self.is_horizontal(): | |
y = (self.p1["y"] + self.p2["y"]) / 2 | |
if y >= label.p1["y"] and y <= label.p2["y"]: | |
return True | |
return False | |
def draw(self, canvas: PIL.ImageDraw): | |
"""Draws the line on the canvas (image) passed. | |
Parameters | |
---------- | |
canvas : PIL.ImageDraw | |
The PIL.ImageDraw on which the line is to be displayed. | |
""" | |
canvas.line([ | |
(self.p1["x"], self.p1["y"]), | |
(self.p2["x"], self.p2["y"]) | |
], width=3, fill=self.color) | |
if self.label: | |
canvas.text((self.p1["x"] + 5, self.p1["y"]), str(self.label), fill=self.color) | |