···1919 actual companion object {
2020 actual fun getCurrentPlatform(): PlatformType = ANDROID
2121 }
2222-2322}
2424-25232624@OptIn(ExperimentalGetImage::class)
2725fun ImageProxy.process(
2826 objectDetector: org.tensorflow.lite.task.vision.detector.ObjectDetector?,
2927 poseDetector: PoseDetector,
3028 timestamp: Long,
2929+ focusArea: Rect?,
3130 onComplete: (AnalysisResult) -> Unit
3231) {
3333- val tensorImage = TensorImage.fromBitmap(toBitmap())
3434- val mlKitImage = image?.let {
3535- InputImage.fromMediaImage(
3636- it, imageInfo.rotationDegrees
3737- )
3838- }
3232+ val bitmap = toBitmap()
3333+ val tensorImage = TensorImage.fromBitmap(bitmap)
3434+ val mlKitImage = InputImage.fromBitmap(bitmap.applyFocusAreaMask(focusArea,imageInfo.rotationDegrees
3535+ ), imageInfo.rotationDegrees)
3936 process(
4037 tensorImage = tensorImage,
4138 mlKitImage = mlKitImage,
···4845 )
4946}
50474848+private fun Rect?.toGraphicsRect(width: Int, height: Int):android.graphics.Rect {
4949+ return this?.let {
5050+ android.graphics.Rect((it.left*width).toInt(),
5151+ (it.top*height).toInt(),
5252+ (it.right*width).toInt(),
5353+ (it.bottom*height).toInt()
5454+ )
5555+ }?: android.graphics.Rect(0, 0, width, height)
5656+}
5757+5158fun Bitmap.process(
5259 objectDetector: org.tensorflow.lite.task.vision.detector.ObjectDetector?,
5360 poseDetector: PoseDetector,
5461 timestamp: Long,
6262+ focusArea: Rect?,
5563 onComplete: (AnalysisResult) -> Unit
5664) {
5765 val tensorImage = TensorImage.fromBitmap(this)
5858- val mlKitImage = InputImage.fromBitmap(this, 0)
6666+ val mlKitImage = InputImage.fromBitmap(this.applyFocusAreaMask(focusArea), 0)
5967 process(
6068 tensorImage = tensorImage,
6169 mlKitImage = mlKitImage,
···6674 height = height,
6775 onComplete = onComplete
6876 )
7777+}
7878+7979+/**
8080+ * Crops the bitmap to the specified focus area rectangle
8181+ * @param focusArea The rectangle area to crop to (in normalized coordinates 0.0-1.0)
8282+ * @return A new bitmap cropped to the focus area, or the original bitmap if focusArea is null
8383+ */
8484+fun Bitmap.cropToFocusArea(focusArea: Rect?): Bitmap {
8585+ return focusArea?.let { rect ->
8686+ val left = (rect.left * width.toFloat()).toInt().coerceIn(0, width)
8787+ val top = (rect.top * height.toFloat()).toInt().coerceIn(0, height)
8888+ val right = (rect.right * width.toFloat()).toInt().coerceIn(left, width)
8989+ val bottom = (rect.bottom * height.toFloat()).toInt().coerceIn(top, height)
9090+9191+ val cropWidth = right - left
9292+ val cropHeight = bottom - top
9393+9494+ if (cropWidth > 0 && cropHeight > 0) {
9595+ Bitmap.createBitmap(this, left, top, cropWidth, cropHeight)
9696+ } else {
9797+ this
9898+ }
9999+ } ?: this
100100+}
101101+102102+/**
103103+ * Creates a copy of the bitmap with everything outside the focus area blacked out
104104+ * @param focusArea The rectangle area to keep visible (in normalized coordinates 0.0-1.0)
105105+ * @param angle The rotation angle in degrees (must be a multiple of 90) to apply to the focus area rectangle
106106+ * @return A new bitmap with areas outside the focus area blacked out, or the original bitmap if focusArea is null
107107+ */
108108+fun Bitmap.applyFocusAreaMask(focusArea: Rect?, angle: Int = 0): Bitmap {
109109+ return focusArea?.let { rect ->
110110+ val result = this.copy(this.config ?: Bitmap.Config.ARGB_8888, true)
111111+ val canvas = android.graphics.Canvas(result)
112112+ val paint = android.graphics.Paint().apply {
113113+ color = android.graphics.Color.BLACK
114114+ }
115115+116116+ // Transform the rectangle coordinates based on the angle
117117+ val transformedRect = when (angle % 360) {
118118+ 90 -> Rect(
119119+ left = rect.top,
120120+ top = 1f - rect.right,
121121+ right = rect.bottom,
122122+ bottom = 1f - rect.left
123123+ )
124124+ 180 -> Rect(
125125+ left = 1f - rect.right,
126126+ top = 1f - rect.bottom,
127127+ right = 1f - rect.left,
128128+ bottom = 1f - rect.top
129129+ )
130130+ 270 -> Rect(
131131+ left = 1f - rect.bottom,
132132+ top = rect.left,
133133+ right = 1f - rect.top,
134134+ bottom = rect.right
135135+ )
136136+ else -> rect // 0 degrees or any other angle
137137+ }
138138+139139+ val focusRect = transformedRect.toGraphicsRect(width, height)
140140+141141+ // Black out top area
142142+ if (focusRect.top > 0) {
143143+ canvas.drawRect(0f, 0f, width.toFloat(), focusRect.top.toFloat(), paint)
144144+ }
145145+146146+ // Black out bottom area
147147+ if (focusRect.bottom < height) {
148148+ canvas.drawRect(0f, focusRect.bottom.toFloat(), width.toFloat(),height.toFloat(), paint)
149149+ }
150150+151151+ // Black out left area
152152+ if (focusRect.left > 0) {
153153+ canvas.drawRect(0f, focusRect.top.toFloat(), focusRect.left.toFloat(), focusRect.bottom.toFloat(), paint)
154154+ }
155155+156156+ // Black out right area
157157+ if (focusRect.right < width) {
158158+ canvas.drawRect(focusRect.right.toFloat(), focusRect.top.toFloat(), width.toFloat(), focusRect.bottom.toFloat(), paint)
159159+ }
160160+161161+ result
162162+ } ?: this
69163}
7016471165private fun process(