1
0

Added pyrate as a direct dependency.

This commit is contained in:
2022-07-11 23:07:33 +02:00
parent 8c4532dad4
commit c99d517f6f
230 changed files with 21114 additions and 0 deletions

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,61 @@
"""This test suite runs additional tests for ``ImageLine`` that are not covered in ``TestObstacleLocator``"""
# Testing
from unittest import TestCase
# Hypothesis
from hypothesis import given
from hypothesis.strategies import composite
from hypothesis.strategies import floats
from hypothesis.strategies import integers
from hypothesis.strategies import just
from hypothesis.strategies import tuples
# Scientific
from numpy import pi
# Module under test
from pyrate.sense.vision.image_line import ImageLine
@composite
def image_dimensions_and_points(draw):
"""Generate image dimensions and points left and right on that image"""
image_dims = draw(tuples(integers(1, 10000), integers(1, 10000)))
point_a = draw(tuples(just(0), integers(0, image_dims[1] - 1)))
point_b = draw(tuples(just(image_dims[0] - 1), integers(0, image_dims[1] - 1)))
return image_dims, point_a, point_b
class TestImageLine(TestCase):
"""Tests the remaining methods of ``ImageLine`` not covered by testing ``ObstacleLocator``"""
@given(floats(1, 10000), floats(1, 10000), floats(-5000, 5000), floats(0, 2 * pi))
def test_from_height_angle(self, image_width, image_height, height, angle):
"""Test that creates (from height and angle) and tests ``ImageLine``s"""
image_line = ImageLine.from_height_angle((image_width, image_height), height, angle)
self.assertTrue(image_line.image_width == image_width and image_line.image_height == image_height)
self.assertTrue(image_line.angle == angle)
self.assertAlmostEqual(image_line.height, int(height + image_height / 2))
end_points = image_line.end_points
self.assertTrue(
end_points[0][0] == 0 and end_points[1][0] == image_width,
msg=f"x1={end_points[0][0]} x2={end_points[1][0]}",
)
@given(test_input=image_dimensions_and_points())
def test_indices(self, test_input):
"""Test that tests the ``indices`` property of ``ImageLine``"""
image_dims, point1, point2 = test_input
image_line = ImageLine.from_points(image_dims, (point1, point2))
x_coords, y_coords = image_line.indices
self.assertTrue(((50 <= x_coords) <= 50).all())
self.assertTrue(((0 <= y_coords) <= 200).all())

View File

@ -0,0 +1,102 @@
"""This test suite evaluates and tests behavior of the ``ImageRectangle`` class"""
# Testing
from unittest import TestCase
# Hypothesis
from hypothesis import given
from hypothesis.strategies import integers
# Module under test
from pyrate.sense.vision.image_rectangle import ImageRectangle
class TestImageRectangle(TestCase):
"""Tests functionality of the ``ImageRectangle`` class"""
@given(
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
)
# pylint: disable=too-many-arguments
def test_bottom_center(self, position_x, position_y, width, height, offset_x, offset_y):
"""Parametrized test that tests correct functionality of the bottom_center property
Args:
position_x: x position of the rectangle
position_y: y position of the rectangle
width: width of the rectangle
height: height of the rectangle
offset_x: x component of the offset
offset_y: y component of the offset
"""
offset = (offset_x, offset_y)
rectangle_without_offset = ImageRectangle((position_x, position_y, width, height))
self.assertTupleEqual(rectangle_without_offset.offset, (0, 0))
self.assertAlmostEqual(rectangle_without_offset.bottom_center[0], position_x + (width / 2), delta=0.5)
self.assertAlmostEqual(rectangle_without_offset.bottom_center[1], position_y + height)
rectangle_with_offset = ImageRectangle((position_x, position_y, width, height), offset=offset)
self.assertTupleEqual(rectangle_with_offset.offset, offset)
self.assertAlmostEqual(
rectangle_with_offset.bottom_center[0], position_x + offset_x + (width / 2), delta=0.5
)
self.assertAlmostEqual(rectangle_with_offset.bottom_center[1], position_y + offset_y + height)
@given(
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
integers(0, 10000),
)
# pylint: disable=too-many-arguments
def test_rectangle_to_corner(self, position_x, position_y, width, height, offset_x, offset_y):
"""Parametrized test that tests correct functionality of the rectangle_to_corner method
Args:
position_x: x position of the rectangle
position_y: y position of the rectangle
width: width of the rectangle
height: height of the rectangle
offset_x: x component of the offset
offset_y: y component of the offset
"""
offset = (offset_x, offset_y)
# rectangle without offset
rectangle_without_offset = ImageRectangle((position_x, position_y, width, height))
self.assertTupleEqual(rectangle_without_offset.offset, (0, 0))
cornerlu, cornerrb = rectangle_without_offset.rectangle_to_corner(offset=False)
self.assertTrue(
cornerlu[0] == position_x and cornerlu[1] == position_y,
msg=f"Left upper corner: {cornerlu}",
)
self.assertTrue(
cornerrb[0] == position_x + width and cornerrb[1] == position_y + height,
msg=f"Right bottom corner: {cornerrb}",
)
# rectangle with offset
rectangle_with_offset = ImageRectangle((position_x, position_y, width, height), offset)
self.assertTupleEqual(rectangle_with_offset.offset, offset)
cornerlu, cornerrb = rectangle_with_offset.rectangle_to_corner(offset=True)
self.assertTrue(
cornerlu[0] == position_x + offset_x and cornerlu[1] == position_y + offset_y,
msg=f"Left upper corner: {cornerlu}",
)
self.assertTrue(
cornerrb[0] == position_x + offset_x + width and cornerrb[1] == position_y + offset_y + height,
msg=f"Right bottom corner: {cornerrb}",
)

View File

@ -0,0 +1,103 @@
"""This test suite evaluates and tests behavior of the ``ObstacleLocator`` class"""
# Standard library
from csv import reader
from math import radians
from pathlib import Path
# Typing
from typing import cast
from typing import Tuple
# Testing
from unittest import TestCase
# Scientific
from cv2 import imread
# Module under test
from pyrate.sense.vision.image_line import ImageLine
from pyrate.sense.vision.obstacle_locator import ObstacleLocator
PATH_TO_DATASET = Path(__file__).parent / "resources" / "testing_dataset_successful"
DATASET_IMAGES_PATHS = sorted(list((PATH_TO_DATASET / "testims").glob("*.jpg")))
DATASET_ANNOTATIONS_PATHS = sorted(list((PATH_TO_DATASET / "annotations").glob("*.txt")))
PATH_TO_FAILING = (
Path(__file__).parent / "resources" / "testing_dataset_no_horizon" / "testims" / "Preprocessed_test_0.jpg"
)
IMAGE_HEIGHT, IMAGE_WIDTH = imread(PATH_TO_FAILING.as_posix()).shape[:2]
class TestObstacleLocator(TestCase):
"""Test for correct predictions made by ``ObstacleLocator``"""
@staticmethod
def parse_annotation(file_path: str, obstacle_locator: ObstacleLocator) -> Tuple[ImageLine, float]:
"""Helper function to parse the ground truth labels from the dataset.
Args:
file_path: Label file path
obstacle_locator: the ObstacleLocator that returns the ImageLine that should be
compared to the returned ImageLine of this function
Returns:
ImageLine as described in the annotation, angle read from annotation
(for testing correct angle calculation)
"""
with open(file_path, "rt", encoding="UTF-8") as label_file:
content = label_file.read().split("\n")
csvreader = reader(content, delimiter=",")
point_a = cast(Tuple[int, int], tuple(int(x) for x in next(csvreader)))
point_b = cast(Tuple[int, int], tuple(int(x) for x in next(csvreader)))
label_angle = radians(float(next(csvreader)[0]))
line = ImageLine.from_points(
image_shape=(obstacle_locator.image_width, obstacle_locator.image_height),
points=(point_a, point_b),
)
return line, label_angle
def test_horizon_angle(self):
"""Compares ``ObstacleLocator`` horizon estimates to ground truth annotations"""
uut_ol = ObstacleLocator(image_width=IMAGE_WIDTH, image_height=IMAGE_HEIGHT) # unit/module under test
for image_path, label_path in zip(DATASET_IMAGES_PATHS, DATASET_ANNOTATIONS_PATHS):
with self.subTest(image=image_path.name):
# Assert that we have the correct label for the test image
self.assertEqual(
image_path.name.split(".")[0],
label_path.name.split(".")[0],
msg="That isn't the right label for the image. This shouldn't happen.",
)
image = imread(image_path.as_posix())
# read annotation and test if ImageLine calculates the line's angle correctly
label_image_line, label_angle = self.parse_annotation(label_path.as_posix(), uut_ol)
self.assertAlmostEqual(label_angle, label_image_line.angle, places=2)
result = uut_ol.detect_horizon(image)
horizons = result[0]
# Test that a) a horizon is detected and b) it has the correct angle
self.assertTrue(len(horizons) > 0, msg="No horizon was detected.")
self.assertAlmostEqual(
horizons[0].angle, label_image_line.angle, places=1, msg="Horizon angle mismatch."
)
def test_missing_lines(self):
"""Tests the branch when no horizon line is detected in the image"""
uut_ol = ObstacleLocator(image_width=IMAGE_WIDTH, image_height=IMAGE_HEIGHT) # unit/module under test
image = imread(PATH_TO_FAILING.as_posix())
# ObstacleLocator does not find a horizon line in this image
result = uut_ol.detect_horizon(image)
self.assertFalse(result[0])