diff --git a/anylabeling/views/labeling/label_converter.py b/anylabeling/views/labeling/label_converter.py index 288a0f64..4c98a78c 100644 --- a/anylabeling/views/labeling/label_converter.py +++ b/anylabeling/views/labeling/label_converter.py @@ -835,7 +835,11 @@ def custom_to_yolo(self, is_empty_file = False elif mode == "obb" and shape_type == "rotation": label = shape["label"] - points = list(chain.from_iterable(shape["points"])) + points = shape["points"] + if not any(0 <= p[0] < image_width and 0 <= p[1] < image_height for p in points): + print(f"{data["imagePath"]}: Skip out of bounds coordinates of {points}!") + continue + points = list(chain.from_iterable(points)) normalized_coords = [ points[i] / image_width if i % 2 == 0 @@ -1078,11 +1082,15 @@ def custom_to_coco(self, input_path, output_path, mode): def custom_to_dota(self, input_file, output_file): with open(input_file, "r", encoding="utf-8") as f: data = json.load(f) + w, h = data["imageWidth"], data["imageHeight"] with open(output_file, "w", encoding="utf-8") as f: for shape in data["shapes"]: points = shape["points"] shape_type = shape["shape_type"] - if shape_type != "rotation" or len(points) != 4: + if (shape_type != "rotation" or len(points) != 4): + continue + if not any(0 <= p[0] < w and 0 <= p[1] < h for p in points): + print(f"{data["imagePath"]}: Skip out of bounds coordinates of {points}!") continue label = shape["label"] difficult = shape.get("difficult", False) diff --git a/anylabeling/views/labeling/widgets/canvas.py b/anylabeling/views/labeling/widgets/canvas.py index 282d4753..316b3f24 100644 --- a/anylabeling/views/labeling/widgets/canvas.py +++ b/anylabeling/views/labeling/widgets/canvas.py @@ -94,6 +94,7 @@ def __init__(self, *args, **kwargs): self.snapping = True self.h_shape_is_selected = False self.h_shape_is_hovered = None + self.allowed_oop_shape_types = ["rotation"] self._painter = QtGui.QPainter() self._cursor = CURSOR_DEFAULT # Menus: @@ -308,8 +309,8 @@ def mouseMoveEvent(self, ev): # noqa: C901 self.show_shape.emit(shape_width, shape_height, pos) color = QtGui.QColor(0, 0, 255) - if self.out_off_pixmap(pos): - # Don't allow the user to draw outside the pixmap. + if self.out_off_pixmap(pos) and self.create_mode not in self.allowed_oop_shape_types: + # Don't allow the user to draw outside the pixmap, except for rotation. # Project the point to the pixmap's edges. pos = self.intersection_point(self.current[-1], pos) elif ( @@ -576,6 +577,15 @@ def mousePressEvent(self, ev): self.set_hiding() self.drawing_polygon.emit(True) self.update() + elif (self.out_off_pixmap(pos) + and self.create_mode in self.allowed_oop_shape_types): + # Create new shape. + self.current = Shape(shape_type=self.create_mode) + self.current.add_point(pos) + self.line.points = [pos, pos] + self.set_hiding() + self.drawing_polygon.emit(True) + self.update() elif self.editing(): if self.selected_edge(): self.add_point_to_edge() @@ -800,7 +810,9 @@ def bounded_move_vertex(self, pos): """Move a vertex. Adjust position to be bounded by pixmap border""" index, shape = self.h_vertex, self.h_hape point = shape[index] - if self.out_off_pixmap(pos): + if (self.out_off_pixmap(pos) + and shape.shape_type + not in self.allowed_oop_shape_types): pos = self.intersection_point(point, pos) if shape.shape_type == "rotation": @@ -809,13 +821,13 @@ def bounded_move_vertex(self, pos): p2, p3, p4 = self.get_adjoint_points( shape.direction, shape[sindex], pos, index ) - if ( - self.out_off_pixmap(p2) - or self.out_off_pixmap(p3) - or self.out_off_pixmap(p4) - ): - # No need to move if one pixal out of map - return + # if ( + # self.out_off_pixmap(p2) + # or self.out_off_pixmap(p3) + # or self.out_off_pixmap(p4) + # ): + # # No need to move if one pixal out of map + # return # Move 4 pixal one by one shape.move_vertex_by(index, pos - point) lindex = (index + 1) % 4 @@ -843,17 +855,26 @@ def bounded_move_vertex(self, pos): def bounded_move_shapes(self, shapes, pos): """Move shapes. Adjust position to be bounded by pixmap border""" - if self.out_off_pixmap(pos): + shape_types = [] + for shape in shapes: + if shape.shape_type in self.allowed_oop_shape_types: + shape_types.append(shape.shape_type) + + if self.out_off_pixmap(pos) and len(shape_types) == 0: return False # No need to move - o1 = pos + self.offsets[0] - if self.out_off_pixmap(o1): - pos -= QtCore.QPoint(min(0, int(o1.x())), min(0, int(o1.y()))) - o2 = pos + self.offsets[1] - if self.out_off_pixmap(o2): - pos += QtCore.QPoint( - min(0, int(self.pixmap.width() - o2.x())), - min(0, int(self.pixmap.height() - o2.y())), - ) + if len(shape_types) > 0 and len(shapes) != len(shape_types): + return False + + if len(shape_types) == 0: + o1 = pos + self.offsets[0] + if self.out_off_pixmap(o1): + pos -= QtCore.QPoint(min(0, int(o1.x())), min(0, int(o1.y()))) + o2 = pos + self.offsets[1] + if self.out_off_pixmap(o2): + pos += QtCore.QPoint( + min(0, int(self.pixmap.width() - o2.x())), + min(0, int(self.pixmap.height() - o2.y())), + ) # XXX: The next line tracks the new position of the cursor # relative to the shape, but also results in making it # a bit "shaky" when nearing the border and allows it to