This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

refactor: object detection experiments

+294 -28
+3
posedetection/build.gradle.kts
··· 103 103 minSdk = 21 104 104 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 105 105 } 106 + buildFeatures { 107 + mlModelBinding = true 108 + } 106 109 } 107 110 108 111 //https://developer.android.com/develop/ui/compose/testing#setup
posedetection/src/androidMain/assets/2.tflite posedetection/src/androidMain/assets/3.tflite
+2 -2
posedetection/src/androidMain/kotlin/com/performancecoachlab/posedetection/objects/createObjectDetector.kt
··· 7 7 8 8 fun createObjectDetector(): ObjectDetector { 9 9 val localModel = LocalModel.Builder() 10 - .setAssetFilePath("2.tflite") 10 + .setAssetFilePath("updated_model_with_metadata_reshape.tflite") 11 11 .build() 12 12 val customObjectDetectorOptions = 13 13 CustomObjectDetectorOptions.Builder(localModel) 14 14 .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE) 15 15 .enableClassification() 16 - .setClassificationConfidenceThreshold(0.5f) 16 + .setClassificationConfidenceThreshold(0.0f) 17 17 .setMaxPerObjectLabelCount(3) 18 18 .build() 19 19 return ObjectDetection.getClient(customObjectDetectorOptions)
+27 -15
posedetection/src/androidMain/kotlin/com/performancecoachlab/posedetection/recording/InputFrame.android.kt
··· 1 1 package com.performancecoachlab.posedetection.recording 2 2 3 3 import android.graphics.Bitmap 4 + import androidx.compose.ui.geometry.Rect 4 5 import androidx.compose.ui.graphics.ImageBitmap 5 6 import androidx.compose.ui.graphics.asImageBitmap 6 7 import com.google.mlkit.vision.common.InputImage 7 8 import com.google.mlkit.vision.pose.PoseDetection 8 9 import com.google.mlkit.vision.pose.defaults.PoseDetectorOptions 10 + import com.performancecoachlab.posedetection.camera.drawAnalysisResults 9 11 import com.performancecoachlab.posedetection.camera.drawSkeleton 10 12 import com.performancecoachlab.posedetection.camera.skeleton 11 13 import com.performancecoachlab.posedetection.objects.createObjectDetector 12 14 import com.performancecoachlab.posedetection.skeleton.Skeleton 13 15 import kotlinx.coroutines.suspendCancellableCoroutine 14 16 import kotlin.coroutines.resume 15 - import org.tensorflow.lite.support.tensorbuffer.TensorBuffer 16 - import org.tensorflow.lite.DataType 17 - import java.nio.ByteBuffer 18 - import java.nio.ByteOrder 19 17 20 18 actual class InputFrame(val bitmap: Bitmap, actual val timestamp: Long) { 21 19 actual fun toImageBitmap(): ImageBitmap { ··· 25 23 actual fun drawSkeleton(skel: Skeleton): ImageBitmap { 26 24 return toImageBitmap().drawSkeleton(skel) 27 25 } 26 + 27 + actual fun drawAnalysisResults(analysisResults: AnalysisResult): ImageBitmap { 28 + return toImageBitmap().drawAnalysisResults(analysisResults) 29 + } 28 30 } 29 31 30 32 actual class FrameAnalyser { ··· 32 34 PoseDetectorOptions.Builder().setDetectorMode(PoseDetectorOptions.STREAM_MODE).build() 33 35 private val poseDetector = PoseDetection.getClient(options) 34 36 private val objectDetector = createObjectDetector() 35 - actual suspend fun analyseFrame(inputFrame: InputFrame): Skeleton? = 37 + actual suspend fun analyseFrame(inputFrame: InputFrame): AnalysisResult = 36 38 suspendCancellableCoroutine { continuation -> 37 39 val img = InputImage.fromBitmap(inputFrame.bitmap, 0) 38 40 var poseResult: Skeleton? = null 39 - var objectResult: Any? = null 41 + var objectResults: List<AnalysisObject>? = null 40 42 var completed = 0 41 43 42 44 fun tryResume() { 43 45 completed++ 44 46 if (completed == 2) { 45 - continuation.resume(poseResult) 47 + continuation.resume(AnalysisResult(poseResult,objectResults?: emptyList())) 46 48 } 47 49 } 48 50 49 51 objectDetector.process(img).addOnSuccessListener { 50 - for (detectedObject in it) { 51 - val boundingBox = detectedObject.boundingBox 52 + objectResults = it.map { detectedObject -> 53 + val boundingBox = detectedObject.boundingBox.let{ bound-> 54 + Rect( 55 + left = bound.left.toFloat(), 56 + top = bound.top.toFloat(), 57 + right = bound.right.toFloat(), 58 + bottom = bound.bottom.toFloat() 59 + ) 60 + } 52 61 val trackingId = detectedObject.trackingId 53 - for (label in detectedObject.labels) { 54 - val text = label.text 55 - val index = label.index 56 - val confidence = label.confidence 57 - println("Detected object: $text, Index: $index, Confidence: $confidence, BoundingBox: $boundingBox, TrackingId: $trackingId") 62 + val labels = detectedObject.labels.map { label -> 63 + label.text 58 64 } 65 + println("Detected object: $labels, bounding box: $boundingBox, trackingId: $trackingId") 66 + AnalysisObject( 67 + boundingBox = boundingBox, 68 + trackingId = trackingId, 69 + labels = labels 70 + ) 59 71 } 60 72 tryResume() 61 73 }.addOnFailureListener { 62 - val e = it 74 + println(it.message) 63 75 tryResume() 64 76 } 65 77
+111 -4
posedetection/src/commonMain/kotlin/com/performancecoachlab/posedetection/camera/Utils.kt
··· 10 10 import androidx.compose.ui.graphics.PaintingStyle.Companion.Fill 11 11 import androidx.compose.ui.graphics.PaintingStyle.Companion.Stroke 12 12 import androidx.compose.ui.graphics.drawscope.CanvasDrawScope 13 + import androidx.compose.ui.graphics.drawscope.Stroke 13 14 import androidx.compose.ui.graphics.rotate 14 15 import androidx.compose.ui.unit.Density 15 16 import androidx.compose.ui.unit.LayoutDirection 17 + import com.performancecoachlab.posedetection.recording.AnalysisResult 16 18 import com.performancecoachlab.posedetection.skeleton.Skeleton 17 19 18 - fun ImageBitmap.drawSkeleton(inskeleton: Skeleton?, rotation: Float = 0f, mirrored: Boolean = false): ImageBitmap { 20 + fun ImageBitmap.drawSkeleton( 21 + inskeleton: Skeleton?, 22 + rotation: Float = 0f, 23 + mirrored: Boolean = false 24 + ): ImageBitmap { 19 25 val skeleton = inskeleton?.let { 20 26 if (mirrored) { 21 27 it.mirror(this.height.toFloat()) ··· 60 66 } 61 67 val paintBlue = Paint().apply { 62 68 color = Color.Blue 63 - strokeWidth = 0.8f*scaledStrokeWidth 69 + strokeWidth = 0.8f * scaledStrokeWidth 70 + style = Fill 71 + } 72 + bones().forEach { line -> 73 + drawLine( 74 + color = paintWhite.color, start = androidx.compose.ui.geometry.Offset( 75 + line.first.x, line.first.y 76 + ), end = androidx.compose.ui.geometry.Offset( 77 + line.second.x, line.second.y 78 + ), strokeWidth = paintWhite.strokeWidth, 79 + blendMode = BlendMode.Softlight 80 + ) 81 + drawLine( 82 + color = paintBlue.color, start = androidx.compose.ui.geometry.Offset( 83 + line.first.x, line.first.y 84 + ), end = androidx.compose.ui.geometry.Offset( 85 + line.second.x, line.second.y 86 + ), strokeWidth = paintBlue.strokeWidth, 87 + blendMode = BlendMode.Color 88 + ) 89 + } 90 + 91 + joints().forEach { joint -> 92 + drawCircle( 93 + brush = Brush.radialGradient( 94 + colors = listOf(Color.Blue, Color.Transparent), 95 + center = androidx.compose.ui.geometry.Offset(joint.x, joint.y), 96 + radius = 1.2f * scaledStrokeWidth 97 + ), 98 + radius = 1.2f * scaledStrokeWidth, 99 + center = androidx.compose.ui.geometry.Offset(joint.x, joint.y) 100 + ) 101 + } 102 + } 103 + } 104 + return bitmap 105 + } 106 + } 107 + 108 + fun ImageBitmap.drawAnalysisResults( 109 + analysisResults: AnalysisResult, 110 + rotation: Float = 0f, 111 + mirrored: Boolean = false 112 + ): ImageBitmap { 113 + val skeleton = analysisResults.skeleton?.let { 114 + if (mirrored) { 115 + it.mirror(this.height.toFloat()) 116 + } else { 117 + it 118 + } 119 + } 120 + also { 121 + val bitmap = ImageBitmap(it.width, it.height) 122 + val canvas = Canvas(bitmap).apply { 123 + if (rotation != 0f) rotate(rotation, it.width.toFloat() / 2f, it.height.toFloat() / 2f) 124 + } 125 + val drawScope = CanvasDrawScope() 126 + val size = Size(it.width.toFloat(), it.height.toFloat()) 127 + 128 + // Calculate scaling factor based on skeleton size 129 + val skeletonSize = skeleton?.joints()?.let { joints -> 130 + val minX = joints.minOfOrNull { joint -> joint.x } ?: 0f 131 + val maxX = joints.maxOfOrNull { joint -> joint.x } ?: 0f 132 + val minY = joints.minOfOrNull { joint -> joint.y } ?: 0f 133 + val maxY = joints.maxOfOrNull { joint -> joint.y } ?: 0f 134 + kotlin.math.max(maxX - minX, maxY - minY) 135 + } ?: 1f 136 + val scaleFactor = skeletonSize / kotlin.math.min(it.width, it.height) 137 + 138 + drawScope.draw( 139 + Density(1f), 140 + LayoutDirection.Ltr, 141 + canvas, 142 + size, 143 + ) { 144 + drawImage(it) 145 + analysisResults.objects.forEach { analysisObject -> 146 + drawRect( 147 + color = Color.Blue, 148 + topLeft = androidx.compose.ui.geometry.Offset( 149 + analysisObject.boundingBox.left, 150 + analysisObject.boundingBox.top 151 + ), 152 + size = Size( 153 + analysisObject.boundingBox.width, 154 + analysisObject.boundingBox.height 155 + ), 156 + style = Stroke(width = 0.4f) 157 + ) 158 + } 159 + skeleton?.apply { 160 + val baseStrokeWidth = 30f 161 + val scaledStrokeWidth = baseStrokeWidth * scaleFactor 162 + 163 + val paintWhite = Paint().apply { 164 + color = Color.White 165 + strokeWidth = scaledStrokeWidth 166 + style = Stroke 167 + } 168 + val paintBlue = Paint().apply { 169 + color = Color.Blue 170 + strokeWidth = 0.8f * scaledStrokeWidth 64 171 style = Fill 65 172 } 66 173 bones().forEach { line -> ··· 87 194 brush = Brush.radialGradient( 88 195 colors = listOf(Color.Blue, Color.Transparent), 89 196 center = androidx.compose.ui.geometry.Offset(joint.x, joint.y), 90 - radius = 1.2f*scaledStrokeWidth 197 + radius = 1.2f * scaledStrokeWidth 91 198 ), 92 - radius = 1.2f*scaledStrokeWidth, 199 + radius = 1.2f * scaledStrokeWidth, 93 200 center = androidx.compose.ui.geometry.Offset(joint.x, joint.y) 94 201 ) 95 202 }
+16 -2
posedetection/src/commonMain/kotlin/com/performancecoachlab/posedetection/recording/InputFrame.kt
··· 1 1 package com.performancecoachlab.posedetection.recording 2 2 3 + import androidx.compose.ui.geometry.Rect 3 4 import androidx.compose.ui.graphics.ImageBitmap 4 5 import com.performancecoachlab.posedetection.skeleton.Skeleton 5 6 6 7 expect class InputFrame { 7 8 fun toImageBitmap(): ImageBitmap 8 9 fun drawSkeleton(skel: Skeleton): ImageBitmap 10 + fun drawAnalysisResults(analysisResults: AnalysisResult): ImageBitmap 11 + 9 12 val timestamp: Long 10 13 } 11 14 12 15 expect class FrameAnalyser(){ 13 - suspend fun analyseFrame(inputFrame: InputFrame): Skeleton? 14 - } 16 + suspend fun analyseFrame(inputFrame: InputFrame): AnalysisResult 17 + } 18 + 19 + data class AnalysisObject( 20 + val boundingBox: Rect, 21 + val trackingId: Int?, 22 + val labels: List<String> 23 + ) 24 + 25 + data class AnalysisResult( 26 + val skeleton: Skeleton?, 27 + val objects: List<AnalysisObject> 28 + )
+83
posedetection/src/iosMain/kotlin/com/performancecoachlab/posedetection/camera/FrameProcessor.kt
··· 1 1 package com.performancecoachlab.posedetection.camera 2 2 3 + import com.performancecoachlab.posedetection.recording.AnalysisObject 3 4 import com.performancecoachlab.posedetection.skeleton.Skeleton 4 5 import kotlinx.cinterop.BetaInteropApi 5 6 import kotlinx.cinterop.CValue ··· 17 18 import platform.CoreGraphics.CGPoint 18 19 import platform.CoreGraphics.CGRect 19 20 import platform.CoreGraphics.CGRectMake 21 + import platform.CoreML.MLModel 22 + import platform.CoreVideo.CVPixelBufferRef 23 + import platform.Foundation.NSBundle 20 24 import platform.Foundation.NSError 21 25 import platform.Vision.VNDetectHumanBodyPoseRequest 22 26 import platform.Vision.VNHumanBodyPoseObservation ··· 37 41 import platform.Vision.VNImageRequestHandler 38 42 import platform.Vision.VNRecognizedPoint 39 43 import platform.Vision.VNRequest 44 + import platform.Foundation.NSProcessInfo 45 + import platform.Foundation.NSUUID 46 + import platform.Vision.* 40 47 41 48 fun bodyPoseHandler(request: VNRequest): MutableMap<VNHumanBodyPoseObservationJointName, VNRecognizedPoint>? { 42 49 try { ··· 115 122 class FrameProcessor() { 116 123 private val skelBuffer = SkelBuffer(maxSize = 10) 117 124 private var regionOfInterest = CGRectMake(0.0, 0.0, 1.0, 1.0) 125 + private var requests = mutableListOf<VNRequest>() 126 + val model = setUpModel() 127 + val objectRecognition = setUpRecognition() 128 + 129 + private fun setUpModel(): VNCoreMLModel? { 130 + val url = NSBundle.mainBundle.URLForResource("YOLOv3", "mlmodelc") 131 + return url?.let { 132 + MLModel.modelWithContentsOfURL(it, null)?.let { model -> 133 + VNCoreMLModel.modelForMLModel(model,null) 134 + } 135 + } 136 + } 137 + private fun setUpRecognition() { 138 + if (model == null) { 139 + return 140 + } 141 + try { 142 + val objectRecognition = VNCoreMLRequest(model) { request, error -> 143 + val results = request?.results as? List<*> ?: return@VNCoreMLRequest 144 + val recognized = results.filterIsInstance<VNRecognizedObjectObservation>() 145 + processResults(recognized) 146 + } 147 + requests = mutableListOf(objectRecognition) 148 + } catch (e: Throwable) { 149 + println("Vision setup error: ${e.message}") 150 + } 151 + } 152 + 153 + fun detectObjects(pixelBuffer: CVPixelBufferRef) { 154 + val handler = VNImageRequestHandler(pixelBuffer, mapOf<Any?, Any?>()) 155 + try { 156 + handler.performRequests(requests, null) 157 + } catch (e: Throwable) { 158 + println("Detection error: ${e.message}") 159 + } 160 + } 161 + 162 + private fun processResults(results: List<VNRecognizedObjectObservation>) { 163 + val detectedObjects = results.map { observation -> 164 + val label = observation.labels.firstOrNull()?.toString()?:"Unknown" 165 + val confidence = observation.confidence 166 + val boundingBox = observation.boundingBox 167 + DetectedObject( 168 + label = label, 169 + confidence = confidence, 170 + boundingBox = boundingBox 171 + ) 172 + } 173 + } 174 + 118 175 @OptIn(BetaInteropApi::class) 119 176 fun analyseFrame(cgImage: CGImageRef?, timestamp: Long, onProccessed: (Skeleton?) -> Unit) { 120 177 autoreleasepool { ··· 206 263 } 207 264 } 208 265 } 266 + 267 + @OptIn(BetaInteropApi::class) 268 + fun analyseFrameForObjects(cgImage: CGImageRef?, timestamp: Long, onProccessed: (List<AnalysisObject>) -> Unit) { 269 + autoreleasepool { 270 + if (cgImage == null) { 271 + onProccessed(emptyList()) 272 + return 273 + } 274 + val width = CGImageGetWidth(cgImage) 275 + val height = CGImageGetHeight(cgImage) 276 + if (width.toUInt() == 0u || height.toUInt() == 0u) { 277 + onProccessed(emptyList()) 278 + return 279 + } 280 + memScoped { 281 + 282 + } 283 + } 284 + 285 + } 209 286 } 210 287 211 288 @OptIn(ExperimentalForeignApi::class) ··· 231 308 } 232 309 233 310 } 311 + data class DetectedObject @OptIn(ExperimentalForeignApi::class) constructor( 312 + val id: String = NSUUID().UUIDString(), 313 + val label: String, 314 + val confidence: Float, 315 + val boundingBox: CValue<CGRect> 316 + ) 234 317 235 318 @OptIn(ExperimentalForeignApi::class) 236 319 @Throws(Throwable::class)
+23 -3
posedetection/src/iosMain/kotlin/com/performancecoachlab/posedetection/recording/InputFrame.ios.kt
··· 11 11 import platform.UIKit.UIImage 12 12 import kotlin.coroutines.resume 13 13 14 - actual class InputFrame @OptIn(ExperimentalForeignApi::class) constructor(val cgImage: CGImageRef, actual val timestamp: Long) { 14 + actual class InputFrame @OptIn(ExperimentalForeignApi::class) constructor( 15 + val cgImage: CGImageRef, actual val timestamp: Long 16 + ) { 15 17 @OptIn(ExperimentalForeignApi::class) 16 18 actual fun toImageBitmap(): ImageBitmap { 17 19 return UIImage.imageWithCGImage(cgImage = cgImage).toImageBitmap() ··· 25 27 26 28 actual class FrameAnalyser actual constructor() { 27 29 private val frameProcessor = FrameProcessor() 30 + 28 31 @OptIn(ExperimentalForeignApi::class) 29 - actual suspend fun analyseFrame(inputFrame: InputFrame): Skeleton? { 32 + actual suspend fun analyseFrame(inputFrame: InputFrame): AnalysisResult { 30 33 val img = inputFrame.cgImage 31 34 return suspendCancellableCoroutine { continuation -> 35 + var poseResult: Skeleton? = null 36 + var objectResults: List<AnalysisObject> = emptyList() 37 + var completed = 0 38 + 39 + fun tryResume() { 40 + completed++ 41 + if (completed == 2) { 42 + continuation.resume(AnalysisResult(poseResult, objectResults ?: emptyList())) 43 + } 44 + } 45 + 32 46 frameProcessor.analyseFrame(img, inputFrame.timestamp, onProccessed = { 33 - continuation.resume(it) 47 + poseResult = it 48 + tryResume() 49 + }) 50 + 51 + frameProcessor.analyseFrameForObjects(img, inputFrame.timestamp, onProccessed = { 52 + objectResults = it 53 + tryResume() 34 54 }) 35 55 } 36 56 }
+6 -2
sample/composeApp/src/commonMain/kotlin/com/nate/posedetection/App.kt
··· 107 107 val coroutineScope = rememberCoroutineScope() 108 108 var currentJob by remember { mutableStateOf<Job?>(null) } 109 109 var image by remember { mutableStateOf<InputFrame?>(null) } 110 - val timeRange = Pair(0L, 4500L) 110 + val timeRange = Pair(0L, 39900L) 111 111 var frame by remember { mutableStateOf(timeRange.first) } 112 112 val frameAnalyser = FrameAnalyser() 113 113 var bitmap by remember { mutableStateOf<ImageBitmap?>(null) } ··· 118 118 try { 119 119 val extractedFrame = extractFrame(url, frame) 120 120 extractedFrame?.let { frame -> 121 - frameAnalyser.analyseFrame(frame)?.let { skel -> 121 + /*frameAnalyser.analyseFrame(frame).skeleton?.let { skel -> 122 122 bitmap = frame.drawSkeleton(skel) 123 + }*/ 124 + frameAnalyser.analyseFrame(frame).let { analysisResults -> 125 + bitmap = frame.drawAnalysisResults(analysisResults) 126 + 123 127 } 124 128 } 125 129 image = extractedFrame
sample/iosApp/FastViTT8F16.mlpackage/Data/com.apple.CoreML/model.mlmodel

This is a binary file and will not be displayed.

sample/iosApp/FastViTT8F16.mlpackage/Data/com.apple.CoreML/weights/weight.bin

This is a binary file and will not be displayed.

+18
sample/iosApp/FastViTT8F16.mlpackage/Manifest.json
··· 1 + { 2 + "fileFormatVersion": "1.0.0", 3 + "itemInfoEntries": { 4 + "76187EC5-87E5-4263-B6E2-1CF5E747A0EE": { 5 + "author": "com.apple.CoreML", 6 + "description": "CoreML Model Weights", 7 + "name": "weights", 8 + "path": "com.apple.CoreML/weights" 9 + }, 10 + "D3756FF7-6CCB-4582-AB58-B91896E60AE4": { 11 + "author": "com.apple.CoreML", 12 + "description": "CoreML Model Specification", 13 + "name": "model.mlmodel", 14 + "path": "com.apple.CoreML/model.mlmodel" 15 + } 16 + }, 17 + "rootModelIdentifier": "D3756FF7-6CCB-4582-AB58-B91896E60AE4" 18 + }
+4
sample/iosApp/iosApp.xcodeproj/project.pbxproj
··· 7 7 objects = { 8 8 9 9 /* Begin PBXBuildFile section */ 10 + 438D2B632DF4C5AC00625680 /* FastViTT8F16.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 438D2B622DF4C5AC00625680 /* FastViTT8F16.mlpackage */; }; 10 11 A93A953B29CC810C00F8E227 /* iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93A953A29CC810C00F8E227 /* iosApp.swift */; }; 11 12 A93A953F29CC810D00F8E227 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A93A953E29CC810D00F8E227 /* Assets.xcassets */; }; 12 13 A93A954229CC810D00F8E227 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A93A954129CC810D00F8E227 /* Preview Assets.xcassets */; }; 13 14 /* End PBXBuildFile section */ 14 15 15 16 /* Begin PBXFileReference section */ 17 + 438D2B622DF4C5AC00625680 /* FastViTT8F16.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = FastViTT8F16.mlpackage; sourceTree = "<group>"; }; 16 18 A93A953729CC810C00F8E227 /* PoseDetection.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PoseDetection.app; sourceTree = BUILT_PRODUCTS_DIR; }; 17 19 A93A953A29CC810C00F8E227 /* iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosApp.swift; sourceTree = "<group>"; }; 18 20 A93A953E29CC810D00F8E227 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; ··· 68 70 C4127409AE3703430489E7BC /* Frameworks */ = { 69 71 isa = PBXGroup; 70 72 children = ( 73 + 438D2B622DF4C5AC00625680 /* FastViTT8F16.mlpackage */, 71 74 ); 72 75 name = Frameworks; 73 76 sourceTree = "<group>"; ··· 164 167 buildActionMask = 2147483647; 165 168 files = ( 166 169 A93A953B29CC810C00F8E227 /* iosApp.swift in Sources */, 170 + 438D2B632DF4C5AC00625680 /* FastViTT8F16.mlpackage in Sources */, 167 171 ); 168 172 runOnlyForDeploymentPostprocessing = 0; 169 173 };
+1
sample/iosApp/iosApp/iosApp.swift
··· 10 10 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 11 11 ) -> Bool { 12 12 window = UIWindow(frame: UIScreen.main.bounds) 13 + 13 14 if let window = window { 14 15 window.rootViewController = MainKt.MainViewController() 15 16 window.makeKeyAndVisible()