add module-info.java

This commit is contained in:
Denis-Cosmin NUTIU 2024-03-30 13:08:15 +02:00
parent 3b717d3861
commit ccd463d3f5
13 changed files with 84 additions and 2011 deletions

View file

@ -4,16 +4,19 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="dac8f85c-5bd6-4e05-9520-9e6a91c7f78e" name="Changes" comment="update gradle config">
<change afterPath="$PROJECT_DIR$/img-ai/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/img-ai/src/main/kotlin/ImageTagsPrediction.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/img-ai/src/main/resources/dev/nuculabs/imagetagger/ai/prediction_categories.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/img-ai/src/test/kotlin/ImageTagsPredictionTests.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/img-ai/src/test/resources/dev/nuculabs/imagetagger/ai/timisoara-bega.jpg" afterDir="false" />
<change afterPath="$PROJECT_DIR$/img-ai/src/test/resources/dev/nuculabs/imagetagger/ai/timisoara-threes.jpg" afterDir="false" />
<change afterPath="$PROJECT_DIR$/img-ai/src/test/resources/dev/nuculabs/imagetagger/ai/timisoara-water-tower.jpg" afterDir="false" />
<list default="true" id="dac8f85c-5bd6-4e05-9520-9e6a91c7f78e" name="Changes" comment="extract common ai-related functionality into img-ai">
<change afterPath="$PROJECT_DIR$/img-ai/src/main/java/module-info.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/img-ai/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/img-ai/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/img-ai/src/main/kotlin/ImageTagsPrediction.kt" beforeDir="false" afterPath="$PROJECT_DIR$/img-ai/src/main/kotlin/dev/nuculabs/imagetagger/ai/ImageTagsPrediction.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/img-ui/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/main/java/module-info.java" beforeDir="false" afterPath="$PROJECT_DIR$/img-ui/src/main/java/module-info.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/main/kotlin/dev/nuculabs/imagetagger/ai/ImageTagsPrediction.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/main/resources/dev/nuculabs/imagetagger/ai/prediction_categories.txt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/test/kotlin/ai/ImageTagsPredictionTests.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/test/resources/ai/timisoara-bega.jpg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/test/resources/ai/timisoara-threes.jpg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/img-ui/src/test/resources/ai/timisoara-water-tower.jpg" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/settings.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/settings.gradle" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@ -54,20 +57,20 @@
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="ImageTagger-Solution" type="f1a62948:ProjectNode" />
<item name="img-ai" type="2d1252cf:ModuleNode" />
<item name="img-ui" type="2d1252cf:ModuleNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="ImageTagger-Solution" type="f1a62948:ProjectNode" />
<item name="img-ai" type="2d1252cf:ModuleNode" />
<item name="img-ui" type="2d1252cf:ModuleNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="ImageTagger-Solution" type="f1a62948:ProjectNode" />
<item name="img-ai" type="2d1252cf:ModuleNode" />
<item name="img-ui" type="2d1252cf:ModuleNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
<item name="verification" type="c8890929:TasksNode$1" />
<item name="distribution" type="c8890929:TasksNode$1" />
</path>
</expand>
<select />
@ -76,6 +79,14 @@
</state>
</system>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
<option value="module-info" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
@ -94,7 +105,11 @@
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Gradle.Build img-ui.executor": "Run",
"Gradle.ImageTagger [build].executor": "Run",
"Gradle.ImageTagger:img-ai [test].executor": "Run",
"Gradle.ImageTagger:img-ui [clean].executor": "Run",
"Gradle.ImageTagger:img-ui [jlinkZip].executor": "Run",
"Gradle.ImageTagsPredictionTests.executor": "Run",
"Gradle.img-ui [build].executor": "Run",
"Gradle.img-ui [run].executor": "Run",
"Kotlin.MainPageKt.executor": "Debug",
@ -128,15 +143,18 @@
<recent name="$PROJECT_DIR$/img-ai/src/main/resources/dev/nuculabs/imagetagger/ai" />
<recent name="$PROJECT_DIR$" />
</key>
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY">
<recent name="dev.nuculabs.imagetagger.ai" />
</key>
<key name="CopyKotlinDeclarationDialog.RECENTS_KEY">
<recent name="" />
</key>
</component>
<component name="RunManager" selected="Gradle.ImageTagsPredictionTests">
<configuration name="ImageTagger:img-ai [test]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<component name="RunManager" selected="Gradle.ImageTagger:img-ui [jlinkZip]">
<configuration name="ImageTagger:img-ui [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/img-ai" />
<option name="externalProjectPath" value="$PROJECT_DIR$/img-ui" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
@ -144,7 +162,29 @@
</option>
<option name="taskNames">
<list>
<option value="test" />
<option value="clean" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="ImageTagger:img-ui [jlinkZip]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/img-ui" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="jlinkZip" />
</list>
</option>
<option name="vmOptions" />
@ -225,9 +265,10 @@
</configuration>
<recent_temporary>
<list>
<item itemvalue="Gradle.ImageTagsPredictionTests" />
<item itemvalue="Gradle.ImageTagger:img-ai [test]" />
<item itemvalue="Gradle.ImageTagger:img-ui [jlinkZip]" />
<item itemvalue="Gradle.ImageTagger:img-ui [clean]" />
<item itemvalue="Gradle.img-ui [run]" />
<item itemvalue="Gradle.ImageTagsPredictionTests" />
<item itemvalue="Gradle.img-ui [build]" />
</list>
</recent_temporary>
@ -249,7 +290,7 @@
<option name="presentableId" value="Default" />
<updated>1711789328334</updated>
<workItem from="1711789330084" duration="2109000" />
<workItem from="1711791444105" duration="1766000" />
<workItem from="1711791444105" duration="4216000" />
</task>
<task id="LOCAL-00001" summary="split ImageTagger into subproject img-ui">
<option name="closed" value="true" />
@ -275,7 +316,15 @@
<option name="project" value="LOCAL" />
<updated>1711791812195</updated>
</task>
<option name="localTasksCounter" value="4" />
<task id="LOCAL-00004" summary="extract common ai-related functionality into img-ai">
<option name="closed" value="true" />
<created>1711793501074</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1711793501074</updated>
</task>
<option name="localTasksCounter" value="5" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -285,7 +334,8 @@
<MESSAGE value="split ImageTagger into subproject img-ui" />
<MESSAGE value="update readme.md" />
<MESSAGE value="update gradle config" />
<option name="LAST_COMMIT_MESSAGE" value="update gradle config" />
<MESSAGE value="extract common ai-related functionality into img-ai" />
<option name="LAST_COMMIT_MESSAGE" value="extract common ai-related functionality into img-ai" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>

View file

@ -1,5 +1,8 @@
plugins {
id("java")
kotlin("jvm") version "1.8.22"
id("org.javamodularity.moduleplugin") version "1.8.12"
}
group = "dev.nuculabs.imagetagger.ai"

View file

@ -0,0 +1,7 @@
module dev.nuculabs.imagetagger.ai {
requires com.microsoft.onnxruntime;
requires java.desktop;
requires java.logging;
requires kotlin.stdlib;
exports dev.nuculabs.imagetagger.ai;
}

View file

@ -37,6 +37,7 @@ javafx {
}
dependencies {
implementation(project(":img-ai"))
implementation('org.controlsfx:controlsfx:11.1.2')
implementation('com.dlsc.formsfx:formsfx-core:11.6.0') {
exclude(group: 'org.openjfx')
@ -46,7 +47,6 @@ dependencies {
}
implementation('org.kordamp.ikonli:ikonli-javafx:12.3.1')
implementation('org.kordamp.ikonli:ikonli-fontawesome5-pack:12.3.1')
implementation('com.microsoft.onnxruntime:onnxruntime:1.17.1') // delete
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}")

View file

@ -7,13 +7,13 @@ module dev.nuculabs.imagetagger.ui {
requires org.controlsfx.controls;
requires com.dlsc.formsfx;
requires net.synedra.validatorfx;
requires com.microsoft.onnxruntime;
requires java.logging;
requires java.desktop;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.core;
requires org.kordamp.ikonli.fontawesome5;
requires kotlinx.coroutines.core;
requires dev.nuculabs.imagetagger.ai;
opens dev.nuculabs.imagetagger.ui to javafx.fxml, javafx.graphics;
opens dev.nuculabs.imagetagger.ui.controls to javafx.fxml, javafx.graphics;

View file

@ -1,173 +0,0 @@
package dev.nuculabs.imagetagger.ai
import ai.onnxruntime.OnnxTensor
import ai.onnxruntime.OrtEnvironment
import ai.onnxruntime.OrtSession
import java.awt.image.BufferedImage
import java.io.IOException
import java.io.InputStream
import java.util.logging.Logger
import javax.imageio.ImageIO
/**
* ImageTagsPrediction is a specialized class that predicts an Image's tags
*/
class ImageTagsPrediction {
private val logger: Logger = Logger.getLogger("InfoLogging")
private var ortEnv: OrtEnvironment = OrtEnvironment.getEnvironment()
private var ortSession: OrtSession
private var modelClasses: MutableList<String> = mutableListOf()
init {
try {
logger.info("Loading ML model. Please wait.")
ImageTagsPrediction::class.java.getResourceAsStream("/dev/nuculabs/imagetagger/ai/prediction.onnx").let { modelFile ->
ortSession = ortEnv.createSession(
modelFile!!.readBytes(),
OrtSession.SessionOptions()
)
}
ImageTagsPrediction::class.java.getResourceAsStream("/dev/nuculabs/imagetagger/ai/prediction_categories.txt")
.let { classesFile ->
modelClasses.addAll(0, classesFile!!.bufferedReader().readLines())
}
logger.info("Loaded ${modelClasses.size} model classes.")
} catch (e: NullPointerException) {
logger.severe(
"Failed to load model file or categories file. If you're building the project from " +
"source, please follow the instructions from the README.md: " +
"https://github.com/dnutiu/ImageTagger." +
"Exception ${e.message}"
)
throw e
}
}
/**
* Processes an image into an ONNX Tensor.
*/
private fun processImage(bufferedImage: BufferedImage): Array<Array<Array<FloatArray>>> {
try {
val tensorData = Array(1) {
Array(3) {
Array(224) {
FloatArray(224)
}
}
}
val mean = floatArrayOf(0.485f, 0.456f, 0.406f)
val standardDeviation = floatArrayOf(0.229f, 0.224f, 0.225f)
// crop image to 224x224
var width: Int = bufferedImage.width
var height: Int = bufferedImage.height
var startX = 0
var startY = 0
if (width > height) {
startX = (width - height) / 2
width = height
} else {
startY = (height - width) / 2
height = width
}
val image = bufferedImage.getSubimage(startX, startY, width, height)
val resizedImage = image.getScaledInstance(224, 224, 4)
val scaledImage = BufferedImage(224, 224, BufferedImage.TYPE_4BYTE_ABGR)
scaledImage.graphics.drawImage(resizedImage, 0, 0, null)
// Process image
for (y in 0 until scaledImage.height) {
for (x in 0 until scaledImage.width) {
val pixel: Int = scaledImage.getRGB(x, y)
// Get RGB values
tensorData[0][0][y][x] =
((pixel shr 16 and 0xFF) / 255f - mean[0]) / standardDeviation[0]
tensorData[0][1][y][x] =
((pixel shr 16 and 0xFF) / 255f - mean[1]) / standardDeviation[1]
tensorData[0][2][y][x] =
((pixel shr 16 and 0xFF) / 255f - mean[2]) / standardDeviation[2]
}
}
return tensorData
} catch (e: IOException) {
throw RuntimeException(e)
}
}
/**
* Uses the ML model to predict tags for a given bitmap.
*/
@Suppress("UNCHECKED_CAST")
private fun predictTagsInternal(bufferedImage: BufferedImage): List<String> {
// 1. Get input and output names
val inputName: String = ortSession.inputNames.iterator().next()
val outputName: String = ortSession.outputNames.iterator().next()
// 2. Create input tensor
val inputTensor = OnnxTensor.createTensor(ortEnv, processImage(bufferedImage))
// 3. Run the model.
val inputs = mapOf(inputName to inputTensor)
val results = ortSession.run(inputs)
// 4. Get output tensor
val outputTensor = results.get(outputName)
if (outputTensor.isPresent) {
// 5. Get prediction results
val floatBuffer = outputTensor.get().value as Array<FloatArray>
val predictions = ArrayList<String>()
// filter buffer by threshold
for (i in floatBuffer[0].indices) {
if (floatBuffer[0][i] > -0.5) {
predictions.add(modelClasses[i])
}
}
return predictions
} else {
return ArrayList()
}
}
/**
* Predicts tags for a Bitmap.
*/
fun predictTags(image: BufferedImage): List<String> {
return predictTagsInternal(image)
}
/**
* Predicts tags for a given image input stream.
*/
fun predictTags(input: InputStream?): List<String> {
if (input == null) {
return ArrayList()
}
return predictTagsInternal(ImageIO.read(input))
}
/**
* Close the session and environment.
*/
fun close() {
ortSession.close()
ortEnv.close()
modelClasses.clear()
}
// Singleton Pattern
companion object {
@Volatile
private var instance: ImageTagsPrediction? = null
fun getInstance() =
instance ?: synchronized(this) {
instance ?: ImageTagsPrediction().also { instance = it }
}
}
}

View file

@ -1,106 +0,0 @@
package ai
import dev.nuculabs.imagetagger.ai.ImageTagsPrediction
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.io.File
import javax.imageio.ImageIO
class ImageTagsPredictionTests {
private val imageTagsPrediction: ImageTagsPrediction = ImageTagsPrediction.getInstance()
@Test
fun testPredictTagsForBufferedImage_TimisoaraBega() {
val timisoaraBega = ImageTagsPredictionTests::class.java.getResource("timisoara-bega.jpg")
val tags = imageTagsPrediction.predictTags(ImageIO.read(timisoaraBega))
assertEquals(
listOf(
"lake",
"nature",
"no people",
"outdoors",
"reflection",
"river",
"sky",
"tranquil",
"tree",
"water"
), tags
)
}
@Test
fun testPredictTagsForBufferedImage_TimisoaraThrees() {
val timisoaraBega = ImageTagsPredictionTests::class.java.getResource("timisoara-threes.jpg")
val tags = imageTagsPrediction.predictTags(ImageIO.read(timisoaraBega))
assertEquals(
listOf("day", "forest", "growth", "nature", "no people", "outdoors", "plant", "tree"), tags
)
}
@Test
fun testPredictTagsForBufferedImage_TimisoaraWaterTower() {
val timisoaraBega = ImageTagsPredictionTests::class.java.getResource("timisoara-water-tower.jpg")
val tags = imageTagsPrediction.predictTags(ImageIO.read(timisoaraBega))
assertEquals(
listOf(
"architecture",
"building exterior",
"built structure",
"day",
"history",
"no people",
"outdoors",
"travel destinations"
), tags
)
}
@Test
fun testPredictTagsForInputStream_TimisoaraBega() {
val image = ImageTagsPredictionTests::class.java.getResource("timisoara-bega.jpg")
val tags = imageTagsPrediction.predictTags(File(image!!.toURI()).inputStream())
assertEquals(
listOf(
"lake",
"nature",
"no people",
"outdoors",
"reflection",
"river",
"sky",
"tranquil",
"tree",
"water"
), tags
)
}
@Test
fun testPredictTagsForInputStream__TimisoaraThrees() {
val image = ImageTagsPredictionTests::class.java.getResource("timisoara-threes.jpg")
val tags = imageTagsPrediction.predictTags(File(image!!.toURI()).inputStream())
assertEquals(
listOf("day", "forest", "growth", "nature", "no people", "outdoors", "plant", "tree"), tags
)
}
@Test
fun testPredictTagsForInputStream_TimisoaraWaterTower() {
val image = ImageTagsPredictionTests::class.java.getResource("timisoara-water-tower.jpg")
val tags = imageTagsPrediction.predictTags(File(image!!.toURI()).inputStream())
assertEquals(
listOf(
"architecture",
"building exterior",
"built structure",
"day",
"history",
"no people",
"outdoors",
"travel destinations"
), tags
)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -2,6 +2,6 @@ plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
}
rootProject.name = "ImageTagger-Solution"
include("img-ui")
include "img-ui"
include 'img-ai'