From 3cc364b72c30fc83add029dc86f400e08a0a8aee Mon Sep 17 00:00:00 2001 From: David Fleischhacker Date: Sun, 14 Jul 2024 11:59:22 +0200 Subject: [PATCH] Support for yolo instance-segmentation polygon format (#119) * added support for yolo instance-segmentation polygon format * added support for yolo instance-segmentation polygon format * Update yolo read-write logic, update tests. --------- Co-authored-by: Markus Fleischhacker --- .../model/io/YOLOLoadStrategy.java | 165 ++-- .../model/io/YOLOSaveStrategy.java | 76 +- .../controller/ControllerTests.java | 751 +++++++++--------- .../austin-neill-685084-unsplash.txt | 3 +- .../yolo/reference/object.data | 3 +- 5 files changed, 512 insertions(+), 486 deletions(-) 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 e151f3d..2d4b748 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 @@ -38,8 +38,9 @@ import java.util.stream.Stream; /** - * Loads rectangular bounding-box annotations in the YOLO-format described at - * ... + * Loads rectangular bounding-box annotations and instance-segmentation annotations in the YOLO-format described at + * ... and + * ... */ public class YOLOLoadStrategy implements ImageAnnotationLoadStrategy { public static final String INVALID_BOUNDING_BOX_COORDINATES_MESSAGE = "Invalid bounding-box coordinates on line "; @@ -66,18 +67,18 @@ public ImageAnnotationImportResult load(Path path, Set filesToLoad, try { loadObjectCategories(path); - } catch(Exception e) { + } catch (Exception e) { unParsedFileErrorMessages.add(new IOErrorInfoEntry(OBJECT_DATA_FILE_NAME, e.getMessage())); return new ImageAnnotationImportResult(0, unParsedFileErrorMessages, ImageAnnotationData.empty()); } - if(categories.isEmpty()) { + if (categories.isEmpty()) { unParsedFileErrorMessages .add(new IOErrorInfoEntry(OBJECT_DATA_FILE_NAME, "Does not contain any category names.")); return new ImageAnnotationImportResult(0, unParsedFileErrorMessages, ImageAnnotationData.empty()); } - try(Stream fileStream = Files.walk(path, INCLUDE_SUBDIRECTORIES ? Integer.MAX_VALUE : 1)) { + try (Stream fileStream = Files.walk(path, INCLUDE_SUBDIRECTORIES ? Integer.MAX_VALUE : 1)) { List annotationFiles = fileStream .filter(pathItem -> pathItem.getFileName().toString().endsWith(".txt")) .map(Path::toFile).toList(); @@ -86,25 +87,25 @@ public ImageAnnotationImportResult load(Path path, Set filesToLoad, AtomicInteger nrProcessedFiles = new AtomicInteger(0); List imageAnnotations = annotationFiles.parallelStream() - .map(file -> { - progress.set(1.0 * nrProcessedFiles - .incrementAndGet() / totalNrOfFiles); - - try { - return loadAnnotationFromFile(file); - } catch(InvalidAnnotationFormatException | - AnnotationToNonExistentImageException | - AnnotationAssociationException | - IOException e) { - unParsedFileErrorMessages - .add(new IOErrorInfoEntry( - file.getName(), - e.getMessage())); - return null; - } - }) - .filter(Objects::nonNull) - .toList(); + .map(file -> { + progress.set(1.0 * nrProcessedFiles + .incrementAndGet() / totalNrOfFiles); + + try { + return loadAnnotationFromFile(file); + } catch (InvalidAnnotationFormatException | + AnnotationToNonExistentImageException | + AnnotationAssociationException | + IOException e) { + unParsedFileErrorMessages + .add(new IOErrorInfoEntry( + file.getName(), + e.getMessage())); + return null; + } + }) + .filter(Objects::nonNull) + .toList(); return new ImageAnnotationImportResult( imageAnnotations.size(), @@ -115,18 +116,18 @@ public ImageAnnotationImportResult load(Path path, Set filesToLoad, } private void loadObjectCategories(Path root) throws IOException { - if(!root.resolve(OBJECT_DATA_FILE_NAME).toFile().exists()) { + if (!root.resolve(OBJECT_DATA_FILE_NAME).toFile().exists()) { throw new InvalidAnnotationFormatException( "Does not exist in annotation folder \"" + root.getFileName().toString() + "\"."); } - try(BufferedReader fileReader = Files.newBufferedReader(root.resolve(OBJECT_DATA_FILE_NAME))) { + try (BufferedReader fileReader = Files.newBufferedReader(root.resolve(OBJECT_DATA_FILE_NAME))) { String line; - while((line = fileReader.readLine()) != null) { + while ((line = fileReader.readLine()) != null) { line = line.strip(); - if(!line.isBlank()) { + if (!line.isBlank()) { categories.add(line); } } @@ -137,30 +138,32 @@ private ImageAnnotation loadAnnotationFromFile(File file) throws IOException { final List annotatedImageFiles = baseFileNameToImageFileMap.get( FilenameUtils.getBaseName(file.getName())); - if(annotatedImageFiles == null) { + if (annotatedImageFiles == null) { throw new AnnotationToNonExistentImageException( "No associated image file."); - } else if(annotatedImageFiles.size() > 1) { + } else if (annotatedImageFiles.size() > 1) { throw new AnnotationAssociationException( "More than one associated image file."); } - final String annotatedImageFileName = annotatedImageFiles.get(0); + final String annotatedImageFileName = annotatedImageFiles.getFirst(); - try(BufferedReader fileReader = Files.newBufferedReader(file.toPath())) { + try (BufferedReader fileReader = Files.newBufferedReader(file.toPath())) { String line; List boundingShapeDataList = new ArrayList<>(); int counter = 1; - while((line = fileReader.readLine()) != null) { + while ((line = fileReader.readLine()) != null) { line = line.strip(); - if(!line.isBlank()) { + if (!line.isBlank()) { try { - boundingShapeDataList.add(parseBoundingBoxData(line, counter)); - } catch(InvalidAnnotationFormatException e) { + final BoundingShapeData boundingShapeData = parseBoundingShapeData(line, counter); + boundingShapeDataList.add(boundingShapeData); + boundingShapeCountPerCategory.merge(boundingShapeData.getCategoryName(), 1, Integer::sum); + } catch (InvalidAnnotationFormatException e) { unParsedFileErrorMessages.add(new IOErrorInfoEntry(file.getName(), e.getMessage())); } } @@ -168,7 +171,7 @@ private ImageAnnotation loadAnnotationFromFile(File file) throws IOException { ++counter; } - if(boundingShapeDataList.isEmpty()) { + if (boundingShapeDataList.isEmpty()) { return null; } @@ -177,95 +180,103 @@ private ImageAnnotation loadAnnotationFromFile(File file) throws IOException { } } - private BoundingBoxData parseBoundingBoxData(String line, int lineNumber) { + private BoundingShapeData parseBoundingShapeData(String line, int lineNumber) { Scanner scanner = new Scanner(line); scanner.useLocale(Locale.ENGLISH); int categoryId = parseCategoryIndex(scanner, lineNumber); - double xMidRelative = parseRatio(scanner, lineNumber); - double yMidRelative = parseRatio(scanner, lineNumber); - double widthRelative = parseRatio(scanner, lineNumber); - double heightRelative = parseRatio(scanner, lineNumber); + List entries = new ArrayList<>(); + while (scanner.hasNextDouble()) { + double entry = scanner.nextDouble(); + + assertRatio(entry, "Bounds value not within interval [0, 1] on line " + lineNumber + "."); + + entries.add(entry); + } + + if (entries.size() == 4) { + return createBoundingBoxData( + categoryId, entries.get(0), entries.get(1), entries.get(2), entries.get(3), lineNumber); + } else if(entries.size() >= 6 && entries.size() % 2 == 0) { + return createBoundingPolygonData(categoryId, entries); + } + + throw new InvalidAnnotationFormatException("Invalid number of bounds values on line " + lineNumber + "."); + } + + private BoundingBoxData createBoundingBoxData(int categoryId, double xMidRelative, double yMidRelative, + double widthRelative, double heightRelative, + int lineNumber) { double xMinRelative = xMidRelative - widthRelative / 2; - if(xMinRelative < 0 && -xMinRelative < 1e-6) { + 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) { + 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) { + 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) { + if (yMaxRelative > 1 && yMaxRelative - 1 < 1e-6) { yMaxRelative = 1; } assertRatio(yMaxRelative, INVALID_BOUNDING_BOX_COORDINATES_MESSAGE + lineNumber + "."); String categoryName = categories.get(categoryId); - ObjectCategory objectCategory = categoryNameToCategoryMap.computeIfAbsent(categoryName, - key -> new ObjectCategory(key, - ColorUtils - .createRandomColor())); + ObjectCategory objectCategory = categoryNameToCategoryMap.computeIfAbsent( + categoryName, + key -> new ObjectCategory(key, + ColorUtils + .createRandomColor())); // Note that there are no tags or parts in YOLO-format. - BoundingBoxData boundingBoxData = new BoundingBoxData(objectCategory, - xMinRelative, yMinRelative, xMaxRelative, yMaxRelative, - Collections.emptyList()); - - boundingShapeCountPerCategory.merge(categoryName, 1, Integer::sum); - - return boundingBoxData; + return new BoundingBoxData(objectCategory, + xMinRelative, yMinRelative, xMaxRelative, yMaxRelative, + Collections.emptyList()); } - private double parseRatio(Scanner scanner, int lineNumber) { - if(!scanner.hasNextDouble()) { - throw new InvalidAnnotationFormatException( - "Missing or invalid bounding-box bounds on line " + lineNumber + "."); - } - - double ratio = scanner.nextDouble(); + private BoundingPolygonData createBoundingPolygonData(int categoryId, List entries) { + String categoryName = categories.get(categoryId); - assertRatio(ratio, lineNumber); + ObjectCategory objectCategory = categoryNameToCategoryMap.computeIfAbsent(categoryName, + key -> new ObjectCategory(key, + ColorUtils + .createRandomColor())); - return ratio; + // Note that there are no tags or parts in YOLO-format. + return new BoundingPolygonData(objectCategory, entries, Collections.emptyList()); } private int parseCategoryIndex(Scanner scanner, int lineNumber) { - if(!scanner.hasNextInt()) { + if (!scanner.hasNextInt()) { throw new InvalidAnnotationFormatException("Missing or invalid category index on line " + lineNumber + "."); } int categoryId = scanner.nextInt(); - if(categoryId < 0 || categoryId >= categories.size()) { + if (categoryId < 0 || categoryId >= categories.size()) { throw new InvalidAnnotationFormatException("Invalid category index " + categoryId - + " (of " + categories.size() + " categories) on line " + - lineNumber + "."); + + " (of " + categories.size() + " categories) on line " + + lineNumber + "."); } return categoryId; } - private void assertRatio(double ratio, int lineNumber) { - if(ratio < 0 || ratio > 1) { - throw new InvalidAnnotationFormatException("Bounds ratio not within [0, 1] on line " + lineNumber + "."); - } - } - private void assertRatio(double ratio, String message) { - if(ratio < 0 || ratio > 1) { + if (ratio < 0 || ratio > 1) { throw new InvalidAnnotationFormatException(message); } } diff --git a/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOSaveStrategy.java b/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOSaveStrategy.java index 630c82d..4493e8e 100644 --- a/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOSaveStrategy.java +++ b/src/main/java/com/github/mfl28/boundingboxeditor/model/io/YOLOSaveStrategy.java @@ -18,10 +18,7 @@ */ package com.github.mfl28.boundingboxeditor.model.io; -import com.github.mfl28.boundingboxeditor.model.data.BoundingBoxData; -import com.github.mfl28.boundingboxeditor.model.data.BoundingShapeData; -import com.github.mfl28.boundingboxeditor.model.data.ImageAnnotation; -import com.github.mfl28.boundingboxeditor.model.data.ImageAnnotationData; +import com.github.mfl28.boundingboxeditor.model.data.*; import com.github.mfl28.boundingboxeditor.model.io.results.IOErrorInfoEntry; import com.github.mfl28.boundingboxeditor.model.io.results.ImageAnnotationExportResult; import javafx.beans.property.DoubleProperty; @@ -36,10 +33,13 @@ import java.text.DecimalFormatSymbols; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; /** - * Saves rectangular bounding-box annotations in the YOLO-format described at - * ... + * Saves bounding-box and bounding-polygon annotations (with at least 3 nodes) + * in the YOLO-format described at + * ... and + * ... */ public class YOLOSaveStrategy implements ImageAnnotationSaveStrategy { private static final DecimalFormat DECIMAL_FORMAT = @@ -54,16 +54,16 @@ public ImageAnnotationExportResult save(ImageAnnotationData annotations, Path de DoubleProperty progress) { this.saveFolderPath = destination; this.categories = annotations.categoryNameToBoundingShapeCountMap().entrySet().stream() - .filter(stringIntegerEntry -> stringIntegerEntry.getValue() > 0) - .map(Map.Entry::getKey) - .sorted() - .toList(); + .filter(stringIntegerEntry -> stringIntegerEntry.getValue() > 0) + .map(Map.Entry::getKey) + .sorted() + .toList(); List unParsedFileErrorMessages = Collections.synchronizedList(new ArrayList<>()); try { createObjectDataFile(); - } catch(IOException e) { + } catch (IOException e) { unParsedFileErrorMessages.add(new IOErrorInfoEntry(OBJECT_DATA_FILE_NAME, e.getMessage())); } @@ -73,7 +73,7 @@ public ImageAnnotationExportResult save(ImageAnnotationData annotations, Path de annotations.imageAnnotations().parallelStream().forEach(annotation -> { try { createAnnotationFile(annotation); - } catch(IOException e) { + } catch (IOException e) { unParsedFileErrorMessages .add(new IOErrorInfoEntry(annotation.getImageFileName(), e.getMessage())); } @@ -88,15 +88,13 @@ public ImageAnnotationExportResult save(ImageAnnotationData annotations, Path de } private void createObjectDataFile() throws IOException { - try(BufferedWriter fileWriter = Files.newBufferedWriter(saveFolderPath - .resolve(OBJECT_DATA_FILE_NAME))) { - for(int i = 0; i < categories.size() - 1; ++i) { + try (BufferedWriter fileWriter = Files.newBufferedWriter( + saveFolderPath.resolve(OBJECT_DATA_FILE_NAME))) { + for (int i = 0; i < categories.size(); ++i) { fileWriter.write(categories.get(i)); - fileWriter.newLine(); - } - - if(!categories.isEmpty()) { - fileWriter.write(categories.get(categories.size() - 1)); + if (i != categories.size() - 1) { + fileWriter.newLine(); + } } } } @@ -105,25 +103,27 @@ private void createAnnotationFile(ImageAnnotation annotation) throws IOException String imageFileName = annotation.getImageFileName(); String imageFileNameWithoutExtension = imageFileName.substring(0, imageFileName.lastIndexOf('.')); - try(BufferedWriter fileWriter = Files.newBufferedWriter(saveFolderPath - .resolve(imageFileNameWithoutExtension + - YOLO_ANNOTATION_FILE_EXTENSION))) { + try (BufferedWriter fileWriter = Files.newBufferedWriter( + saveFolderPath.resolve(imageFileNameWithoutExtension + + YOLO_ANNOTATION_FILE_EXTENSION))) { List boundingShapeDataList = annotation.getBoundingShapeData(); - for(int i = 0; i < boundingShapeDataList.size() - 1; ++i) { + for (int i = 0; i < boundingShapeDataList.size(); ++i) { BoundingShapeData boundingShapeData = boundingShapeDataList.get(i); - if(boundingShapeData instanceof BoundingBoxData boundingBoxData) { + if (boundingShapeData instanceof BoundingBoxData boundingBoxData) { fileWriter.write(createBoundingBoxDataEntry(boundingBoxData)); - fileWriter.newLine(); - } - } - if(!boundingShapeDataList.isEmpty()) { - BoundingShapeData lastShapeData = boundingShapeDataList.get(boundingShapeDataList.size() - 1); + if (i != boundingShapeDataList.size() - 1) { + fileWriter.newLine(); + } + } else if (boundingShapeData instanceof BoundingPolygonData boundingPolygonData + && boundingPolygonData.getRelativePointsInImage().size() >= 6) { + fileWriter.write(createBoundingPolygonDataEntry(boundingPolygonData)); - if(lastShapeData instanceof BoundingBoxData boundingBoxData) { - fileWriter.write(createBoundingBoxDataEntry(boundingBoxData)); + if (i != boundingShapeDataList.size() - 1) { + fileWriter.newLine(); + } } } } @@ -141,4 +141,16 @@ private String createBoundingBoxDataEntry(BoundingBoxData boundingBoxData) { return StringUtils.join(List.of(categoryIndex, xMidRelative, yMidRelative, widthRelative, heightRelative), " "); } + + private String createBoundingPolygonDataEntry(BoundingPolygonData boundingPolygonData) { + int categoryIndex = categories.indexOf(boundingPolygonData.getCategoryName()); + + List relativePoints = boundingPolygonData.getRelativePointsInImage(); + + String relativePointsEntry = relativePoints.stream() + .map(DECIMAL_FORMAT::format) + .collect(Collectors.joining(" ")); + + return StringUtils.join(List.of(categoryIndex, relativePointsEntry), " "); + } } 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 29e53b5..5aa0187 100644 --- a/src/test/java/com/github/mfl28/boundingboxeditor/controller/ControllerTests.java +++ b/src/test/java/com/github/mfl28/boundingboxeditor/controller/ControllerTests.java @@ -64,7 +64,7 @@ class ControllerTests extends BoundingBoxEditorTestBase { @Start void start(Stage stage) { super.onStart(stage); - controller.loadImageFiles(new File(getClass().getResource(TEST_IMAGE_FOLDER_PATH_1).getFile())); + controller.loadImageFiles(new File(Objects.requireNonNull(getClass().getResource(TEST_IMAGE_FOLDER_PATH_1)).getFile())); } @Test @@ -81,10 +81,10 @@ void onExportAnnotation_PVOC_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); verifyThat(model.isSaved(), Matchers.is(true), saveScreenshot(testinfo)); - final File referenceAnnotationFile = new File(getClass().getResource(referenceAnnotationFilePath).getFile()); + final File referenceAnnotationFile = new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationFilePath)).getFile()); timeOutClickOn(robot, "#file-menu", testinfo); WaitForAsyncUtils.waitForFxEvents(); @@ -107,68 +107,68 @@ void onExportAnnotation_PVOC_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv Path actualDir = Files.createDirectory(tempDirectory.resolve("actual")); Assertions.assertTrue(Files.isDirectory(actualDir), - () -> saveScreenshotAndReturnMessage(testinfo, "Actual files " + - "directory does not exist.")); + () -> saveScreenshotAndReturnMessage(testinfo, "Actual files " + + "directory does not exist.")); final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 2) && - Objects.equals(counts.get("Sail"), 6) && - Objects.equals(counts.get("Flag"), 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 2) && + Objects.equals(counts.get("Sail"), 6) && + Objects.equals(counts.get("Flag"), 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(3), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(model.getObjectCategories(), Matchers.hasSize(3), saveScreenshot(testinfo)); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> mainView.getImageFileListView() - .getSelectionModel() - .getSelectedItem() - .isHasAssignedBoundingShapes() - && mainView.getCurrentBoundingShapes() - .stream() - .filter(viewable -> viewable instanceof BoundingBoxView) - .count() == 8 - && mainView.getCurrentBoundingShapes() - .stream() - .filter(viewable -> viewable instanceof BoundingPolygonView) - .count() == 1), - () -> saveScreenshotAndReturnMessage(testinfo, - "Bounding shape counts did not match " + - "within " + TIMEOUT_DURATION_IN_SEC + - " sec.")); + () -> mainView.getImageFileListView() + .getSelectionModel() + .getSelectedItem() + .isHasAssignedBoundingShapes() + && mainView.getCurrentBoundingShapes() + .stream() + .filter(viewable -> viewable instanceof BoundingBoxView) + .count() == 8 + && mainView.getCurrentBoundingShapes() + .stream() + .filter(viewable -> viewable instanceof BoundingPolygonView) + .count() == 1), + () -> saveScreenshotAndReturnMessage(testinfo, + "Bounding shape counts did not match " + + "within " + TIMEOUT_DURATION_IN_SEC + + " sec.")); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> controller.getIoMetaData() - .getDefaultAnnotationLoadingDirectory() - .equals(referenceAnnotationFile - .getParentFile())), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected default annotation loading " + - "directory was not set within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> controller.getIoMetaData() + .getDefaultAnnotationLoadingDirectory() + .equals(referenceAnnotationFile + .getParentFile())), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected default annotation loading " + + "directory was not set within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(model.getImageFileNameToAnnotationMap().values().stream() .allMatch(imageAnnotation -> imageAnnotation.getImageMetaData().hasDetails()), - Matchers.equalTo(true), saveScreenshot(testinfo)); + Matchers.equalTo(true), saveScreenshot(testinfo)); // Zoom a bit to change the image-view size. robot.moveTo(mainView.getEditorImageView()) - .press(KeyCode.SHORTCUT) - .scroll(-30) - .release(KeyCode.SHORTCUT); + .press(KeyCode.SHORTCUT) + .scroll(-30) + .release(KeyCode.SHORTCUT); WaitForAsyncUtils.waitForFxEvents(); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); verifyThat(model.isSaved(), Matchers.is(true), saveScreenshot(testinfo)); // Save the annotations to the temporary folder. Platform.runLater(() -> controller.initiateAnnotationExport(actualDir.toFile(), - ImageAnnotationSaveStrategy.Type.PASCAL_VOC)); + ImageAnnotationSaveStrategy.Type.PASCAL_VOC)); WaitForAsyncUtils.waitForFxEvents(); timeOutAssertServiceSucceeded(controller.getAnnotationExportService(), testinfo); @@ -179,36 +179,36 @@ void onExportAnnotation_PVOC_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv // Wait until the output-file actually exists. If the file was not created in // the specified time-frame, a TimeoutException is thrown and the test fails. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Files.exists(actualFilePath)), - () -> saveScreenshotAndReturnMessage(testinfo, - "Output-file was not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Files.exists(actualFilePath)), + () -> saveScreenshotAndReturnMessage(testinfo, + "Output-file was not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); // The output file should be exactly the same as the reference file. - final File referenceFile = new File(getClass().getResource(referenceAnnotationFilePath).getFile()); + final File referenceFile = new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationFilePath)).getFile()); final byte[] referenceArray = Files.readAllBytes(referenceFile.toPath()); // Wait until the annotations were written to the output file and the file is equivalent to the reference file // or throw a TimeoutException if this did not happen within the specified time-frame. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Arrays.equals(referenceArray, - Files.readAllBytes( - actualFilePath))), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected annotation output-file " + - "content was not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Arrays.equals(referenceArray, + Files.readAllBytes( + actualFilePath))), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected annotation output-file " + + "content was not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> controller.getIoMetaData() - .getDefaultAnnotationSavingDirectory() - .equals(actualDir.toFile())), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected default annotation saving " + - "directory was no set within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> controller.getIoMetaData() + .getDefaultAnnotationSavingDirectory() + .equals(actualDir.toFile())), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected default annotation saving " + + "directory was no set within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); } @Test @@ -224,10 +224,10 @@ void onExportAnnotation_YOLO_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); final File referenceAnnotationFolder = - new File(getClass().getResource(referenceAnnotationDirectoryPath).getFile()); + new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationDirectoryPath)).getFile()); timeOutClickOn(robot, "#file-menu", testinfo); WaitForAsyncUtils.waitForFxEvents(); @@ -250,48 +250,50 @@ void onExportAnnotation_YOLO_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv Path actualDir = Files.createDirectory(tempDirectory.resolve("actual")); Assertions.assertTrue(Files.isDirectory(actualDir), - () -> saveScreenshotAndReturnMessage(testinfo, "Actual files " + - "directory does not exist.")); + () -> saveScreenshotAndReturnMessage(testinfo, "Actual files " + + "directory does not exist.")); final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 2) && - Objects.equals(counts.get("Sail"), 6) && - Objects.equals(counts.get("Flag"), 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 2) && + Objects.equals(counts.get("Sail"), 6) && + Objects.equals(counts.get("Flag"), 1) && + Objects.equals(counts.get("Test"), 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding shape " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); - verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(3), - saveScreenshot(testinfo)); - verifyThat(model.getObjectCategories(), Matchers.hasSize(3), saveScreenshot(testinfo)); + verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(4), + saveScreenshot(testinfo)); + verifyThat(model.getObjectCategories(), Matchers.hasSize(4), saveScreenshot(testinfo)); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> mainView.getImageFileListView() - .getSelectionModel() - .getSelectedItem() - .isHasAssignedBoundingShapes() - && mainView.getCurrentBoundingShapes() - .stream() - .filter(viewable -> viewable instanceof BoundingBoxView) - .count() == 9 - && mainView.getCurrentBoundingShapes() - .stream().noneMatch( - viewable -> viewable instanceof BoundingPolygonView)), - () -> saveScreenshotAndReturnMessage(testinfo, - "Bounding shape counts did not match " + - "within " + TIMEOUT_DURATION_IN_SEC + - " sec.")); + () -> mainView.getImageFileListView() + .getSelectionModel() + .getSelectedItem() + .isHasAssignedBoundingShapes() + && mainView.getCurrentBoundingShapes() + .stream() + .filter(viewable -> viewable instanceof BoundingBoxView) + .count() == 9 + && mainView.getCurrentBoundingShapes() + .stream() + .filter(viewable -> viewable instanceof BoundingPolygonView) + .count() == 1), + () -> saveScreenshotAndReturnMessage(testinfo, + "Bounding shape counts did not match " + + "within " + TIMEOUT_DURATION_IN_SEC + + " sec.")); // Zoom a bit to change the image-view size. robot.moveTo(mainView.getEditorImageView()) - .press(KeyCode.SHORTCUT) - .scroll(-30) - .release(KeyCode.SHORTCUT); + .press(KeyCode.SHORTCUT) + .scroll(-30) + .release(KeyCode.SHORTCUT); WaitForAsyncUtils.waitForFxEvents(); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); // Save the annotations to the temporary folder. Platform.runLater( @@ -306,11 +308,11 @@ void onExportAnnotation_YOLO_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv // Wait until the output-file actually exists. If the file was not created in // the specified time-frame, a TimeoutException is thrown and the test fails. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Files.exists(actualFilePath) && - Files.exists(actualObjectDataFilePath)), - () -> saveScreenshotAndReturnMessage(testinfo, - "Output-files were not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Files.exists(actualFilePath) && + Files.exists(actualObjectDataFilePath)), + () -> saveScreenshotAndReturnMessage(testinfo, + "Output-files were not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); final File objectDataFile = referenceAnnotationFolder.toPath().resolve("object.data").toFile(); final byte[] objectDataFileArray = Files.readAllBytes(objectDataFile.toPath()); @@ -318,13 +320,13 @@ void onExportAnnotation_YOLO_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv // Wait until the annotations were written to the output file and the file is equivalent to the reference file // or throw a TimeoutException if this did not happen within the specified time-frame. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Arrays.equals(objectDataFileArray, - Files.readAllBytes( - actualObjectDataFilePath))), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected annotation output-file " + - "content was not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Arrays.equals(objectDataFileArray, + Files.readAllBytes( + actualObjectDataFilePath))), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected annotation output-file " + + "content was not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); // The output file should be exactly the same as the reference file. @@ -334,13 +336,13 @@ void onExportAnnotation_YOLO_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv // Wait until the annotations were written to the output file and the file is equivalent to the reference file // or throw a TimeoutException if this did not happen within the specified time-frame. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Arrays.equals(referenceArray, - Files.readAllBytes( - actualFilePath))), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected annotation output-file " + - "content was not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Arrays.equals(referenceArray, + Files.readAllBytes( + actualFilePath))), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected annotation output-file " + + "content was not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); } @Test @@ -356,10 +358,10 @@ void onExportAnnotation_JSON_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); final File referenceAnnotationFile = - new File(getClass().getResource(referenceAnnotationFilePath).getFile()); + new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationFilePath)).getFile()); timeOutClickOn(robot, "#file-menu", testinfo); WaitForAsyncUtils.waitForFxEvents(); @@ -382,55 +384,55 @@ void onExportAnnotation_JSON_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv Path actualDir = Files.createDirectory(tempDirectory.resolve("actual")); Assertions.assertTrue(Files.isDirectory(actualDir), () -> saveScreenshotAndReturnMessage(testinfo, - "Actual " + - "files " + - "directory does not exist.")); + "Actual " + + "files " + + "directory does not exist.")); final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 2) && - Objects.equals(counts.get("Sail"), 6) && - Objects.equals(counts.get("Flag"), 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 2) && + Objects.equals(counts.get("Sail"), 6) && + Objects.equals(counts.get("Flag"), 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(3), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(model.getObjectCategories(), Matchers.hasSize(3), saveScreenshot(testinfo)); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> mainView.getImageFileListView() - .getSelectionModel() - .getSelectedItem() - .isHasAssignedBoundingShapes() - && mainView.getCurrentBoundingShapes() - .stream() - .filter(viewable -> viewable instanceof BoundingBoxView) - .count() == 8 - && mainView.getCurrentBoundingShapes() - .stream() - .filter(viewable -> viewable instanceof BoundingPolygonView) - .count() == 1), - () -> saveScreenshotAndReturnMessage(testinfo, - "Bounding shape counts did not match " + - "within " + TIMEOUT_DURATION_IN_SEC + - " sec.")); + () -> mainView.getImageFileListView() + .getSelectionModel() + .getSelectedItem() + .isHasAssignedBoundingShapes() + && mainView.getCurrentBoundingShapes() + .stream() + .filter(viewable -> viewable instanceof BoundingBoxView) + .count() == 8 + && mainView.getCurrentBoundingShapes() + .stream() + .filter(viewable -> viewable instanceof BoundingPolygonView) + .count() == 1), + () -> saveScreenshotAndReturnMessage(testinfo, + "Bounding shape counts did not match " + + "within " + TIMEOUT_DURATION_IN_SEC + + " sec.")); // Zoom a bit to change the image-view size. robot.moveTo(mainView.getEditorImageView()) - .press(KeyCode.SHORTCUT) - .scroll(-30) - .release(KeyCode.SHORTCUT); + .press(KeyCode.SHORTCUT) + .scroll(-30) + .release(KeyCode.SHORTCUT); WaitForAsyncUtils.waitForFxEvents(); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); // Save the annotations to the temporary folder. Platform.runLater( () -> controller.initiateAnnotationExport(actualDir.resolve(expectedAnnotationFileName).toFile(), - ImageAnnotationSaveStrategy.Type.JSON)); + ImageAnnotationSaveStrategy.Type.JSON)); WaitForAsyncUtils.waitForFxEvents(); timeOutAssertServiceSucceeded(controller.getAnnotationExportService(), testinfo); @@ -440,10 +442,10 @@ void onExportAnnotation_JSON_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv // Wait until the output-file actually exists. If the file was not created in // the specified time-frame, a TimeoutException is thrown and the test fails. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Files.exists(actualFilePath)), - () -> saveScreenshotAndReturnMessage(testinfo, - "Output-files were not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Files.exists(actualFilePath)), + () -> saveScreenshotAndReturnMessage(testinfo, + "Output-files were not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); // The output file should be exactly the same as the reference file. final byte[] referenceArray = Files.readAllBytes(referenceAnnotationFile.toPath()); @@ -451,13 +453,13 @@ void onExportAnnotation_JSON_WhenPreviouslyImportedAnnotation_ShouldProduceEquiv // Wait until the annotations were written to the output file and the file is equivalent to the reference file // or throw a TimeoutException if this did not happen within the specified time-frame. Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Arrays.equals(referenceArray, - Files.readAllBytes( - actualFilePath))), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected annotation output-file " + - "content was not created within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Arrays.equals(referenceArray, + Files.readAllBytes( + actualFilePath))), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected annotation output-file " + + "content was not created within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); } @Test @@ -469,9 +471,9 @@ void onLoadAnnotation_YOLO_WhenObjectDataFileMissing_ShouldNotLoadAnnotations(Fx timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); - final File inputFile = new File(getClass().getResource(inputPath).getFile()); + final File inputFile = new File(Objects.requireNonNull(getClass().getResource(inputPath)).getFile()); // Load bounding-boxes defined in annotation-file. Platform.runLater(() -> controller.initiateAnnotationImport(inputFile, ImageAnnotationLoadStrategy.Type.YOLO)); @@ -485,26 +487,26 @@ void onLoadAnnotation_YOLO_WhenObjectDataFileMissing_ShouldNotLoadAnnotations(Fx final String errorReportDialogContentReferenceText = "The source does not contain any valid annotations."; final DialogPane errorReportDialog = (DialogPane) errorReportStage.getScene().getRoot(); verifyThat(errorReportDialog.getContentText(), Matchers.equalTo(errorReportDialogContentReferenceText), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(errorReportDialog.getExpandableContent(), Matchers.instanceOf(GridPane.class), - saveScreenshot(testinfo)); - verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().get(0), - Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); + saveScreenshot(testinfo)); + verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().getFirst(), + Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); final GridPane errorReportDialogContentPane = (GridPane) errorReportDialog.getExpandableContent(); - verifyThat(errorReportDialogContentPane.getChildren().get(0), Matchers.instanceOf(TableView.class), - saveScreenshot(testinfo)); + verifyThat(errorReportDialogContentPane.getChildren().getFirst(), Matchers.instanceOf(TableView.class), + saveScreenshot(testinfo)); @SuppressWarnings("unchecked") final TableView errorInfoTable = - (TableView) errorReportDialogContentPane.getChildren().get(0); + (TableView) errorReportDialogContentPane.getChildren().getFirst(); final List errorInfoEntries = errorInfoTable.getItems(); verifyThat(errorInfoEntries, Matchers.hasSize(1), saveScreenshot(testinfo)); final IOErrorInfoEntry referenceErrorInfoEntry1 = new IOErrorInfoEntry("object.data", - "Does not exist in annotation folder \"missing-classes-file\"."); + "Does not exist in annotation folder \"missing-classes-file\"."); verifyThat(errorInfoEntries, Matchers.contains(referenceErrorInfoEntry1), saveScreenshot(testinfo)); @@ -521,7 +523,7 @@ void onLoadAnnotation_YOLO_WhenObjectDataFileMissing_ShouldNotLoadAnnotations(Fx verifyThat(counts.size(), Matchers.equalTo(0), saveScreenshot(testinfo)); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); } @Test @@ -534,9 +536,9 @@ void onLoadAnnotation_YOLO_WhenAnnotationFileContainsErrors_ShouldNotLoadInvalid timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); - final File inputFile = new File(getClass().getResource(inputPath).getFile()); + final File inputFile = new File(Objects.requireNonNull(getClass().getResource(inputPath)).getFile()); // Load bounding-boxes defined in annotation-file. Platform.runLater(() -> controller.initiateAnnotationImport(inputFile, ImageAnnotationLoadStrategy.Type.YOLO)); @@ -551,19 +553,19 @@ void onLoadAnnotation_YOLO_WhenAnnotationFileContainsErrors_ShouldNotLoadInvalid "Some bounding boxes could not be loaded from 4 image-annotations."; final DialogPane errorReportDialog = (DialogPane) errorReportStage.getScene().getRoot(); verifyThat(errorReportDialog.getContentText(), Matchers.equalTo(errorReportDialogContentReferenceText), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(errorReportDialog.getExpandableContent(), Matchers.instanceOf(GridPane.class), - saveScreenshot(testinfo)); - verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().get(0), - Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); + saveScreenshot(testinfo)); + verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().getFirst(), + Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); final GridPane errorReportDialogContentPane = (GridPane) errorReportDialog.getExpandableContent(); - verifyThat(errorReportDialogContentPane.getChildren().get(0), Matchers.instanceOf(TableView.class), - saveScreenshot(testinfo)); + verifyThat(errorReportDialogContentPane.getChildren().getFirst(), Matchers.instanceOf(TableView.class), + saveScreenshot(testinfo)); @SuppressWarnings("unchecked") final TableView errorInfoTable = - (TableView) errorReportDialogContentPane.getChildren().get(0); + (TableView) errorReportDialogContentPane.getChildren().getFirst(); final List errorInfoEntries = errorInfoTable.getItems(); @@ -571,27 +573,27 @@ void onLoadAnnotation_YOLO_WhenAnnotationFileContainsErrors_ShouldNotLoadInvalid final IOErrorInfoEntry referenceErrorInfoEntry1 = new IOErrorInfoEntry("austin-neill-685084-unsplash.txt", - "Invalid category index 4 (of 4 categories) on line 1."); + "Invalid category index 4 (of 4 categories) on line 1."); final IOErrorInfoEntry referenceErrorInfoEntry2 = new IOErrorInfoEntry("caleb-george-316073-unsplash.txt", - "Missing or invalid category index on line 1."); + "Missing or invalid category index on line 1."); final IOErrorInfoEntry referenceErrorInfoEntry3 = new IOErrorInfoEntry("nico-bhlr-1067059-unsplash.txt", - "Missing or invalid bounding-box bounds on line 1."); + "Invalid number of bounds values on line 1."); final IOErrorInfoEntry referenceErrorInfoEntry4 = new IOErrorInfoEntry("tyler-nix-582593-unsplash.txt", - "Bounds ratio not within [0, 1] on line 1."); + "Bounds value not within interval [0, 1] on line 1."); final IOErrorInfoEntry referenceErrorInfoEntry5 = new IOErrorInfoEntry("tyler-nix-582593-unsplash.txt", - "Invalid bounding-box coordinates on line 2."); + "Invalid bounding-box coordinates on line 2."); verifyThat(errorInfoEntries, Matchers.containsInAnyOrder(referenceErrorInfoEntry1, referenceErrorInfoEntry2, - referenceErrorInfoEntry3, referenceErrorInfoEntry4, - referenceErrorInfoEntry5), saveScreenshot(testinfo)); + referenceErrorInfoEntry3, referenceErrorInfoEntry4, + referenceErrorInfoEntry5), saveScreenshot(testinfo)); WaitForAsyncUtils.waitForFxEvents(); @@ -606,10 +608,10 @@ void onLoadAnnotation_YOLO_WhenAnnotationFileContainsErrors_ShouldNotLoadInvalid verifyThat(counts.get("Ship"), Matchers.equalTo(1), saveScreenshot(testinfo)); verifyThat(mainView.getCurrentBoundingShapes(), Matchers.hasSize(0), saveScreenshot(testinfo)); verifyThat(model.createImageAnnotationData().imageAnnotations(), Matchers.hasSize(1), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); } @Test @@ -621,7 +623,7 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingNonCriticalElements_ShouldNotLoadIn WaitForAsyncUtils.waitForFxEvents(); timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); - final File referenceAnnotationFile = new File(getClass().getResource(inputFilePath).getFile()); + final File referenceAnnotationFile = new File(Objects.requireNonNull(getClass().getResource(inputFilePath)).getFile()); // Load bounding-boxes defined in annotation-file. Platform.runLater(() -> controller @@ -637,19 +639,19 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingNonCriticalElements_ShouldNotLoadIn "Some bounding boxes could not be loaded from 1 image-annotation."; final DialogPane errorReportDialog = (DialogPane) errorReportStage.getScene().getRoot(); verifyThat(errorReportDialog.getContentText(), Matchers.equalTo(errorReportDialogContentReferenceText), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(errorReportDialog.getExpandableContent(), Matchers.instanceOf(GridPane.class), - saveScreenshot(testinfo)); - verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().get(0), - Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); + saveScreenshot(testinfo)); + verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().getFirst(), + Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); final GridPane errorReportDialogContentPane = (GridPane) errorReportDialog.getExpandableContent(); - verifyThat(errorReportDialogContentPane.getChildren().get(0), Matchers.instanceOf(TableView.class), - saveScreenshot(testinfo)); + verifyThat(errorReportDialogContentPane.getChildren().getFirst(), Matchers.instanceOf(TableView.class), + saveScreenshot(testinfo)); @SuppressWarnings("unchecked") final TableView errorInfoTable = - (TableView) errorReportDialogContentPane.getChildren().get(0); + (TableView) errorReportDialogContentPane.getChildren().getFirst(); final List errorInfoEntries = errorInfoTable.getItems(); @@ -657,12 +659,12 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingNonCriticalElements_ShouldNotLoadIn final IOErrorInfoEntry referenceErrorInfoEntry1 = new IOErrorInfoEntry("annotation_with_missing_elements.xml", - "Missing element: name"); + "Missing element: name"); final IOErrorInfoEntry referenceErrorInfoEntry2 = new IOErrorInfoEntry("annotation_with_missing_elements.xml", - "Missing element: ymin"); + "Missing element: ymin"); verifyThat(errorInfoEntries, Matchers.contains(referenceErrorInfoEntry1, referenceErrorInfoEntry2), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); WaitForAsyncUtils.waitForFxEvents(); @@ -673,15 +675,15 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingNonCriticalElements_ShouldNotLoadIn final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 1) && - Objects.equals(counts.get("Sail"), 6) && - counts.get("Flag") == null), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 1) && + Objects.equals(counts.get("Sail"), 6) && + counts.get("Flag") == null), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully imported annotations for 1 image in"), saveScreenshot(testinfo)); } @Test @@ -693,7 +695,7 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingCriticalElement_ShouldNotLoadAnyBou WaitForAsyncUtils.waitForFxEvents(); timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); - final File referenceAnnotationFile = new File(getClass().getResource(inputFilePath).getFile()); + final File referenceAnnotationFile = new File(Objects.requireNonNull(getClass().getResource(inputFilePath)).getFile()); // Load bounding-boxes defined in annotation-file. Platform.runLater(() -> controller @@ -708,33 +710,33 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingCriticalElement_ShouldNotLoadAnyBou final String errorReportDialogContentReferenceText = "The source does not contain any valid annotations."; final DialogPane errorReportDialog = (DialogPane) errorReportStage.getScene().getRoot(); verifyThat(errorReportDialog.getContentText(), Matchers.equalTo(errorReportDialogContentReferenceText), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(errorReportDialog.getExpandableContent(), Matchers.instanceOf(GridPane.class), - saveScreenshot(testinfo)); - verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().get(0), - Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); + saveScreenshot(testinfo)); + verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().getFirst(), + Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); final GridPane errorReportDialogContentPane = (GridPane) errorReportDialog.getExpandableContent(); - verifyThat(errorReportDialogContentPane.getChildren().get(0), Matchers.instanceOf(TableView.class), - saveScreenshot(testinfo)); + verifyThat(errorReportDialogContentPane.getChildren().getFirst(), Matchers.instanceOf(TableView.class), + saveScreenshot(testinfo)); @SuppressWarnings("unchecked") final TableView errorInfoTable = - (TableView) errorReportDialogContentPane.getChildren().get(0); + (TableView) errorReportDialogContentPane.getChildren().getFirst(); final List errorInfoEntries = errorInfoTable.getItems(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> errorInfoTable.getItems().size() == 1), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected number of error info entries" + - " not found in " + - TIMEOUT_DURATION_IN_SEC + - " sec.")); + () -> errorInfoTable.getItems().size() == 1), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected number of error info entries" + + " not found in " + + TIMEOUT_DURATION_IN_SEC + + " sec.")); final IOErrorInfoEntry referenceErrorInfoEntry = new IOErrorInfoEntry("annotation_with_missing_filename.xml", - "Missing element: filename"); + "Missing element: filename"); verifyThat(errorInfoEntries, Matchers.contains(referenceErrorInfoEntry), saveScreenshot(testinfo)); @@ -747,14 +749,14 @@ void onLoadAnnotation_PVOC_WhenFileHasMissingCriticalElement_ShouldNotLoadAnyBou timeOutAssertTopModalStageClosed(robot, "Annotation Import Error Report", testinfo); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().isEmpty(), Matchers.is(true), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(model.getObjectCategories(), Matchers.empty(), saveScreenshot(testinfo)); verifyThat(model.createImageAnnotationData().imageAnnotations(), Matchers.empty(), saveScreenshot(testinfo)); verifyThat(mainView.getCurrentBoundingShapes(), Matchers.empty(), saveScreenshot(testinfo)); // Should not have changed the status message. verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); } @Test @@ -782,36 +784,36 @@ void onLoadAnnotation_PVOC_WhenAnnotationsPresent_ShouldAskForAndCorrectlyApplyU WaitForAsyncUtils.waitForFxEvents(); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap(), Matchers.hasEntry("Test", 0), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); // Draw a bounding box. moveRelativeToImageView(robot, new Point2D(0.25, 0.25), new Point2D(0.75, 0.75)); WaitForAsyncUtils.waitForFxEvents(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> mainView.getCurrentBoundingShapes() - .size() == 1), - () -> saveScreenshotAndReturnMessage(testinfo, - "Expected number of bounding boxes not" + - " found in " + - TIMEOUT_DURATION_IN_SEC + - " sec.")); + () -> mainView.getCurrentBoundingShapes() + .size() == 1), + () -> saveScreenshotAndReturnMessage(testinfo, + "Expected number of bounding boxes not" + + " found in " + + TIMEOUT_DURATION_IN_SEC + + " sec.")); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap(), Matchers.hasEntry("Test", 1), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(mainView.getImageFileListView().getSelectionModel() - .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), - saveScreenshot(testinfo)); + .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), + saveScreenshot(testinfo)); - verifyThat(mainView.getCurrentBoundingShapes().get(0), Matchers.instanceOf(BoundingBoxView.class), - saveScreenshot(testinfo)); + verifyThat(mainView.getCurrentBoundingShapes().getFirst(), Matchers.instanceOf(BoundingBoxView.class), + saveScreenshot(testinfo)); verifyThat(model.isSaved(), Matchers.is(true), saveScreenshot(testinfo)); verifyThat(mainView.getStatusBar().isSavedStatus(), Matchers.is(true), saveScreenshot(testinfo)); - final BoundingBoxView drawnBoundingBox = (BoundingBoxView) mainView.getCurrentBoundingShapes().get(0); + final BoundingBoxView drawnBoundingBox = (BoundingBoxView) mainView.getCurrentBoundingShapes().getFirst(); - final File annotationFile = new File(getClass().getResource(referenceAnnotationFilePath).getFile()); + final File annotationFile = new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationFilePath)).getFile()); // (1) User chooses Cancel: userChoosesCancelOnAnnotationImportDialogSubtest(robot, drawnBoundingBox, annotationFile, testinfo); @@ -833,9 +835,9 @@ void onLoadAnnotation_JSON_WhenFileHasMissingCriticalElements_ShouldNotLoadInval timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); - final File inputFile = new File(getClass().getResource(missingFileNameAnnotationFilePath).getFile()); + final File inputFile = new File(Objects.requireNonNull(getClass().getResource(missingFileNameAnnotationFilePath)).getFile()); // Load bounding-boxes defined in annotation-file. Platform.runLater(() -> controller.initiateAnnotationImport(inputFile, ImageAnnotationLoadStrategy.Type.JSON)); @@ -850,92 +852,92 @@ void onLoadAnnotation_JSON_WhenFileHasMissingCriticalElements_ShouldNotLoadInval "image-annotation."; final DialogPane errorReportDialog = (DialogPane) errorReportStage.getScene().getRoot(); verifyThat(errorReportDialog.getContentText(), Matchers.equalTo(errorReportDialogContentReferenceText), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(errorReportDialog.getExpandableContent(), Matchers.instanceOf(GridPane.class), - saveScreenshot(testinfo)); - verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().get(0), - Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); + saveScreenshot(testinfo)); + verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().getFirst(), + Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); final GridPane errorReportDialogContentPane = (GridPane) errorReportDialog.getExpandableContent(); - verifyThat(errorReportDialogContentPane.getChildren().get(0), Matchers.instanceOf(TableView.class), - saveScreenshot(testinfo)); + verifyThat(errorReportDialogContentPane.getChildren().getFirst(), Matchers.instanceOf(TableView.class), + saveScreenshot(testinfo)); @SuppressWarnings("unchecked") final TableView errorInfoTable = - (TableView) errorReportDialogContentPane.getChildren().get(0); + (TableView) errorReportDialogContentPane.getChildren().getFirst(); final List errorInfoEntries = errorInfoTable.getItems(); verifyThat(errorInfoEntries, Matchers.hasSize(17), saveScreenshot(testinfo)); final IOErrorInfoEntry error1 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid coordinate value for minX element" + - " in bndbox element in annotation " + - "for image " + - "tyler-nix-582593-unsplash.jpg."); + "Invalid coordinate value for minX element" + + " in bndbox element in annotation " + + "for image " + + "tyler-nix-582593-unsplash.jpg."); final IOErrorInfoEntry error2 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid coordinate value for minY element" + - " in bndbox element in annotation " + - "for image " + - "tyler-nix-582593-unsplash.jpg."); + "Invalid coordinate value for minY element" + + " in bndbox element in annotation " + + "for image " + + "tyler-nix-582593-unsplash.jpg."); final IOErrorInfoEntry error3 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing category name element in " + - "annotation for image " + - "tyler-nix-582593-unsplash.jpg."); + "Missing category name element in " + + "annotation for image " + + "tyler-nix-582593-unsplash.jpg."); final IOErrorInfoEntry error4 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid coordinate value for maxX element" + - " in bndbox element in annotation " + - "for image " + - "nico-bhlr-1067059-unsplash.jpg."); + "Invalid coordinate value for maxX element" + + " in bndbox element in annotation " + + "for image " + + "nico-bhlr-1067059-unsplash.jpg."); final IOErrorInfoEntry error5 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing maxY element in bndbox element in" + - " annotation for image " + - "nico-bhlr-1067059-unsplash.jpg."); + "Missing maxY element in bndbox element in" + + " annotation for image " + + "nico-bhlr-1067059-unsplash.jpg."); final IOErrorInfoEntry error6 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid color element " + - "in annotation for image " + - "nico-bhlr-1067059-unsplash.jpg."); + "Invalid color element " + + "in annotation for image " + + "nico-bhlr-1067059-unsplash.jpg."); final IOErrorInfoEntry error7 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing category element in bndbox " + - "element in annotation for image nico-bhlr-1067059-unsplash.jpg."); + "Missing category element in bndbox " + + "element in annotation for image nico-bhlr-1067059-unsplash.jpg."); final IOErrorInfoEntry error8 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid tags value(s) in bndbox element " + - "in annotation for image nico-bhlr-1067059-unsplash.jpg."); + "Invalid tags value(s) in bndbox element " + + "in annotation for image nico-bhlr-1067059-unsplash.jpg."); final IOErrorInfoEntry error9 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing bndbox or polygon element in " + - "annotation for image austin-neill-685084-unsplash.jpg."); + "Missing bndbox or polygon element in " + + "annotation for image austin-neill-685084-unsplash.jpg."); final IOErrorInfoEntry error10 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing maxY element in bndbox element in" + - " annotation for image austin-neill-685084-unsplash.jpg."); + "Missing maxY element in bndbox element in" + + " annotation for image austin-neill-685084-unsplash.jpg."); final IOErrorInfoEntry error11 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid parts value(s) in bndbox element " + - "in annotation for image austin-neill-685084-unsplash.jpg."); + "Invalid parts value(s) in bndbox element " + + "in annotation for image austin-neill-685084-unsplash.jpg."); final IOErrorInfoEntry error12 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing minY element" + - " in bndbox element in annotation " + - "for image " + - "austin-neill-685084-unsplash.jpg."); + "Missing minY element" + + " in bndbox element in annotation " + + "for image " + + "austin-neill-685084-unsplash.jpg."); final IOErrorInfoEntry error13 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing maxY element" + - " in bndbox element in annotation " + - "for image " + - "austin-neill-685084-unsplash.jpg."); + "Missing maxY element" + + " in bndbox element in annotation " + + "for image " + + "austin-neill-685084-unsplash.jpg."); final IOErrorInfoEntry error14 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid number of coordinates in polygon " + - "element in annotation for image caleb-george-316073-unsplash.jpg."); + "Invalid number of coordinates in polygon " + + "element in annotation for image caleb-george-316073-unsplash.jpg."); final IOErrorInfoEntry error15 = new IOErrorInfoEntry("missing_critical_elements.json", - "Invalid coordinate value(s) in polygon " + - "element in annotation for image caleb-george-316073-unsplash.jpg."); + "Invalid coordinate value(s) in polygon " + + "element in annotation for image caleb-george-316073-unsplash.jpg."); final IOErrorInfoEntry error16 = new IOErrorInfoEntry("missing_critical_elements.json", - "Missing image fileName element."); + "Missing image fileName element."); final IOErrorInfoEntry error17 = new IOErrorInfoEntry("missing_critical_elements.json", - "Image nothere.jpg does not belong to " + - "currently loaded image files."); + "Image nothere.jpg does not belong to " + + "currently loaded image files."); verifyThat(errorInfoEntries, - Matchers.containsInAnyOrder(error1, error2, error3, error4, error5, error6, error7, - error8, error9, error10, error11, error12, error13, error14, - error15, error16, error17), saveScreenshot(testinfo)); + Matchers.containsInAnyOrder(error1, error2, error3, error4, error5, error6, error7, + error8, error9, error10, error11, error12, error13, error14, + error15, error16, error17), saveScreenshot(testinfo)); WaitForAsyncUtils.waitForFxEvents(); @@ -955,12 +957,12 @@ void onLoadAnnotation_JSON_WhenFileHasMissingCriticalElements_ShouldNotLoadInval final List objectCategories = model.getObjectCategories(); verifyThat(objectCategories, Matchers.hasSize(4), saveScreenshot(testinfo)); verifyThat(objectCategories.stream().map(ObjectCategory::getName).collect(Collectors.toList()), - Matchers.containsInAnyOrder("Car", "Sail", "Surfboard", "Boat"), saveScreenshot(testinfo)); + Matchers.containsInAnyOrder("Car", "Sail", "Surfboard", "Boat"), saveScreenshot(testinfo)); verifyThat(mainView.getCurrentBoundingShapes(), Matchers.hasSize(4), saveScreenshot(testinfo)); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully imported annotations for 3 images in "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully imported annotations for 3 images in "), saveScreenshot(testinfo)); } @Test @@ -972,9 +974,9 @@ void onLoadAnnotation_JSON_WhenAnnotationFileIsEmpty_ShouldDisplayErrorDialog(Fx timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); - final File inputFile = new File(getClass().getResource(emptyAnnotationFilePath).getFile()); + final File inputFile = new File(Objects.requireNonNull(getClass().getResource(emptyAnnotationFilePath)).getFile()); Platform.runLater(() -> controller.initiateAnnotationImport(inputFile, ImageAnnotationLoadStrategy.Type.JSON)); WaitForAsyncUtils.waitForFxEvents(); @@ -997,9 +999,9 @@ void onLoadAnnotation_JSON_WhenAnnotationFileIsCorrupt_ShouldDisplayErrorReport( timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); - final File inputFile = new File(getClass().getResource(corruptAnnotationFilePath).getFile()); + final File inputFile = new File(Objects.requireNonNull(getClass().getResource(corruptAnnotationFilePath)).getFile()); Platform.runLater(() -> controller.initiateAnnotationImport(inputFile, ImageAnnotationLoadStrategy.Type.JSON)); WaitForAsyncUtils.waitForFxEvents(); @@ -1012,28 +1014,28 @@ void onLoadAnnotation_JSON_WhenAnnotationFileIsCorrupt_ShouldDisplayErrorReport( final String errorReportDialogContentReferenceText = "The source does not contain any valid annotations."; final DialogPane errorReportDialog = (DialogPane) errorReportStage.getScene().getRoot(); verifyThat(errorReportDialog.getContentText(), Matchers.equalTo(errorReportDialogContentReferenceText), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(errorReportDialog.getExpandableContent(), Matchers.instanceOf(GridPane.class), - saveScreenshot(testinfo)); - verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().get(0), - Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); + saveScreenshot(testinfo)); + verifyThat(((GridPane) errorReportDialog.getExpandableContent()).getChildren().getFirst(), + Matchers.instanceOf(TableView.class), saveScreenshot(testinfo)); final GridPane errorReportDialogContentPane = (GridPane) errorReportDialog.getExpandableContent(); - verifyThat(errorReportDialogContentPane.getChildren().get(0), Matchers.instanceOf(TableView.class), - saveScreenshot(testinfo)); + verifyThat(errorReportDialogContentPane.getChildren().getFirst(), Matchers.instanceOf(TableView.class), + saveScreenshot(testinfo)); @SuppressWarnings("unchecked") final TableView errorInfoTable = - (TableView) errorReportDialogContentPane.getChildren().get(0); + (TableView) errorReportDialogContentPane.getChildren().getFirst(); final List errorInfoEntries = errorInfoTable.getItems(); verifyThat(errorInfoEntries, Matchers.hasSize(1), saveScreenshot(testinfo)); - verifyThat(errorInfoEntries.get(0).getSourceName(), Matchers.equalTo("corrupt.json"), saveScreenshot(testinfo)); - verifyThat(errorInfoEntries.get(0).getErrorDescription(), Matchers.startsWith("Unterminated array at line 2 " + - "column 13"), - saveScreenshot(testinfo)); + verifyThat(errorInfoEntries.getFirst().getSourceName(), Matchers.equalTo("corrupt.json"), saveScreenshot(testinfo)); + verifyThat(errorInfoEntries.getFirst().getErrorDescription(), Matchers.startsWith("Unterminated array at line 2 " + + "column 13"), + saveScreenshot(testinfo)); timeOutLookUpInStageAndClickOn(robot, errorReportStage, "OK", testinfo); } @@ -1049,13 +1051,13 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS timeOutAssertServiceSucceeded(controller.getImageMetaDataLoadingService(), testinfo); verifyThat(controller.getStage().getTitle(), Matchers.matchesRegex("^Bounding Box Editor \\d\\.\\d\\.\\d - .*$"), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(mainView.getStatusBar().getCurrentEventMessage(), - Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); + Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); verifyThat(model.isSaved(), Matchers.is(true), saveScreenshot(testinfo)); - final File referenceAnnotationFile = new File(getClass().getResource(referenceAnnotationFilePath).getFile()); + final File referenceAnnotationFile = new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationFilePath)).getFile()); // Load bounding-boxes defined in the reference annotation-file. Platform.runLater(() -> controller @@ -1066,12 +1068,12 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 2) && - Objects.equals(counts.get("Sail"), 6) && - Objects.equals(counts.get("Flag"), 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 2) && + Objects.equals(counts.get("Sail"), 6) && + Objects.equals(counts.get("Flag"), 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(model.isSaved(), Matchers.is(true), saveScreenshot(testinfo)); @@ -1087,9 +1089,9 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS loadImageFolder(TEST_IMAGE_FOLDER_PATH_1); Stage keepExistingCategoriesDialogStage = timeOutAssertDialogOpenedAndGetStage(robot, - "Open Image Folder", - "Keep existing categories?", - testinfo); + "Open Image Folder", + "Keep existing categories?", + testinfo); timeOutLookUpInStageAndClickOn(robot, keepExistingCategoriesDialogStage, "No", testinfo); WaitForAsyncUtils.waitForFxEvents(); @@ -1104,8 +1106,8 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS verifyThat(model.getImageFileNameToAnnotationMap().size(), Matchers.equalTo(0), saveScreenshot(testinfo)); verifyThat(controller.lastLoadedImageUrl, Matchers.nullValue(), saveScreenshot(testinfo)); verifyThat(controller.getIoMetaData().getDefaultImageLoadingDirectory(), - Matchers.equalTo(new File(getClass().getResource(TEST_IMAGE_FOLDER_PATH_1).getFile())), - saveScreenshot(testinfo)); + Matchers.equalTo(new File(Objects.requireNonNull(getClass().getResource(TEST_IMAGE_FOLDER_PATH_1)).getFile())), + saveScreenshot(testinfo)); // Reload bounding-boxes defined in the reference annotation-file. Platform.runLater(() -> controller @@ -1116,16 +1118,16 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS final Map countsReloaded = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects - .equals(countsReloaded.get("Boat"), 2) && - Objects.equals(countsReloaded.get("Sail"), - 6) - && - Objects.equals(countsReloaded.get("Flag"), - 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects + .equals(countsReloaded.get("Boat"), 2) && + Objects.equals(countsReloaded.get("Sail"), + 6) + && + Objects.equals(countsReloaded.get("Flag"), + 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); timeOutClickOn(robot, "#next-button", testinfo); waitUntilCurrentImageIsLoaded(testinfo); @@ -1136,8 +1138,7 @@ void onReloadAnnotations_afterImageFilesReopened_shouldCorrectlyDisplayBoundingS } @Test - void onLoadAnnotation_YOLO_WhenAnnotationWithinYOLOPrecision_ShouldLoadBoundingBoxes(FxRobot robot, - TestInfo testinfo) { + void onLoadAnnotation_YOLO_WhenAnnotationWithinYOLOPrecision_ShouldLoadBoundingBoxes(TestInfo testinfo) { final String referenceAnnotationDirectoryPath = "/testannotations/yolo/precision"; @@ -1149,7 +1150,7 @@ void onLoadAnnotation_YOLO_WhenAnnotationWithinYOLOPrecision_ShouldLoadBoundingB Matchers.startsWith("Successfully loaded 4 image-files from folder "), saveScreenshot(testinfo)); final File referenceAnnotationFolder = - new File(getClass().getResource(referenceAnnotationDirectoryPath).getFile()); + new File(Objects.requireNonNull(getClass().getResource(referenceAnnotationDirectoryPath)).getFile()); // Load bounding-boxes defined in the reference annotation-file. Platform.runLater(() -> controller @@ -1191,8 +1192,8 @@ private void userChoosesToSaveExistingAnnotationsOnAnnotationImport(FxRobot robo verifyThat(mainView.getCurrentBoundingShapes().size(), Matchers.equalTo(1), saveScreenshot(testinfo)); verifyThat(mainView.getImageFileListView().getSelectionModel() - .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), - saveScreenshot(testinfo)); + .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), + saveScreenshot(testinfo)); // Trying to import annotations updates annotation data and "saved" check // (there are unsaved annotations): verifyThat(model.isSaved(), Matchers.is(false), saveScreenshot(testinfo)); @@ -1214,28 +1215,28 @@ private void userChoosesNotToSaveExistingAnnotationsOnAnnotationImport(FxRobot r // the newly imported ones should exist. final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 2) && - Objects.equals(counts.get("Sail"), 6) && - Objects.equals(counts.get("Flag"), 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 2) && + Objects.equals(counts.get("Sail"), 6) && + Objects.equals(counts.get("Flag"), 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(3), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(model.getObjectCategories(), Matchers.hasSize(3), saveScreenshot(testinfo)); verifyThat(model.createImageAnnotationData().imageAnnotations(), Matchers.hasSize(1), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(mainView.getCurrentBoundingShapes(), Matchers.empty(), saveScreenshot(testinfo)); verifyThat(mainView.getObjectTree().getRoot().getChildren().size(), Matchers.equalTo(0), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(mainView.getImageFileListView().getSelectionModel() - .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(false), - saveScreenshot(testinfo)); + .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(false), + saveScreenshot(testinfo)); - verifyThat(mainView.getImageFileListView().getItems().get(0).isHasAssignedBoundingShapes(), - Matchers.is(true), saveScreenshot(testinfo)); + verifyThat(mainView.getImageFileListView().getItems().getFirst().isHasAssignedBoundingShapes(), + Matchers.is(true), saveScreenshot(testinfo)); // Loading new annotations from existing annotation files // should lead to a positive "saved" status: @@ -1252,48 +1253,48 @@ private void userChoosesYesOnAnnotationImportDialogSubTest(FxRobot robot, Boundi verifyThat(mainView.getCurrentBoundingShapes(), Matchers.hasItem(drawnBoundingBox), saveScreenshot(testinfo)); verifyThat(mainView.getImageFileListView().getSelectionModel() - .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), - saveScreenshot(testinfo)); + .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), + saveScreenshot(testinfo)); // ... but there should be additional categories and bounding boxes in the model. final Map counts = model.getCategoryToAssignedBoundingShapesCountMap(); Assertions.assertDoesNotThrow(() -> WaitForAsyncUtils.waitFor(TIMEOUT_DURATION_IN_SEC, TimeUnit.SECONDS, - () -> Objects.equals(counts.get("Boat"), 2) && - Objects.equals(counts.get("Sail"), 6) - && - Objects.equals(counts.get("Flag"), 1) && - Objects.equals(counts.get("Test"), 1)), - () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + - "per-category-counts were not read within " + - TIMEOUT_DURATION_IN_SEC + " sec.")); + () -> Objects.equals(counts.get("Boat"), 2) && + Objects.equals(counts.get("Sail"), 6) + && + Objects.equals(counts.get("Flag"), 1) && + Objects.equals(counts.get("Test"), 1)), + () -> saveScreenshotAndReturnMessage(testinfo, "Correct bounding box " + + "per-category-counts were not read within " + + TIMEOUT_DURATION_IN_SEC + " sec.")); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(4), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(model.getObjectCategories(), Matchers.hasSize(4), saveScreenshot(testinfo)); verifyThat(model.createImageAnnotationData().imageAnnotations(), Matchers.hasSize(2), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); - verifyThat(mainView.getImageFileListView().getItems().get(0).isHasAssignedBoundingShapes(), - Matchers.is(true), saveScreenshot(testinfo)); + verifyThat(mainView.getImageFileListView().getItems().getFirst().isHasAssignedBoundingShapes(), + Matchers.is(true), saveScreenshot(testinfo)); // Remove the imported Annotations manually to reset for next test. timeOutClickOn(robot, "#previous-button", testinfo); waitUntilCurrentImageIsLoaded(testinfo); WaitForAsyncUtils.waitForFxEvents(); - robot.rightClickOn(mainView.getObjectTree().getRoot().getChildren().get(0).getGraphic()) - .clickOn("Delete"); + robot.rightClickOn(mainView.getObjectTree().getRoot().getChildren().getFirst().getGraphic()) + .clickOn("Delete"); WaitForAsyncUtils.waitForFxEvents(); - for(int i = 0; i != 3; ++i) { + for (int i = 0; i != 3; ++i) { NodeQuery nodeQuery = robot.from(mainView.getObjectCategoryTable()).lookup("#delete-button").nth(1); robot.clickOn((Node) nodeQuery.query(), MouseButton.PRIMARY); } verifyThat(mainView.getObjectCategoryTable(), TableViewMatchers.hasNumRows(1), saveScreenshot(testinfo)); - verifyThat(mainView.getObjectCategoryTable().getItems().get(0).getName(), Matchers.equalTo("Test"), - saveScreenshot(testinfo)); + verifyThat(mainView.getObjectCategoryTable().getItems().getFirst().getName(), Matchers.equalTo("Test"), + saveScreenshot(testinfo)); timeOutClickOn(robot, "#next-button", testinfo); @@ -1312,13 +1313,13 @@ private void userChoosesCancelOnAnnotationImportDialogSubtest(FxRobot robot, verifyThat(mainView.getCurrentBoundingShapes().size(), Matchers.equalTo(1), saveScreenshot(testinfo)); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap().size(), Matchers.equalTo(1), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(model.getCategoryToAssignedBoundingShapesCountMap(), Matchers.hasEntry("Test", 1), - saveScreenshot(testinfo)); + saveScreenshot(testinfo)); verifyThat(mainView.getCurrentBoundingShapes(), Matchers.hasItem(drawnBoundingBox), saveScreenshot(testinfo)); verifyThat(mainView.getImageFileListView().getSelectionModel() - .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), - saveScreenshot(testinfo)); + .getSelectedItem().isHasAssignedBoundingShapes(), Matchers.is(true), + saveScreenshot(testinfo)); // Trying to import annotations updates annotation data and "saved" check // (there are unsaved annotations): verifyThat(model.isSaved(), Matchers.is(false), saveScreenshot(testinfo)); diff --git a/src/test/resources/testannotations/yolo/reference/austin-neill-685084-unsplash.txt b/src/test/resources/testannotations/yolo/reference/austin-neill-685084-unsplash.txt index 54efce6..a28ce5e 100644 --- a/src/test/resources/testannotations/yolo/reference/austin-neill-685084-unsplash.txt +++ b/src/test/resources/testannotations/yolo/reference/austin-neill-685084-unsplash.txt @@ -6,4 +6,5 @@ 2 0.483448 0.350107 0.127639 0.261242 2 0.476105 0.194325 0.033887 0.163812 2 0.471587 0.522484 0.044052 0.256959 -2 0.469611 0.79015 0.040099 0.147752 \ No newline at end of file +2 0.469611 0.79015 0.040099 0.147752 +3 0.75 0.5 0.8 0.6 0.82 0.4 \ No newline at end of file diff --git a/src/test/resources/testannotations/yolo/reference/object.data b/src/test/resources/testannotations/yolo/reference/object.data index 4806953..3bacb63 100644 --- a/src/test/resources/testannotations/yolo/reference/object.data +++ b/src/test/resources/testannotations/yolo/reference/object.data @@ -1,3 +1,4 @@ Boat Flag -Sail \ No newline at end of file +Sail +Test \ No newline at end of file