From 73fbb9891cac3b28d9893e505416d5efdf27b4f7 Mon Sep 17 00:00:00 2001 From: Semyon Date: Mon, 11 Dec 2023 19:50:09 +0300 Subject: [PATCH 1/8] solution for another corner-case markup --- sahi/utils/coco.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/sahi/utils/coco.py b/sahi/utils/coco.py index 1777555a..27ab50a8 100644 --- a/sahi/utils/coco.py +++ b/sahi/utils/coco.py @@ -14,10 +14,9 @@ from typing import Dict, List, Optional, Set, Union import numpy as np -from shapely import MultiPolygon -from shapely.validation import make_valid from tqdm import tqdm - +from shapely.validation import make_valid +from shapely import MultiPolygon, GeometryCollection from sahi.utils.file import is_colab, load_json, save_json from sahi.utils.shapely import ShapelyAnnotation, box, get_shapely_multipolygon @@ -228,6 +227,8 @@ def get_sliced_coco_annotation(self, slice_bbox: List[int]): samp = self._shapely_annotation.multipolygon if not samp.is_valid: valid = make_valid(samp) + if isinstance(valid, GeometryCollection): + valid = valid.convex_hull if not isinstance(valid, MultiPolygon): valid = MultiPolygon([valid]) self._shapely_annotation.multipolygon = valid @@ -2371,6 +2372,7 @@ def export_coco_as_yolov5( output_dir: str, train_coco: Coco = None, val_coco: Coco = None, + test_coco: Coco = None, train_split_rate: float = 0.9, numpy_seed=0, disable_symlink=False, @@ -2425,10 +2427,10 @@ def export_coco_as_yolov5( # create train val image dirs train_dir = Path(os.path.abspath(output_dir)) / "train/" - train_dir.mkdir(parents=True, exist_ok=True) # create dir + train_dir.mkdir(parents=True, exist_ok=False) # create dir val_dir = Path(os.path.abspath(output_dir)) / "val/" - val_dir.mkdir(parents=True, exist_ok=True) # create dir - + val_dir.mkdir(parents=True, exist_ok=False) # create dir + # create image symlinks and annotation txts export_yolov5_images_and_txts_from_coco_object( output_dir=train_dir, @@ -2444,7 +2446,6 @@ def export_coco_as_yolov5( mp=False, disable_symlink=disable_symlink, ) - # create yolov5 data yaml data = { "train": str(train_dir).replace("\\", "/"), @@ -2452,9 +2453,22 @@ def export_coco_as_yolov5( "nc": len(train_coco.category_mapping), "names": list(train_coco.category_mapping.values()), } + + if test_coco: + test_dir = Path(os.path.abspath(output_dir)) / "test/" + test_dir.mkdir(parents=True, exist_ok=False) # create dir + export_yolov5_images_and_txts_from_coco_object( + output_dir=test_dir, + coco=test_coco, + ignore_negative_samples=test_coco.ignore_negative_samples, + mp=False, + disable_symlink=disable_symlink, + ) + data["test"] = str(test_dir).replace("\\", "/") + yaml_path = str(Path(output_dir) / "data.yml") with open(yaml_path, "w") as outfile: - yaml.dump(data, outfile, default_flow_style=False) + yaml.dump(data, outfile, default_flow_style=False, allow_unicode=True) return yaml_path From e291bbd9484b9993d95ffbf38e2f107fd0c16837 Mon Sep 17 00:00:00 2001 From: Semyon Date: Mon, 11 Dec 2023 19:53:21 +0300 Subject: [PATCH 2/8] Update coco.py --- sahi/utils/coco.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/sahi/utils/coco.py b/sahi/utils/coco.py index 27ab50a8..901e09b3 100644 --- a/sahi/utils/coco.py +++ b/sahi/utils/coco.py @@ -14,9 +14,10 @@ from typing import Dict, List, Optional, Set, Union import numpy as np -from tqdm import tqdm +from shapely import MultiPolygon from shapely.validation import make_valid -from shapely import MultiPolygon, GeometryCollection +from tqdm import tqdm + from sahi.utils.file import is_colab, load_json, save_json from sahi.utils.shapely import ShapelyAnnotation, box, get_shapely_multipolygon @@ -2372,7 +2373,6 @@ def export_coco_as_yolov5( output_dir: str, train_coco: Coco = None, val_coco: Coco = None, - test_coco: Coco = None, train_split_rate: float = 0.9, numpy_seed=0, disable_symlink=False, @@ -2427,10 +2427,10 @@ def export_coco_as_yolov5( # create train val image dirs train_dir = Path(os.path.abspath(output_dir)) / "train/" - train_dir.mkdir(parents=True, exist_ok=False) # create dir + train_dir.mkdir(parents=True, exist_ok=True) # create dir val_dir = Path(os.path.abspath(output_dir)) / "val/" - val_dir.mkdir(parents=True, exist_ok=False) # create dir - + val_dir.mkdir(parents=True, exist_ok=True) # create dir + # create image symlinks and annotation txts export_yolov5_images_and_txts_from_coco_object( output_dir=train_dir, @@ -2446,6 +2446,7 @@ def export_coco_as_yolov5( mp=False, disable_symlink=disable_symlink, ) + # create yolov5 data yaml data = { "train": str(train_dir).replace("\\", "/"), @@ -2453,22 +2454,9 @@ def export_coco_as_yolov5( "nc": len(train_coco.category_mapping), "names": list(train_coco.category_mapping.values()), } - - if test_coco: - test_dir = Path(os.path.abspath(output_dir)) / "test/" - test_dir.mkdir(parents=True, exist_ok=False) # create dir - export_yolov5_images_and_txts_from_coco_object( - output_dir=test_dir, - coco=test_coco, - ignore_negative_samples=test_coco.ignore_negative_samples, - mp=False, - disable_symlink=disable_symlink, - ) - data["test"] = str(test_dir).replace("\\", "/") - yaml_path = str(Path(output_dir) / "data.yml") with open(yaml_path, "w") as outfile: - yaml.dump(data, outfile, default_flow_style=False, allow_unicode=True) + yaml.dump(data, outfile, default_flow_style=False) return yaml_path From 6b0cd62334db1597098e8fd974ffa4ddbd151924 Mon Sep 17 00:00:00 2001 From: Semyon Date: Mon, 11 Dec 2023 20:22:50 +0300 Subject: [PATCH 3/8] Update coco.py --- sahi/utils/coco.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sahi/utils/coco.py b/sahi/utils/coco.py index 901e09b3..8eb4d4cf 100644 --- a/sahi/utils/coco.py +++ b/sahi/utils/coco.py @@ -14,7 +14,7 @@ from typing import Dict, List, Optional, Set, Union import numpy as np -from shapely import MultiPolygon +from shapely import GeometryCollection, MultiPolygon from shapely.validation import make_valid from tqdm import tqdm From be2b45959adc5f92f644d838fb60b39db17b837a Mon Sep 17 00:00:00 2001 From: Semyon Sergiev Date: Thu, 18 Jul 2024 18:59:20 +0300 Subject: [PATCH 4/8] utils: moved filter_polygons upwards: from coco.py to shapely.py --- sahi/utils/coco.py | 21 --------------------- sahi/utils/shapely.py | 26 +++++++++++++++++++++++--- tests/test_shapelyutils.py | 6 ++++++ 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/sahi/utils/coco.py b/sahi/utils/coco.py index 942561cb..cfc5c6de 100644 --- a/sahi/utils/coco.py +++ b/sahi/utils/coco.py @@ -224,28 +224,7 @@ def __init__( self._shapely_annotation = shapely_annotation def get_sliced_coco_annotation(self, slice_bbox: List[int]): - def filter_polygons(geometry): - """ - Filters out and returns only Polygon or MultiPolygon components of a geometry. - If geometry is a Polygon, it converts it into a MultiPolygon. - If it's a GeometryCollection, it filters to create a MultiPolygon from any Polygons in the collection. - Returns an empty MultiPolygon if no Polygon or MultiPolygon components are found. - """ - if isinstance(geometry, Polygon): - return MultiPolygon([geometry]) - elif isinstance(geometry, MultiPolygon): - return geometry - elif isinstance(geometry, GeometryCollection): - polygons = [geom for geom in geometry.geoms if isinstance(geom, Polygon)] - return MultiPolygon(polygons) if polygons else MultiPolygon() - return MultiPolygon() - shapely_polygon = box(slice_bbox[0], slice_bbox[1], slice_bbox[2], slice_bbox[3]) - samp = self._shapely_annotation.multipolygon - if not samp.is_valid: - valid = make_valid(samp) - valid = filter_polygons(valid) - self._shapely_annotation.multipolygon = valid intersection_shapely_annotation = self._shapely_annotation.get_intersection(shapely_polygon) return CocoAnnotation.from_shapely_annotation( intersection_shapely_annotation, diff --git a/sahi/utils/shapely.py b/sahi/utils/shapely.py index e579a085..14c33118 100644 --- a/sahi/utils/shapely.py +++ b/sahi/utils/shapely.py @@ -3,7 +3,8 @@ from typing import List -from shapely.geometry import CAP_STYLE, JOIN_STYLE, MultiPolygon, Polygon, box +from shapely.geometry import CAP_STYLE, JOIN_STYLE, GeometryCollection, MultiPolygon, Polygon, box +from shapely.validation import make_valid def get_shapely_box(x: int, y: int, width: int, height: int) -> Polygon: @@ -21,15 +22,34 @@ def get_shapely_box(x: int, y: int, width: int, height: int) -> Polygon: def get_shapely_multipolygon(coco_segmentation: List[List]) -> MultiPolygon: """ - Accepts coco style polygon coords and converts it to shapely multipolygon object + Accepts coco style polygon coords and converts it to valid shapely multipolygon object """ + def filter_polygons(geometry): + """ + Filters out and returns only Polygon or MultiPolygon components of a geometry. + If geometry is a Polygon, it converts it into a MultiPolygon. + If it's a GeometryCollection, it filters to create a MultiPolygon from any Polygons in the collection. + Returns an empty MultiPolygon if no Polygon or MultiPolygon components are found. + """ + if isinstance(geometry, Polygon): + return MultiPolygon([geometry]) + elif isinstance(geometry, MultiPolygon): + return geometry + elif isinstance(geometry, GeometryCollection): + polygons = [geom for geom in geometry.geoms if isinstance(geom, Polygon)] + return MultiPolygon(polygons) if polygons else MultiPolygon() + return MultiPolygon() + polygon_list = [] for coco_polygon in coco_segmentation: point_list = list(zip(coco_polygon[0::2], coco_polygon[1::2])) shapely_polygon = Polygon(point_list) polygon_list.append(shapely_polygon) shapely_multipolygon = MultiPolygon(polygon_list) - + + if not shapely_multipolygon.is_valid: + shapely_multipolygon = filter_polygons(make_valid(shapely_multipolygon)) + return shapely_multipolygon diff --git a/tests/test_shapelyutils.py b/tests/test_shapelyutils.py index 97240260..d2175c6f 100644 --- a/tests/test_shapelyutils.py +++ b/tests/test_shapelyutils.py @@ -26,6 +26,12 @@ def test_get_shapely_multipolygon(self): self.assertEqual(shapely_multipolygon.area, 41177.5) self.assertTupleEqual(shapely_multipolygon.bounds, (1, 1, 325, 200)) + def test_get_shapely_multipolygon_naughty(self): + # self-intersection case + coco_segmentation = [[3559.0, 2046.86, 3.49, 2060.0, 3540.9, 3249.7, 2060.0, 3239.61, 2052.87]] + shapely_multipolygon = get_shapely_multipolygon(coco_segmentation) + self.assertTrue(shapely_multipolygon.is_valid) + def test_shapely_annotation(self): # init shapely_annotation from coco segmentation segmentation = [[1, 1, 325, 125.2, 250, 200, 5, 200]] From 9b0205b24c9a959b13188979f8020b17e424cd18 Mon Sep 17 00:00:00 2001 From: Semyon Sergiev Date: Thu, 18 Jul 2024 19:46:59 +0300 Subject: [PATCH 5/8] formatting --- sahi/utils/shapely.py | 10 +++++++--- tests/test_shapelyutils.py | 6 +++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/sahi/utils/shapely.py b/sahi/utils/shapely.py index 14c33118..1310389a 100644 --- a/sahi/utils/shapely.py +++ b/sahi/utils/shapely.py @@ -20,10 +20,14 @@ def get_shapely_box(x: int, y: int, width: int, height: int) -> Polygon: return shapely_box + + + def get_shapely_multipolygon(coco_segmentation: List[List]) -> MultiPolygon: """ Accepts coco style polygon coords and converts it to valid shapely multipolygon object """ + def filter_polygons(geometry): """ Filters out and returns only Polygon or MultiPolygon components of a geometry. @@ -39,17 +43,17 @@ def filter_polygons(geometry): polygons = [geom for geom in geometry.geoms if isinstance(geom, Polygon)] return MultiPolygon(polygons) if polygons else MultiPolygon() return MultiPolygon() - + polygon_list = [] for coco_polygon in coco_segmentation: point_list = list(zip(coco_polygon[0::2], coco_polygon[1::2])) shapely_polygon = Polygon(point_list) polygon_list.append(shapely_polygon) shapely_multipolygon = MultiPolygon(polygon_list) - + if not shapely_multipolygon.is_valid: shapely_multipolygon = filter_polygons(make_valid(shapely_multipolygon)) - + return shapely_multipolygon diff --git a/tests/test_shapelyutils.py b/tests/test_shapelyutils.py index d2175c6f..a08fd4a4 100644 --- a/tests/test_shapelyutils.py +++ b/tests/test_shapelyutils.py @@ -26,11 +26,15 @@ def test_get_shapely_multipolygon(self): self.assertEqual(shapely_multipolygon.area, 41177.5) self.assertTupleEqual(shapely_multipolygon.bounds, (1, 1, 325, 200)) + def test_get_shapely_multipolygon_naughty(self): # self-intersection case - coco_segmentation = [[3559.0, 2046.86, 3.49, 2060.0, 3540.9, 3249.7, 2060.0, 3239.61, 2052.87]] + coco_segmentation = [ + [3559.0, 2046.86, 3.49, 2060.0, 3540.9, 3249.7, 2060.0, 3239.61, 2052.87] + ] shapely_multipolygon = get_shapely_multipolygon(coco_segmentation) self.assertTrue(shapely_multipolygon.is_valid) + def test_shapely_annotation(self): # init shapely_annotation from coco segmentation From 508b9f240f042d6e9ea610373a4fd45bc2e34de7 Mon Sep 17 00:00:00 2001 From: Semyon Sergiev Date: Thu, 18 Jul 2024 19:52:22 +0300 Subject: [PATCH 6/8] formatting again... --- sahi/utils/shapely.py | 3 ++- tests/test_shapelyutils.py | 25 ++++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/sahi/utils/shapely.py b/sahi/utils/shapely.py index 1310389a..6a9007fa 100644 --- a/sahi/utils/shapely.py +++ b/sahi/utils/shapely.py @@ -32,7 +32,8 @@ def filter_polygons(geometry): """ Filters out and returns only Polygon or MultiPolygon components of a geometry. If geometry is a Polygon, it converts it into a MultiPolygon. - If it's a GeometryCollection, it filters to create a MultiPolygon from any Polygons in the collection. + If it's a GeometryCollection, it filters + to create a MultiPolygon from any Polygons in the collection. Returns an empty MultiPolygon if no Polygon or MultiPolygon components are found. """ if isinstance(geometry, Polygon): diff --git a/tests/test_shapelyutils.py b/tests/test_shapelyutils.py index a08fd4a4..72627119 100644 --- a/tests/test_shapelyutils.py +++ b/tests/test_shapelyutils.py @@ -3,7 +3,12 @@ import unittest -from sahi.utils.shapely import MultiPolygon, ShapelyAnnotation, get_shapely_box, get_shapely_multipolygon +from sahi.utils.shapely import ( + MultiPolygon, + ShapelyAnnotation, + get_shapely_box, + get_shapely_multipolygon, +) class TestShapelyUtils(unittest.TestCase): @@ -11,7 +16,9 @@ def test_get_shapely_box(self): x, y, width, height = 1, 1, 256, 256 shapely_box = get_shapely_box(x, y, width, height) - self.assertListEqual(shapely_box.exterior.coords.xy[0].tolist(), [257.0, 257.0, 1.0, 1.0, 257.0]) + self.assertListEqual( + shapely_box.exterior.coords.xy[0].tolist(), [257.0, 257.0, 1.0, 1.0, 257.0] + ) self.assertEqual(shapely_box.area, 65536) self.assertTupleEqual(shapely_box.bounds, (1, 1, 257, 257)) @@ -26,7 +33,6 @@ def test_get_shapely_multipolygon(self): self.assertEqual(shapely_multipolygon.area, 41177.5) self.assertTupleEqual(shapely_multipolygon.bounds, (1, 1, 325, 200)) - def test_get_shapely_multipolygon_naughty(self): # self-intersection case coco_segmentation = [ @@ -35,7 +41,6 @@ def test_get_shapely_multipolygon_naughty(self): shapely_multipolygon = get_shapely_multipolygon(coco_segmentation) self.assertTrue(shapely_multipolygon.is_valid) - def test_shapely_annotation(self): # init shapely_annotation from coco segmentation segmentation = [[1, 1, 325, 125.2, 250, 200, 5, 200]] @@ -84,7 +89,9 @@ def test_shapely_annotation(self): # init shapely_annotation from coco bbox coco_bbox = [1, 1, 100, 100] - shapely_polygon = get_shapely_box(x=coco_bbox[0], y=coco_bbox[1], width=coco_bbox[2], height=coco_bbox[3]) + shapely_polygon = get_shapely_box( + x=coco_bbox[0], y=coco_bbox[1], width=coco_bbox[2], height=coco_bbox[3] + ) shapely_annotation = ShapelyAnnotation.from_coco_bbox(coco_bbox) # test conversion methods @@ -134,7 +141,9 @@ def test_get_intersection(self): coco_segmentation = [[1, 1, 325, 125, 250, 200, 5, 200]] shapely_annotation = ShapelyAnnotation.from_coco_segmentation(coco_segmentation) - intersection_shapely_annotation = shapely_annotation.get_intersection(shapely_box) + intersection_shapely_annotation = shapely_annotation.get_intersection( + shapely_box + ) test_list = intersection_shapely_annotation.to_list()[0] true_list = [(0, 0), (4, 199), (249, 199), (256, 192), (256, 97), (0, 0)] @@ -171,7 +180,9 @@ def test_get_empty_intersection(self): coco_segmentation = [[1, 1, 325, 125, 250, 200, 5, 200]] shapely_annotation = ShapelyAnnotation.from_coco_segmentation(coco_segmentation) - intersection_shapely_annotation = shapely_annotation.get_intersection(shapely_box) + intersection_shapely_annotation = shapely_annotation.get_intersection( + shapely_box + ) self.assertEqual(intersection_shapely_annotation.area, 0) From 9e7a8a96ebf3e33293b5ece1d318b80a727a5b04 Mon Sep 17 00:00:00 2001 From: Semyon Sergiev Date: Thu, 18 Jul 2024 19:55:28 +0300 Subject: [PATCH 7/8] format 3 --- sahi/utils/shapely.py | 22 ++++++++++++++-------- tests/test_shapelyutils.py | 7 +------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/sahi/utils/shapely.py b/sahi/utils/shapely.py index 6a9007fa..61083f87 100644 --- a/sahi/utils/shapely.py +++ b/sahi/utils/shapely.py @@ -20,9 +20,6 @@ def get_shapely_box(x: int, y: int, width: int, height: int) -> Polygon: return shapely_box - - - def get_shapely_multipolygon(coco_segmentation: List[List]) -> MultiPolygon: """ Accepts coco style polygon coords and converts it to valid shapely multipolygon object @@ -32,7 +29,7 @@ def filter_polygons(geometry): """ Filters out and returns only Polygon or MultiPolygon components of a geometry. If geometry is a Polygon, it converts it into a MultiPolygon. - If it's a GeometryCollection, it filters + If it's a GeometryCollection, it filters to create a MultiPolygon from any Polygons in the collection. Returns an empty MultiPolygon if no Polygon or MultiPolygon components are found. """ @@ -100,7 +97,9 @@ def from_coco_bbox(cls, bbox: List[int], slice_bbox: List[int] = None): slice_bbox (List[int]): [x_min, y_min, x_max, y_max] Is used to calculate sliced coco coordinates. """ - shapely_polygon = get_shapely_box(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3]) + shapely_polygon = get_shapely_box( + x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3] + ) shapely_multipolygon = MultiPolygon([shapely_polygon]) return cls(multipolygon=shapely_multipolygon, slice_bbox=slice_bbox) @@ -184,7 +183,9 @@ def to_coco_segmentation(self): if coco_polygon[:2] == coco_polygon[-2:]: del coco_polygon[-2:] # append coco_polygon to coco_segmentation - coco_polygon = [point for point in coco_polygon] if coco_polygon else coco_polygon + coco_polygon = ( + [point for point in coco_polygon] if coco_polygon else coco_polygon + ) coco_segmentation.append(coco_polygon) return coco_segmentation @@ -207,7 +208,10 @@ def to_opencv_contours(self): miny = self.slice_bbox[1] x_coords = [x_coord - minx for x_coord in x_coords] y_coords = [y_coord - miny for y_coord in y_coords] - opencv_contour = [[[int(x_coords[ind]), int(y_coords[ind])]] for ind in range(len(x_coords))] + opencv_contour = [ + [[int(x_coords[ind]), int(y_coords[ind])]] + for ind in range(len(x_coords)) + ] else: opencv_contour: List = [] # append opencv_contour to opencv_contours @@ -321,6 +325,8 @@ def get_intersection(self, polygon: Polygon): else: intersection_multipolygon = MultiPolygon([]) # create shapely annotation from intersection multipolygon - intersection_shapely_annotation = ShapelyAnnotation(intersection_multipolygon, slice_bbox) + intersection_shapely_annotation = ShapelyAnnotation( + intersection_multipolygon, slice_bbox + ) return intersection_shapely_annotation diff --git a/tests/test_shapelyutils.py b/tests/test_shapelyutils.py index 72627119..11ab53f9 100644 --- a/tests/test_shapelyutils.py +++ b/tests/test_shapelyutils.py @@ -3,12 +3,7 @@ import unittest -from sahi.utils.shapely import ( - MultiPolygon, - ShapelyAnnotation, - get_shapely_box, - get_shapely_multipolygon, -) +from sahi.utils.shapely import MultiPolygon, ShapelyAnnotation, get_shapely_box, get_shapely_multipolygon class TestShapelyUtils(unittest.TestCase): From 66a526a6fec87ace9deade7efaf274a3e3576a2b Mon Sep 17 00:00:00 2001 From: Semyon Sergiev Date: Tue, 6 Aug 2024 15:45:01 +0300 Subject: [PATCH 8/8] format 4: scripts.run_code_style --- sahi/models/detectron2.py | 6 +++--- sahi/utils/shapely.py | 17 ++++------------- tests/test_shapelyutils.py | 20 +++++--------------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/sahi/models/detectron2.py b/sahi/models/detectron2.py index 223a6d82..e8ce35d9 100644 --- a/sahi/models/detectron2.py +++ b/sahi/models/detectron2.py @@ -149,9 +149,9 @@ def _create_object_prediction_list_from_original_predictions( object_prediction_list = [ ObjectPrediction( bbox=box.tolist() if mask is None else None, - segmentation=get_coco_segmentation_from_bool_mask(mask.detach().cpu().numpy()) - if mask is not None - else None, + segmentation=( + get_coco_segmentation_from_bool_mask(mask.detach().cpu().numpy()) if mask is not None else None + ), category_id=category_id.item(), category_name=self.category_mapping[str(category_id.item())], shift_amount=shift_amount, diff --git a/sahi/utils/shapely.py b/sahi/utils/shapely.py index 61083f87..7380e9f7 100644 --- a/sahi/utils/shapely.py +++ b/sahi/utils/shapely.py @@ -97,9 +97,7 @@ def from_coco_bbox(cls, bbox: List[int], slice_bbox: List[int] = None): slice_bbox (List[int]): [x_min, y_min, x_max, y_max] Is used to calculate sliced coco coordinates. """ - shapely_polygon = get_shapely_box( - x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3] - ) + shapely_polygon = get_shapely_box(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3]) shapely_multipolygon = MultiPolygon([shapely_polygon]) return cls(multipolygon=shapely_multipolygon, slice_bbox=slice_bbox) @@ -183,9 +181,7 @@ def to_coco_segmentation(self): if coco_polygon[:2] == coco_polygon[-2:]: del coco_polygon[-2:] # append coco_polygon to coco_segmentation - coco_polygon = ( - [point for point in coco_polygon] if coco_polygon else coco_polygon - ) + coco_polygon = [point for point in coco_polygon] if coco_polygon else coco_polygon coco_segmentation.append(coco_polygon) return coco_segmentation @@ -208,10 +204,7 @@ def to_opencv_contours(self): miny = self.slice_bbox[1] x_coords = [x_coord - minx for x_coord in x_coords] y_coords = [y_coord - miny for y_coord in y_coords] - opencv_contour = [ - [[int(x_coords[ind]), int(y_coords[ind])]] - for ind in range(len(x_coords)) - ] + opencv_contour = [[[int(x_coords[ind]), int(y_coords[ind])]] for ind in range(len(x_coords))] else: opencv_contour: List = [] # append opencv_contour to opencv_contours @@ -325,8 +318,6 @@ def get_intersection(self, polygon: Polygon): else: intersection_multipolygon = MultiPolygon([]) # create shapely annotation from intersection multipolygon - intersection_shapely_annotation = ShapelyAnnotation( - intersection_multipolygon, slice_bbox - ) + intersection_shapely_annotation = ShapelyAnnotation(intersection_multipolygon, slice_bbox) return intersection_shapely_annotation diff --git a/tests/test_shapelyutils.py b/tests/test_shapelyutils.py index 11ab53f9..d3fa70a9 100644 --- a/tests/test_shapelyutils.py +++ b/tests/test_shapelyutils.py @@ -11,9 +11,7 @@ def test_get_shapely_box(self): x, y, width, height = 1, 1, 256, 256 shapely_box = get_shapely_box(x, y, width, height) - self.assertListEqual( - shapely_box.exterior.coords.xy[0].tolist(), [257.0, 257.0, 1.0, 1.0, 257.0] - ) + self.assertListEqual(shapely_box.exterior.coords.xy[0].tolist(), [257.0, 257.0, 1.0, 1.0, 257.0]) self.assertEqual(shapely_box.area, 65536) self.assertTupleEqual(shapely_box.bounds, (1, 1, 257, 257)) @@ -30,9 +28,7 @@ def test_get_shapely_multipolygon(self): def test_get_shapely_multipolygon_naughty(self): # self-intersection case - coco_segmentation = [ - [3559.0, 2046.86, 3.49, 2060.0, 3540.9, 3249.7, 2060.0, 3239.61, 2052.87] - ] + coco_segmentation = [[3559.0, 2046.86, 3.49, 2060.0, 3540.9, 3249.7, 2060.0, 3239.61, 2052.87]] shapely_multipolygon = get_shapely_multipolygon(coco_segmentation) self.assertTrue(shapely_multipolygon.is_valid) @@ -84,9 +80,7 @@ def test_shapely_annotation(self): # init shapely_annotation from coco bbox coco_bbox = [1, 1, 100, 100] - shapely_polygon = get_shapely_box( - x=coco_bbox[0], y=coco_bbox[1], width=coco_bbox[2], height=coco_bbox[3] - ) + shapely_polygon = get_shapely_box(x=coco_bbox[0], y=coco_bbox[1], width=coco_bbox[2], height=coco_bbox[3]) shapely_annotation = ShapelyAnnotation.from_coco_bbox(coco_bbox) # test conversion methods @@ -136,9 +130,7 @@ def test_get_intersection(self): coco_segmentation = [[1, 1, 325, 125, 250, 200, 5, 200]] shapely_annotation = ShapelyAnnotation.from_coco_segmentation(coco_segmentation) - intersection_shapely_annotation = shapely_annotation.get_intersection( - shapely_box - ) + intersection_shapely_annotation = shapely_annotation.get_intersection(shapely_box) test_list = intersection_shapely_annotation.to_list()[0] true_list = [(0, 0), (4, 199), (249, 199), (256, 192), (256, 97), (0, 0)] @@ -175,9 +167,7 @@ def test_get_empty_intersection(self): coco_segmentation = [[1, 1, 325, 125, 250, 200, 5, 200]] shapely_annotation = ShapelyAnnotation.from_coco_segmentation(coco_segmentation) - intersection_shapely_annotation = shapely_annotation.get_intersection( - shapely_box - ) + intersection_shapely_annotation = shapely_annotation.get_intersection(shapely_box) self.assertEqual(intersection_shapely_annotation.area, 0)