From 0eacaff622c8300f8d56f6dd5d9f85f0c87154af Mon Sep 17 00:00:00 2001 From: Markus Fleischhacker Date: Thu, 30 May 2024 13:38:33 +0200 Subject: [PATCH] Add YOLO annotation out-of-bounds error tolerance. --- .../model/io/YOLOLoadStrategy.java | 12 +++++++ .../controller/AnnotationTests.java | 1 - .../controller/ControllerTests.java | 35 +++++++++++++++++++ .../austin-neill-685084-unsplash.txt | 2 ++ .../yolo/precision/object.data | 1 + 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/testannotations/yolo/precision/austin-neill-685084-unsplash.txt create mode 100644 src/test/resources/testannotations/yolo/precision/object.data diff --git a/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOLoadStrategy.java b/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOLoadStrategy.java index 4c317c9..e151f3d 100644 --- a/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOLoadStrategy.java +++ b/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOLoadStrategy.java @@ -189,15 +189,27 @@ private BoundingBoxData parseBoundingBoxData(String line, int lineNumber) { double heightRelative = parseRatio(scanner, lineNumber); double xMinRelative = xMidRelative - widthRelative / 2; + if(xMinRelative < 0 && -xMinRelative < 1e-6) { + xMinRelative = 0; + } assertRatio(xMinRelative, INVALID_BOUNDING_BOX_COORDINATES_MESSAGE + lineNumber + "."); double yMinRelative = yMidRelative - heightRelative / 2; + if(yMinRelative < 0 && -yMinRelative < 1e-6) { + yMinRelative = 0; + } assertRatio(yMinRelative, INVALID_BOUNDING_BOX_COORDINATES_MESSAGE + lineNumber + "."); double xMaxRelative = xMidRelative + widthRelative / 2; + if(xMaxRelative > 1 && xMaxRelative - 1 < 1e-6) { + xMaxRelative = 1; + } assertRatio(xMaxRelative, INVALID_BOUNDING_BOX_COORDINATES_MESSAGE + lineNumber + "."); double yMaxRelative = yMidRelative + heightRelative / 2; + if(yMaxRelative > 1 && yMaxRelative - 1 < 1e-6) { + yMaxRelative = 1; + } assertRatio(yMaxRelative, INVALID_BOUNDING_BOX_COORDINATES_MESSAGE + lineNumber + "."); String categoryName = categories.get(categoryId); diff --git a/src/test/java/com/github/mfl28/boundingboxeditor/controller/AnnotationTests.java b/src/test/java/com/github/mfl28/boundingboxeditor/controller/AnnotationTests.java index b20491f..fb363b7 100644 --- a/src/test/java/com/github/mfl28/boundingboxeditor/controller/AnnotationTests.java +++ b/src/test/java/com/github/mfl28/boundingboxeditor/controller/AnnotationTests.java @@ -36,7 +36,6 @@ import java.io.File; import java.util.List; -import java.util.Map; import static org.testfx.api.FxAssert.verifyThat; diff --git a/src/test/java/com/github/mfl28/boundingboxeditor/controller/ControllerTests.java b/src/test/java/com/github/mfl28/boundingboxeditor/controller/ControllerTests.java index a22fd33..29e53b5 100644 --- a/src/test/java/com/github/mfl28/boundingboxeditor/controller/ControllerTests.java +++ b/src/test/java/com/github/mfl28/boundingboxeditor/controller/ControllerTests.java @@ -1135,6 +1135,41 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS verifyThat(mainView.getObjectTree().getRoot().getChildren(), Matchers.hasSize(0), saveScreenshot(testinfo)); } + @Test + void onLoadAnnotation_YOLO_WhenAnnotationWithinYOLOPrecision_ShouldLoadBoundingBoxes(FxRobot robot, + TestInfo testinfo) { + + final String referenceAnnotationDirectoryPath = "/testannotations/yolo/precision"; + + waitUntilCurrentImageIsLoaded(testinfo); + WaitForAsyncUtils.waitForFxEvents(); + timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); + + verifyThat(mainView.getStatusBar().getCurrentEventMessage(), + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + + final File referenceAnnotationFolder = + new File(getClass().getResource(referenceAnnotationDirectoryPath).getFile()); + + // Load bounding-boxes defined in the reference annotation-file. + Platform.runLater(() -> controller + .initiateAnnotationImport(referenceAnnotationFolder, ImageAnnotationLoadStrategy.Type.YOLO)); + WaitForAsyncUtils.waitForFxEvents(); + + timeOutAssertServiceSucceeded(controller.getAnnotationImportService(), testinfo); + + final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); + Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, + () -> Objects.equals(counts.get("Test"), 2)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); + + verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(1), + saveScreenshot(testinfo)); + verifyThat(model.getObjectCategories(), Matchers.hasSize(1), saveScreenshot(testinfo)); + } + private void userChoosesNoOnAnnotationImportDialogSubtest(FxRobot robot, File annotationFile, TestInfo testinfo) { userChoosesToSaveExistingAnnotationsOnAnnotationImport(robot, annotationFile, testinfo); userChoosesNotToSaveExistingAnnotationsOnAnnotationImport(robot, annotationFile, testinfo); diff --git a/src/test/resources/testannotations/yolo/precision/austin-neill-685084-unsplash.txt b/src/test/resources/testannotations/yolo/precision/austin-neill-685084-unsplash.txt new file mode 100644 index 0000000..7e047f0 --- /dev/null +++ b/src/test/resources/testannotations/yolo/precision/austin-neill-685084-unsplash.txt @@ -0,0 +1,2 @@ +0 0.250000 0.250000 0.500001 0.500001 +0 0.750000 0.750000 0.500001 0.500001 diff --git a/src/test/resources/testannotations/yolo/precision/object.data b/src/test/resources/testannotations/yolo/precision/object.data new file mode 100644 index 0000000..8318c86 --- /dev/null +++ b/src/test/resources/testannotations/yolo/precision/object.data @@ -0,0 +1 @@ +Test \ No newline at end of file