diff --git a/img-ui/build.gradle b/img-ui/build.gradle index c4b6677..86eec4e 100644 --- a/img-ui/build.gradle +++ b/img-ui/build.gradle @@ -1,70 +1,71 @@ plugins { - id 'java' - id 'application' - id 'org.jetbrains.kotlin.jvm' version '1.8.22' - id 'org.javamodularity.moduleplugin' version '1.8.12' - id 'org.openjfx.javafxplugin' version '0.0.13' - id 'org.beryx.jlink' version '2.25.0' + id 'java' + id 'application' + id 'org.jetbrains.kotlin.jvm' version '1.8.22' + id 'org.javamodularity.moduleplugin' version '1.8.12' + id 'org.openjfx.javafxplugin' version '0.0.13' + id 'org.beryx.jlink' version '2.25.0' } group 'com.nuculabs.dev' version '1.2' repositories { - mavenCentral() + mavenCentral() } ext { - junitVersion = '5.10.0' + junitVersion = '5.10.0' } tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' + options.encoding = 'UTF-8' } application { - mainModule = 'dev.nuculabs.imagetagger.ui' - mainClass = 'dev.nuculabs.imagetagger.ui.MainPage' + mainModule = 'dev.nuculabs.imagetagger.ui' + mainClass = 'dev.nuculabs.imagetagger.ui.MainPage' } kotlin { - jvmToolchain( 17 ) + jvmToolchain(17) } javafx { - version = '21' - modules = ['javafx.controls', 'javafx.fxml'] + version = '21' + modules = ['javafx.controls', 'javafx.fxml'] } 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') - } - implementation('net.synedra:validatorfx:0.4.0') { - exclude(group: 'org.openjfx') - } - implementation('org.kordamp.ikonli:ikonli-javafx:12.3.1') - implementation('org.kordamp.ikonli:ikonli-fontawesome5-pack:12.3.1') - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1-Beta") - testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") + 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') + } + implementation('net.synedra:validatorfx:0.4.0') { + exclude(group: 'org.openjfx') + } + implementation('org.kordamp.ikonli:ikonli-javafx:12.3.1') + implementation('org.kordamp.ikonli:ikonli-fontawesome5-pack:12.3.1') + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1-Beta") + implementation("org.apache.commons:commons-lang3:3.14.0") + testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") } test { - useJUnitPlatform() + useJUnitPlatform() } jlink { - imageZip = project.file("${buildDir}/distributions/ImageTagger-${javafx.platform.classifier}-${version}.zip") - options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] - launcher { - name = 'ImageTagger' - } + imageZip = project.file("${buildDir}/distributions/ImageTagger-${javafx.platform.classifier}-${version}.zip") + options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] + launcher { + name = 'ImageTagger' + } } jlinkZip { - group = 'distribution' + group = 'distribution' } \ No newline at end of file diff --git a/img-ui/src/main/java/module-info.java b/img-ui/src/main/java/module-info.java index 9224102..b3de0cf 100644 --- a/img-ui/src/main/java/module-info.java +++ b/img-ui/src/main/java/module-info.java @@ -15,6 +15,7 @@ module dev.nuculabs.imagetagger.ui { requires kotlinx.coroutines.core; requires dev.nuculabs.imagetagger.ai; requires dev.nuculabs.imagetagger.core; + requires org.apache.commons.lang3; 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/controls/ImageTagsEntryControl.kt b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsEntryControl.kt index aa047e1..c235046 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 @@ -2,6 +2,7 @@ package dev.nuculabs.imagetagger.ui.controls import dev.nuculabs.imagetagger.core.AnalyzedImage import dev.nuculabs.imagetagger.ui.alerts.ErrorAlert +import org.apache.commons.lang3.SystemUtils; import javafx.fxml.FXML import javafx.fxml.FXMLLoader import javafx.scene.control.Button @@ -121,14 +122,21 @@ class ImageTagsEntryControl(private val image: AnalyzedImage) : HBox() { * If the operation fails it will display an error alert. */ fun onOpenImageClick() { - if (Desktop.isDesktopSupported()) { - val desktop = Desktop.getDesktop() - if (desktop.isSupported(Desktop.Action.OPEN)) { - desktop.open(File(image.absolutePath())) - } + if (image.hasError()) { + return + } + if (SystemUtils.IS_OS_LINUX) { + Runtime.getRuntime().exec("xdg-open ${image.absolutePath()}") } else { - logger.severe("Cannot open image ${image.absolutePath()}. Desktop action not supported!") - ErrorAlert("Can't open file: ${image.absolutePath()}\nOperation is not supported!") + if (Desktop.isDesktopSupported()) { + val desktop = Desktop.getDesktop() + if (desktop.isSupported(Desktop.Action.OPEN)) { + desktop.open(File(image.absolutePath())) + } + } else { + logger.severe("Cannot open image ${image.absolutePath()}. Desktop action not supported!") + ErrorAlert("Can't open file: ${image.absolutePath()}\nOperation is not supported!") + } } } diff --git a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsSessionHeader.kt b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsSessionHeader.kt index 389acaf..b16f232 100644 --- a/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsSessionHeader.kt +++ b/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ui/controls/ImageTagsSessionHeader.kt @@ -8,6 +8,7 @@ import javafx.fxml.FXMLLoader import javafx.scene.control.Button import javafx.scene.control.Label import javafx.scene.layout.HBox +import org.apache.commons.lang3.SystemUtils import java.awt.Desktop import java.io.File import java.io.IOException @@ -71,18 +72,22 @@ class ImageTagsSessionHeader : HBox() { * Opens the directory in the user's Desktop. */ fun openDirectory() { - this.directoryPath?.let { - if (Desktop.isDesktopSupported()) { - val desktop = Desktop.getDesktop() - if (desktop.isSupported(Desktop.Action.OPEN)) { - desktop.open(File(it)) + directoryPath?.let { + if (SystemUtils.IS_OS_LINUX) { + Runtime.getRuntime().exec("xdg-open $directoryPath") + } else { + if (Desktop.isDesktopSupported()) { + val desktop = Desktop.getDesktop() + if (desktop.isSupported(Desktop.Action.OPEN)) { + desktop.open(File(it)) + } else { + logger.severe("Cannot open image directory $it. Desktop action not supported!") + ErrorAlert("Can't open file: $it\nOperation is not supported!") + } } else { logger.severe("Cannot open image directory $it. Desktop action not supported!") ErrorAlert("Can't open file: $it\nOperation is not supported!") } - } else { - logger.severe("Cannot open image directory $it. Desktop action not supported!") - ErrorAlert("Can't open file: $it\nOperation is not supported!") } } } diff --git a/readme.md b/readme.md index ab22bc2..bf34769 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,7 @@ -# ![](./docs/image-analysis.png) Image Tagger +# ![](./docs/image-analysis.png) Image Tagger -Image Tagger is a simple software application for predicting an image's keywords using a deep learning model based on resnet. +Image Tagger is a simple software application for predicting an image's keywords using a deep learning model based on +resnet. It allows photographers to automate the image tagging process. 📸 @@ -10,7 +11,7 @@ It allows photographers to automate the image tagging process. 📸 1. Download a release from the release page. 2. Unzip the release. -3. Run `ImageTagger\image\bin\ImageTagger`. +3. Run `ImageTagger\image\bin\ImageTagger`. ![./docs/application.png](./docs/application.png) @@ -18,21 +19,20 @@ Photo credit: [https://unsplash.com/@ndcphoto](https://unsplash.com/@ndcphoto) ## Development -If you want to build the application yourself, you will need Java 17 JDK and the +If you want to build the application yourself, you will need Java 17 JDK and the AI models available in the AIModels release. The release archive is in the [releases page](https://github.com/dnutiu/ImageTagger/releases). -Note: Some desktop related features do not work under Linux. I have tested them on Fedora 39. -Contributions are welcome :-) - +Note: On Linux desktop related features (opening images, folders) are handled +via [xdg-open](https://linux.die.net/man/1/xdg-open). ### Building and Running from source To build from source you will need Java 17 JDK and Gradle. Due to some GitHub limitations that do not allow me to upload large files, you'll need to download the AIModels -zip file which contains the deep learning models and place them into the +zip file which contains the deep learning models and place them into the `ImageTagger/img-ai/src/main/resources/dev/nuculabs/imagetagger/ai/` path. To build the project run: @@ -53,4 +53,5 @@ You can visit my tech blog at [https://blog.nuculabs.dev](https://blog.nuculabs. # Credits -- Icons: Image analysis icons created by Dewi Sari - Flaticon \ No newline at end of file +- Icons: Image analysis icons + created by Dewi Sari - Flaticon \ No newline at end of file