From cb00500705cca8562ec6d71641df4c58b2255f6c Mon Sep 17 00:00:00 2001 From: dnutiu Date: Sat, 27 Apr 2024 15:33:37 +0300 Subject: [PATCH] implement img-core a core module --- .idea/gradle.xml | 1 + img-ai/build.gradle.kts | 1 + img-ai/src/main/java/module-info.java | 1 + .../imagetagger/ai/ImageTagsPrediction.kt | 1 + img-core/build.gradle.kts | 20 +++++++ img-core/src/main/java/module-info.java | 7 +++ .../imagetagger/core/AnalyzedImage.kt | 52 +++++++++++++++++ .../abstractions}/IImageTagsPrediction.kt | 2 +- img-ui/build.gradle | 1 + img-ui/src/main/java/module-info.java | 1 + .../imagetagger/ui/BasicServiceLocator.kt | 2 +- .../dev/nuculabs/imagetagger/ui/MainPage.kt | 2 +- .../imagetagger/ui/MainPageController.kt | 57 +++++-------------- .../ui/controls/ImageTagsEntryControl.kt | 13 +++-- settings.gradle | 1 + 15 files changed, 109 insertions(+), 53 deletions(-) create mode 100644 img-core/build.gradle.kts create mode 100644 img-core/src/main/java/module-info.java create mode 100644 img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/AnalyzedImage.kt rename {img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai => img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/abstractions}/IImageTagsPrediction.kt (86%) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 9b66e48..19790fa 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -9,6 +9,7 @@ diff --git a/img-ai/build.gradle.kts b/img-ai/build.gradle.kts index 9d06292..399a419 100644 --- a/img-ai/build.gradle.kts +++ b/img-ai/build.gradle.kts @@ -14,6 +14,7 @@ repositories { } dependencies { + implementation(project(":img-core")) implementation("com.microsoft.onnxruntime:onnxruntime:1.17.1") testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") diff --git a/img-ai/src/main/java/module-info.java b/img-ai/src/main/java/module-info.java index 37af9e9..5195591 100644 --- a/img-ai/src/main/java/module-info.java +++ b/img-ai/src/main/java/module-info.java @@ -3,5 +3,6 @@ module dev.nuculabs.imagetagger.ai { requires java.desktop; requires java.logging; requires kotlin.stdlib; + requires dev.nuculabs.imagetagger.core; exports dev.nuculabs.imagetagger.ai; } \ No newline at end of file diff --git a/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/ImageTagsPrediction.kt b/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/ImageTagsPrediction.kt index 71ce441..57c6035 100644 --- a/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/ImageTagsPrediction.kt +++ b/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/ImageTagsPrediction.kt @@ -3,6 +3,7 @@ package dev.nuculabs.imagetagger.ai import ai.onnxruntime.OnnxTensor import ai.onnxruntime.OrtEnvironment import ai.onnxruntime.OrtSession +import dev.nuculabs.imagetagger.core.abstractions.IImageTagsPrediction import java.awt.image.BufferedImage import java.io.Closeable import java.io.IOException diff --git a/img-core/build.gradle.kts b/img-core/build.gradle.kts new file mode 100644 index 0000000..0031bb6 --- /dev/null +++ b/img-core/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("java") + kotlin("jvm") version "1.8.22" + id("org.javamodularity.moduleplugin") version "1.8.12" +} + +group = "dev.nuculabs.imagetagger.core" +version = "1.1" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/img-core/src/main/java/module-info.java b/img-core/src/main/java/module-info.java new file mode 100644 index 0000000..a82f5bf --- /dev/null +++ b/img-core/src/main/java/module-info.java @@ -0,0 +1,7 @@ +module dev.nuculabs.imagetagger.core { + requires kotlin.stdlib; + requires java.desktop; + requires java.logging; + exports dev.nuculabs.imagetagger.core; + exports dev.nuculabs.imagetagger.core.abstractions; +} \ No newline at end of file diff --git a/img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/AnalyzedImage.kt b/img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/AnalyzedImage.kt new file mode 100644 index 0000000..005a261 --- /dev/null +++ b/img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/AnalyzedImage.kt @@ -0,0 +1,52 @@ +package dev.nuculabs.imagetagger.core + +import dev.nuculabs.imagetagger.core.abstractions.IImageTagsPrediction +import java.awt.image.BufferedImage +import java.io.File +import java.util.logging.Logger +import javax.imageio.ImageIO + +/** + * AnalyzedImage represents an Analyzed Image inside the ImageTagger application. + */ +class AnalyzedImage(private val file: File, imageTagsPrediction: IImageTagsPrediction) { + + private var bufferedImage: BufferedImage? = null + private var predictedTags: List = emptyList() + private val logger: Logger = Logger.getLogger("AnalyzedImage") + private var error: String = "" + + /** + * Initializes the analyzed image and predicts its tags. + */ + init { + try { + bufferedImage = ImageIO.read(File(file.absolutePath)) + predictedTags = imageTagsPrediction.predictTags(bufferedImage!!) + } catch (e: Exception) { + logger.warning("Error while predicting images $e") + error = e.message.toString() + } + } + + /** + * Returns the prediction error + */ + fun errorMessage(): String { + return error; + } + + /** + * Returns the absolute file path of the image. + */ + fun absolutePath(): String { + return file.absolutePath; + } + + /** + * Returns the predicted tags. + */ + fun tags(): List { + return predictedTags + } +} \ No newline at end of file diff --git a/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/IImageTagsPrediction.kt b/img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/abstractions/IImageTagsPrediction.kt similarity index 86% rename from img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/IImageTagsPrediction.kt rename to img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/abstractions/IImageTagsPrediction.kt index 2e9c960..8373cd9 100644 --- a/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/IImageTagsPrediction.kt +++ b/img-core/src/main/kotlin/dev/nuculabs/imagetagger/core/abstractions/IImageTagsPrediction.kt @@ -1,4 +1,4 @@ -package dev.nuculabs.imagetagger.ai +package dev.nuculabs.imagetagger.core.abstractions import java.awt.image.BufferedImage import java.io.InputStream diff --git a/img-ui/build.gradle b/img-ui/build.gradle index ce09f2b..c4b6677 100644 --- a/img-ui/build.gradle +++ b/img-ui/build.gradle @@ -38,6 +38,7 @@ javafx { dependencies { implementation(project(":img-ai")) + implementation(project(":img-core")) implementation('org.controlsfx:controlsfx:11.1.2') implementation('com.dlsc.formsfx:formsfx-core:11.6.0') { exclude(group: 'org.openjfx') diff --git a/img-ui/src/main/java/module-info.java b/img-ui/src/main/java/module-info.java index 0dd6bab..9224102 100644 --- a/img-ui/src/main/java/module-info.java +++ b/img-ui/src/main/java/module-info.java @@ -14,6 +14,7 @@ module dev.nuculabs.imagetagger.ui { requires org.kordamp.ikonli.fontawesome5; requires kotlinx.coroutines.core; requires dev.nuculabs.imagetagger.ai; + requires dev.nuculabs.imagetagger.core; opens dev.nuculabs.imagetagger.ui to javafx.fxml, javafx.graphics; opens dev.nuculabs.imagetagger.ui.controls to javafx.fxml, javafx.graphics; diff --git a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/BasicServiceLocator.kt b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/BasicServiceLocator.kt index 85aa287..5e5a9d2 100644 --- a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/BasicServiceLocator.kt +++ b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/BasicServiceLocator.kt @@ -1,6 +1,6 @@ package dev.nuculabs.imagetagger.ui -import dev.nuculabs.imagetagger.ai.IImageTagsPrediction +import dev.nuculabs.imagetagger.core.abstractions.IImageTagsPrediction /** * BasicServiceLocator is implemented to avoid polluting the apps with singletons. diff --git a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPage.kt b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPage.kt index 05d6e9a..fa51685 100644 --- a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPage.kt +++ b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPage.kt @@ -1,6 +1,6 @@ package dev.nuculabs.imagetagger.ui -import dev.nuculabs.imagetagger.ai.IImageTagsPrediction +import dev.nuculabs.imagetagger.core.abstractions.IImageTagsPrediction import dev.nuculabs.imagetagger.ai.ImageTagsPrediction import javafx.application.Application import javafx.application.Platform diff --git a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPageController.kt b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPageController.kt index 32eecef..dcd6280 100644 --- a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPageController.kt +++ b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/MainPageController.kt @@ -1,5 +1,6 @@ package dev.nuculabs.imagetagger.ui +import dev.nuculabs.imagetagger.core.AnalyzedImage import dev.nuculabs.imagetagger.ui.controls.ImageTagsEntryControl import dev.nuculabs.imagetagger.ui.controls.ImageTagsSessionHeader import javafx.application.Platform @@ -11,13 +12,11 @@ import javafx.scene.layout.HBox import javafx.scene.layout.Priority import javafx.scene.layout.VBox import javafx.stage.FileChooser -import java.io.File import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.Semaphore import java.util.concurrent.atomic.AtomicInteger import java.util.logging.Logger -import javax.imageio.ImageIO class MainPageController { @@ -34,7 +33,7 @@ class MainPageController { private val maxImagesPredictionInProgress = Runtime.getRuntime().availableProcessors() /** - * Semaphore to limit the maximum amount of predictions submitted to the tread pool. + * Semaphore to limit the maximum number of predictions submitted to the tread pool. */ private val workerSemaphore: Semaphore = Semaphore(maxImagesPredictionInProgress) @@ -54,8 +53,8 @@ class MainPageController { private val imageTagsPrediction = BasicServiceLocator.getInstance().imageTagsPrediction /** - * A boolean that when set to true it will stop the current image tagging operation. - * When a new operation is started the boolean is reset to false. + * A boolean that when set to, true it will stop the current image tagging operation. + * When a new operation is started, the boolean is reset to false. */ private var isCurrentTagsOperationCancelled: Boolean = false @@ -112,19 +111,12 @@ class MainPageController { return@submit } - predictImageTags( - filePath, - onError = { - workerSemaphore.release() - } - ) { imagePath, imageTags -> - // Add newly predicted tags to UI. - Platform.runLater { - // Add image and prediction to the view. - addNewImagePredictionEntry(imagePath, imageTags) - updateProgressBar() - workerSemaphore.release() - } + val analyzedImage = AnalyzedImage(filePath, this.imageTagsPrediction) + workerSemaphore.release() + Platform.runLater { + // Add image and prediction to the view. + addNewImagePredictionEntry(analyzedImage) + updateProgressBar() } } } @@ -142,38 +134,15 @@ class MainPageController { cancelButton.isVisible = false } - /** - * Predicts an image tags and executes an action with it. - * - * @param filePath - The image file's absolute path. - */ - fun predictImageTags( - filePath: File, - onError: (Exception) -> Unit, - onSuccess: (String, List) -> Unit - ) { - try { - // Get predictions for the image. - val imageFile = ImageIO.read(File(filePath.absolutePath)) - val tags: List = imageTagsPrediction.predictTags(imageFile) - onSuccess(filePath.absolutePath, tags) - } catch (e: Exception) { - logger.warning("Error while predicting images $e") - onError(e) - } - } - /** * Updates the UI with a new ImagePredictionEntry. * - * @param imagePath - The image path. - * @param imageTags - The image's tags. + * @param analyzedImage - The analyzed image instance. */ fun addNewImagePredictionEntry( - imagePath: String, - imageTags: List, + analyzedImage: AnalyzedImage, ) { - verticalBox.children.add(ImageTagsEntryControl(imagePath, imageTags)) + verticalBox.children.add(ImageTagsEntryControl(analyzedImage)) verticalBox.children.add(Separator()) } diff --git a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsEntryControl.kt b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsEntryControl.kt index e3ce683..51439a1 100644 --- a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsEntryControl.kt +++ b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsEntryControl.kt @@ -1,5 +1,6 @@ package dev.nuculabs.imagetagger.ui.controls +import dev.nuculabs.imagetagger.core.AnalyzedImage import dev.nuculabs.imagetagger.ui.alerts.ErrorAlert import javafx.fxml.FXML import javafx.fxml.FXMLLoader @@ -21,7 +22,7 @@ import java.util.logging.Logger /** * This class is used to create a custom control for the image prediction entry. */ -class ImageTagsEntryControl(private val imagePath: String, predictions: List) : HBox() { +class ImageTagsEntryControl(private val image: AnalyzedImage) : HBox() { private val logger: Logger = Logger.getLogger("ImageTagsEntryControl") /** @@ -59,8 +60,8 @@ class ImageTagsEntryControl(private val imagePath: String, predictions: List