diff --git a/.gitignore b/.gitignore index 01f061a..aedc290 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ -/gradlew -/gradlew.bat -/gradle/ -/.gradle/ -/.idea/ -/build/ -/cache/ -/clientFiles/ -/serverFiles/ +.gradle +.idea +build +cache +clientFiles +serverFiles diff --git a/build.gradle b/examples/build.gradle similarity index 100% rename from build.gradle rename to examples/build.gradle diff --git a/gradle.properties b/examples/gradle.properties similarity index 100% rename from gradle.properties rename to examples/gradle.properties diff --git a/settings.gradle b/examples/settings.gradle similarity index 100% rename from settings.gradle rename to examples/settings.gradle diff --git a/src/main/kotlin/demo/Demo.md b/examples/src/main/kotlin/demo/Demo.md similarity index 100% rename from src/main/kotlin/demo/Demo.md rename to examples/src/main/kotlin/demo/Demo.md diff --git a/src/main/kotlin/demo/imagerecognition/imageRecognition.kt b/examples/src/main/kotlin/demo/imagerecognition/imageRecognition.kt similarity index 100% rename from src/main/kotlin/demo/imagerecognition/imageRecognition.kt rename to examples/src/main/kotlin/demo/imagerecognition/imageRecognition.kt diff --git a/src/main/kotlin/demo/ktor/example_1/client.kt b/examples/src/main/kotlin/demo/ktor/example_1/client.kt similarity index 100% rename from src/main/kotlin/demo/ktor/example_1/client.kt rename to examples/src/main/kotlin/demo/ktor/example_1/client.kt diff --git a/src/main/kotlin/demo/ktor/example_1/server.kt b/examples/src/main/kotlin/demo/ktor/example_1/server.kt similarity index 100% rename from src/main/kotlin/demo/ktor/example_1/server.kt rename to examples/src/main/kotlin/demo/ktor/example_1/server.kt diff --git a/src/main/kotlin/demo/ktor/example_2/client.kt b/examples/src/main/kotlin/demo/ktor/example_2/client.kt similarity index 100% rename from src/main/kotlin/demo/ktor/example_2/client.kt rename to examples/src/main/kotlin/demo/ktor/example_2/client.kt diff --git a/src/main/kotlin/demo/ktor/example_2/server.kt b/examples/src/main/kotlin/demo/ktor/example_2/server.kt similarity index 100% rename from src/main/kotlin/demo/ktor/example_2/server.kt rename to examples/src/main/kotlin/demo/ktor/example_2/server.kt diff --git a/src/main/kotlin/demo/ktor/example_3/server.kt b/examples/src/main/kotlin/demo/ktor/example_3/server.kt similarity index 100% rename from src/main/kotlin/demo/ktor/example_3/server.kt rename to examples/src/main/kotlin/demo/ktor/example_3/server.kt diff --git a/src/main/kotlin/demo/objectdetection/objectDetectionSSD.kt b/examples/src/main/kotlin/demo/objectdetection/objectDetectionSSD.kt similarity index 100% rename from src/main/kotlin/demo/objectdetection/objectDetectionSSD.kt rename to examples/src/main/kotlin/demo/objectdetection/objectDetectionSSD.kt diff --git a/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation.kt b/examples/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation.kt similarity index 100% rename from src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation.kt rename to examples/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation.kt diff --git a/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation2.kt b/examples/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation2.kt similarity index 100% rename from src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation2.kt rename to examples/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation2.kt diff --git a/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation3.kt b/examples/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation3.kt similarity index 100% rename from src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation3.kt rename to examples/src/main/kotlin/demo/objectdetection/objectDetectionSSDWithVisualisation3.kt diff --git a/src/main/kotlin/demo/util.kt b/examples/src/main/kotlin/demo/util.kt similarity index 100% rename from src/main/kotlin/demo/util.kt rename to examples/src/main/kotlin/demo/util.kt diff --git a/src/main/kotlin/outdated/client.kt b/examples/src/main/kotlin/outdated/client.kt similarity index 100% rename from src/main/kotlin/outdated/client.kt rename to examples/src/main/kotlin/outdated/client.kt diff --git a/src/main/kotlin/outdated/client2.kt b/examples/src/main/kotlin/outdated/client2.kt similarity index 100% rename from src/main/kotlin/outdated/client2.kt rename to examples/src/main/kotlin/outdated/client2.kt diff --git a/src/main/kotlin/outdated/example_1_hello_world.kt b/examples/src/main/kotlin/outdated/example_1_hello_world.kt similarity index 100% rename from src/main/kotlin/outdated/example_1_hello_world.kt rename to examples/src/main/kotlin/outdated/example_1_hello_world.kt diff --git a/src/main/kotlin/outdated/main.kt b/examples/src/main/kotlin/outdated/main.kt similarity index 100% rename from src/main/kotlin/outdated/main.kt rename to examples/src/main/kotlin/outdated/main.kt diff --git a/src/main/kotlin/outdated/server.kt b/examples/src/main/kotlin/outdated/server.kt similarity index 100% rename from src/main/kotlin/outdated/server.kt rename to examples/src/main/kotlin/outdated/server.kt diff --git a/src/main/resources/detection/image1.jpg b/examples/src/main/resources/detection/image1.jpg similarity index 100% rename from src/main/resources/detection/image1.jpg rename to examples/src/main/resources/detection/image1.jpg diff --git a/src/main/resources/detection/image2.jpg b/examples/src/main/resources/detection/image2.jpg similarity index 100% rename from src/main/resources/detection/image2.jpg rename to examples/src/main/resources/detection/image2.jpg diff --git a/src/main/resources/detection/image3.jpg b/examples/src/main/resources/detection/image3.jpg similarity index 100% rename from src/main/resources/detection/image3.jpg rename to examples/src/main/resources/detection/image3.jpg diff --git a/src/main/resources/detection/image4.jpg b/examples/src/main/resources/detection/image4.jpg similarity index 100% rename from src/main/resources/detection/image4.jpg rename to examples/src/main/resources/detection/image4.jpg diff --git a/src/main/resources/detection/image5.jpg b/examples/src/main/resources/detection/image5.jpg similarity index 100% rename from src/main/resources/detection/image5.jpg rename to examples/src/main/resources/detection/image5.jpg diff --git a/src/main/resources/recognition/image1.jpg b/examples/src/main/resources/recognition/image1.jpg similarity index 100% rename from src/main/resources/recognition/image1.jpg rename to examples/src/main/resources/recognition/image1.jpg diff --git a/src/main/resources/recognition/image2.jpg b/examples/src/main/resources/recognition/image2.jpg similarity index 100% rename from src/main/resources/recognition/image2.jpg rename to examples/src/main/resources/recognition/image2.jpg diff --git a/src/main/resources/recognition/image3.jpg b/examples/src/main/resources/recognition/image3.jpg similarity index 100% rename from src/main/resources/recognition/image3.jpg rename to examples/src/main/resources/recognition/image3.jpg diff --git a/src/main/resources/recognition/image4.jpg b/examples/src/main/resources/recognition/image4.jpg similarity index 100% rename from src/main/resources/recognition/image4.jpg rename to examples/src/main/resources/recognition/image4.jpg diff --git a/src/main/resources/recognition/image5.jpg b/examples/src/main/resources/recognition/image5.jpg similarity index 100% rename from src/main/resources/recognition/image5.jpg rename to examples/src/main/resources/recognition/image5.jpg diff --git a/src/main/resources/recognition/image6.jpg b/examples/src/main/resources/recognition/image6.jpg similarity index 100% rename from src/main/resources/recognition/image6.jpg rename to examples/src/main/resources/recognition/image6.jpg diff --git a/src/main/resources/recognition/image7.jpg b/examples/src/main/resources/recognition/image7.jpg similarity index 100% rename from src/main/resources/recognition/image7.jpg rename to examples/src/main/resources/recognition/image7.jpg diff --git a/src/main/resources/recognition/image8.jpg b/examples/src/main/resources/recognition/image8.jpg similarity index 100% rename from src/main/resources/recognition/image8.jpg rename to examples/src/main/resources/recognition/image8.jpg diff --git a/objectdetection/build.gradle.kts b/objectdetection/build.gradle.kts new file mode 100644 index 0000000..2c56e22 --- /dev/null +++ b/objectdetection/build.gradle.kts @@ -0,0 +1,40 @@ +import org.jetbrains.compose.compose +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.7.20" + id("org.jetbrains.compose") version "1.2.0" +} + +group = "me.mharakal" +version = "1.0" + +repositories { + google() + mavenCentral() +} + +val kotlindl = "0.4.0" + +dependencies { + implementation(compose.desktop.currentOs) + implementation("org.jetbrains.kotlinx:kotlin-deeplearning-api:$kotlindl") + implementation("org.jetbrains.kotlinx:kotlin-deeplearning-onnx:$kotlindl") + implementation("org.jetbrains.kotlinx:kotlin-deeplearning-visualization:$kotlindl") +} + +tasks.withType() { + kotlinOptions.jvmTarget = "11" +} + +compose.desktop { + application { + mainClass = "MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "objectdetection" + packageVersion = "1.0.0" + } + } +} \ No newline at end of file diff --git a/objectdetection/gradle.properties b/objectdetection/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/objectdetection/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/objectdetection/gradle/wrapper/gradle-wrapper.jar b/objectdetection/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/objectdetection/gradle/wrapper/gradle-wrapper.jar differ diff --git a/objectdetection/gradle/wrapper/gradle-wrapper.properties b/objectdetection/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2e6e589 --- /dev/null +++ b/objectdetection/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/objectdetection/gradlew b/objectdetection/gradlew new file mode 100755 index 0000000..744e882 --- /dev/null +++ b/objectdetection/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/objectdetection/gradlew.bat b/objectdetection/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/objectdetection/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/objectdetection/settings.gradle.kts b/objectdetection/settings.gradle.kts new file mode 100644 index 0000000..f897715 --- /dev/null +++ b/objectdetection/settings.gradle.kts @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + google() + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } + +} +rootProject.name = "objectdetection" + diff --git a/objectdetection/src/image2.png b/objectdetection/src/image2.png new file mode 100644 index 0000000..c0a4aec Binary files /dev/null and b/objectdetection/src/image2.png differ diff --git a/objectdetection/src/main/kotlin/Main.kt b/objectdetection/src/main/kotlin/Main.kt new file mode 100644 index 0000000..0692d92 --- /dev/null +++ b/objectdetection/src/main/kotlin/Main.kt @@ -0,0 +1,120 @@ +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.res.loadImageBitmap +import androidx.compose.ui.res.useResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.WindowState +import androidx.compose.ui.window.singleWindowApplication +import widget.ImageWithDetectedObjects +import java.awt.Container +import java.awt.datatransfer.DataFlavor +import java.awt.dnd.DnDConstants +import java.awt.dnd.DropTarget +import java.awt.dnd.DropTargetDropEvent +import java.io.File + + +@Composable +@Preview +fun App(dropTarget: Container, viewModel: ObjectDetectionViewModel = ObjectDetectionViewModel()) { + val imageName = "detection/image2.png" + + val viewState by viewModel.uiState.collectAsState( + ObjectDetectionUiState.Beginning(), + viewModel.viewModelScope.coroutineContext + ) + + LaunchedEffect(true) { + viewModel.detect(getFileFromResourceAsStream(imageName)) + } + + val name = remember { mutableStateOf(TextFieldValue(System.getProperty("user.home"))) } + + MaterialTheme { + Row(Modifier.onGloballyPositioned { coordinates -> + // This will be the size of the Column. + print("Windows size ${coordinates.size}") + }) { + Column { + TextField( + value = name.value, + onValueChange = { name.value = it }, + singleLine = true, + placeholder = { + Text("..") + }, + keyboardOptions = KeyboardOptions( + autoCorrect = false, + keyboardType = KeyboardType.Uri, + capitalization = KeyboardCapitalization.None, + imeAction = ImeAction.Search + ), + keyboardActions = KeyboardActions(onAny = { + + }) + ) + ImageWithDetectedObjects( + loadImageBitmap(File("/Users/mharakal/projects/private/ds/ObjectDetectionMH/objectdetection/src/image2.png").inputStream()), + viewState.detectionState, + viewState.detectedObjects, + modifier = Modifier.heightIn(min = 100.dp, max = 500.dp) + ) + val text = when (viewState.detectionState) { + DetectionUiStateType.BEGINNING -> "Starting" + DetectionUiStateType.LOADING -> "Loading" + DetectionUiStateType.DETECTING -> "Detecting" + DetectionUiStateType.DONE -> "Done" + } + Text(text) + } + } + } +} + +val target = object : DropTarget() { + @Synchronized + override fun drop(evt: DropTargetDropEvent) { + try { + evt.acceptDrop(DnDConstants.ACTION_REFERENCE) + val droppedFiles = evt + .transferable.getTransferData( + DataFlavor.javaFileListFlavor + ) as List<*> + droppedFiles.first()?.let { + //name.value = TextFieldValue((it as File).absolutePath) + } + } catch (ex: Exception) { + ex.printStackTrace() + } + } +} +//dropTarget.dropTarget = target +//} + + +fun main() = singleWindowApplication( + title = "Object detection", + state = WindowState(width = 1280.dp, height = 768.dp), + icon = BitmapPainter(useResource("ic_launcher.png", ::loadImageBitmap)), +) { + + App(window.contentPane) +} diff --git a/objectdetection/src/main/kotlin/ObjectDetection.kt b/objectdetection/src/main/kotlin/ObjectDetection.kt new file mode 100644 index 0000000..a1d5860 --- /dev/null +++ b/objectdetection/src/main/kotlin/ObjectDetection.kt @@ -0,0 +1,73 @@ +import org.jetbrains.kotlinx.dl.api.inference.loaders.ONNXModelHub +import org.jetbrains.kotlinx.dl.api.inference.objectdetection.DetectedObject +import org.jetbrains.kotlinx.dl.api.inference.onnx.ONNXModels +import org.jetbrains.kotlinx.dl.dataset.image.ColorMode +import org.jetbrains.kotlinx.dl.dataset.preprocessor.* +import org.jetbrains.kotlinx.dl.dataset.preprocessor.image.convert +import org.jetbrains.kotlinx.dl.dataset.preprocessor.image.resize +import java.io.File +import java.io.InputStream +import kotlin.io.path.createTempFile + + +class ObjectDetection(private val cacheDirectory: String = "cache/pretrainedModels") { + + fun objectDetection(imageFile: InputStream): List { + val modelHub = + ONNXModelHub(cacheDirectory = File(cacheDirectory)) + val model = ONNXModels.ObjectDetection.SSD.pretrainedModel(modelHub) + val modelType = ONNXModels.ObjectDetection.SSD + + + model.use { detectionModel -> + + val preprocessing: Preprocessing = preprocess { + transformImage { + resize { + outputHeight = 1200 + outputWidth = 1200 + } + convert { colorMode = ColorMode.BGR } + } + } + + + //val image = imageFile.use { inputStream -> ImageConverter.toBufferedImage(inputStream) } + + val file: File = createTempFile().toFile() + + imageFile.use { input -> + file.outputStream().use { output -> + input.copyTo(output) + } + } + + + val inputData = modelType.preprocessInput( + file, + preprocessing + ) + + + val start = System.currentTimeMillis() + val yhat = detectionModel.predictRaw(inputData)// { output -> output.getFloatArray(0) } + val end = System.currentTimeMillis() + println("Prediction took ${end - start} ms") + + val detectedObjects = + detectionModel.detectObjects(imageFile = file, topK = 20) + + detectedObjects.forEach { + println("Found ${it.classLabel} with probability ${it.probability}") + } + + + val a = yhat.values.toTypedArray() + + println(yhat.values.toTypedArray().contentDeepToString()) + + return detectedObjects + } + } + +} \ No newline at end of file diff --git a/objectdetection/src/main/kotlin/ObjectDetectionViewModel.kt b/objectdetection/src/main/kotlin/ObjectDetectionViewModel.kt new file mode 100644 index 0000000..9321963 --- /dev/null +++ b/objectdetection/src/main/kotlin/ObjectDetectionViewModel.kt @@ -0,0 +1,82 @@ +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import org.jetbrains.kotlinx.dl.api.inference.objectdetection.DetectedObject +import java.io.File +import java.io.InputStream + +sealed interface ObjectDetectionUiState { + + val detectionState: DetectionUiStateType + val detectedObjects: List + + data class Beginning( + override val detectionState: DetectionUiStateType = DetectionUiStateType.BEGINNING, + override val detectedObjects: List = emptyList(), + ) : ObjectDetectionUiState + + data class Loading( + override val detectionState: DetectionUiStateType = DetectionUiStateType.LOADING, + override val detectedObjects: List = emptyList(), + ) : ObjectDetectionUiState + + data class Detecting( + override val detectionState: DetectionUiStateType = DetectionUiStateType.DETECTING, + override val detectedObjects: List = emptyList(), + ) : ObjectDetectionUiState + + data class Detected( + override val detectionState: DetectionUiStateType = DetectionUiStateType.DONE, + override val detectedObjects: List, + ) : ObjectDetectionUiState +} + +enum class DetectionUiStateType { + BEGINNING, + LOADING, + DETECTING, + DONE +} + +data class ObjectDetectionState( + val detectedObjects: List = emptyList(), + val stateType: DetectionUiStateType = DetectionUiStateType.BEGINNING +) { + + fun toUiState(): ObjectDetectionUiState = when (stateType) { + DetectionUiStateType.BEGINNING -> ObjectDetectionUiState.Beginning() + DetectionUiStateType.LOADING -> ObjectDetectionUiState.Loading() + DetectionUiStateType.DETECTING -> ObjectDetectionUiState.Detecting() + DetectionUiStateType.DONE -> ObjectDetectionUiState.Detected(detectedObjects = detectedObjects) + } + +} + +class ObjectDetectionViewModel { + + val viewModelScope: CoroutineScope = GlobalScope + + private val viewModelState = MutableStateFlow(ObjectDetectionState()) + + // UI state exposed to the UI + val uiState = viewModelState + .map { it.toUiState() } + .stateIn( + viewModelScope, + SharingStarted.Eagerly, + viewModelState.value.toUiState() + ) + + suspend fun detect(file: InputStream) { + val detector = ObjectDetection() + viewModelState.value = ObjectDetectionState(emptyList(), DetectionUiStateType.DETECTING) + viewModelScope.launch { + withContext(Dispatchers.IO) { + val detectedObject = detector.objectDetection(file) + viewModelState.value = ObjectDetectionState(detectedObject, DetectionUiStateType.DONE) + } + } + } +} \ No newline at end of file diff --git a/objectdetection/src/main/kotlin/utils.kt b/objectdetection/src/main/kotlin/utils.kt new file mode 100644 index 0000000..9e5f624 --- /dev/null +++ b/objectdetection/src/main/kotlin/utils.kt @@ -0,0 +1,23 @@ +import java.io.File +import java.io.InputStream +import java.net.URISyntaxException +import java.net.URL + + +@Throws(URISyntaxException::class) +fun getFileFromResource(fileName: String): File { + val classLoader: ClassLoader = object {}.javaClass.classLoader + val resource: URL? = classLoader.getResource(fileName) + return if (resource == null) { + throw IllegalArgumentException("File not found! $fileName") + } else { + File(resource.toURI()) + } +} + +fun getFileFromResourceAsStream(fileName: String): InputStream { + + // The class loader that loaded the class + val classLoader: ClassLoader = object {}.javaClass.classLoader + return classLoader.getResourceAsStream(fileName) +} diff --git a/objectdetection/src/main/kotlin/widget/ImageWithDetectedObjects.kt b/objectdetection/src/main/kotlin/widget/ImageWithDetectedObjects.kt new file mode 100644 index 0000000..e52096a --- /dev/null +++ b/objectdetection/src/main/kotlin/widget/ImageWithDetectedObjects.kt @@ -0,0 +1,122 @@ +package widget + +import DetectionUiStateType +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.toSize +import org.jetbrains.kotlinx.dl.api.inference.objectdetection.DetectedObject +import kotlin.math.roundToInt + + +@Composable +@Preview +fun ImageWithDetectedObjects( + image: ImageBitmap, + state: DetectionUiStateType, + objects: List, + modifier: Modifier = Modifier +) { + Column(modifier = modifier) { + val customPainter = + OverlayImagePainter( + image, + objects, + ) + + + Box { + Image( + contentDescription = null, + contentScale = ContentScale.Fit, + painter = customPainter + ) + } + if (state != DetectionUiStateType.DONE) { + LinearProgressIndicator() + } + } +} + +class OverlayImagePainter constructor( + private val image: ImageBitmap, + private val objects: List, + private val srcOffset: IntOffset = IntOffset.Zero, + private val srcSize: IntSize = IntSize(image.width, image.height), +) : Painter() { + + private val size: IntSize = validateSize(srcOffset, srcSize) + override fun DrawScope.onDraw() { + print("Real size ${size}") + // draw the first image without any blend mode + drawImage( + image, + srcOffset, + srcSize, + dstSize = IntSize( + this@onDraw.size.width.roundToInt(), + this@onDraw.size.height.roundToInt() + ) + ) + + objects + .filter { it.probability > 0.5 } + .forEach { obj -> + val color = when (obj.classLabel) { + "dog" -> Color.Blue + "car" -> Color.Green + "clock" -> Color.Yellow + "bicycle" -> Color.Magenta + "person" -> Color.Red + else -> Color.White + } + val top = obj.yMin * size.height + val left = obj.xMin * size.width + val bottom = obj.yMax * size.height + val right = obj.xMax * size.width + + if (color != Color.White) { + + drawLine(color, Offset(left, top), Offset(right, top)) + drawLine(color, Offset(right, top), Offset(right, bottom)) + drawLine(color, Offset(right, bottom), Offset(left, bottom)) + drawLine(color, Offset(left, bottom), Offset(left, top)) + } + } + } + + /** + * Return the dimension of the underlying [ImageBitmap] as it's intrinsic width and height + */ + override val intrinsicSize: Size get() = size.toSize() + + private fun validateSize(srcOffset: IntOffset, srcSize: IntSize): IntSize { + require( + srcOffset.x >= 0 && + srcOffset.y >= 0 && + srcSize.width >= 0 && + srcSize.height >= 0 && + srcSize.width <= image.width && + srcSize.height <= image.height + ) + return srcSize + } +} + diff --git a/objectdetection/src/main/resources/detection/image2.png b/objectdetection/src/main/resources/detection/image2.png new file mode 100644 index 0000000..c0a4aec Binary files /dev/null and b/objectdetection/src/main/resources/detection/image2.png differ diff --git a/objectdetection/src/main/resources/detection/timmy.jpeg b/objectdetection/src/main/resources/detection/timmy.jpeg new file mode 100644 index 0000000..9e3344a Binary files /dev/null and b/objectdetection/src/main/resources/detection/timmy.jpeg differ diff --git a/objectdetection/src/main/resources/ic_launcher.png b/objectdetection/src/main/resources/ic_launcher.png new file mode 100644 index 0000000..db540cf Binary files /dev/null and b/objectdetection/src/main/resources/ic_launcher.png differ diff --git a/objectdetection/src/timmy.jpeg b/objectdetection/src/timmy.jpeg new file mode 100644 index 0000000..9e3344a Binary files /dev/null and b/objectdetection/src/timmy.jpeg differ