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