You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin应用:图片背景红点检测及组件定位添加技术咨询

Solving Red Dot Detection & Dynamic Component Placement in Kotlin Android

Hey there! Let's walk through how to tackle this problem—detecting red dots on a blueprint background and placing buttons/text views right on top of them. Here's a practical, step-by-step approach tailored for your Android app:

1. Load & Preprocess the Background Image

First, you need to get the admin-uploaded image into a Bitmap (since we’ll analyze its pixels directly). You can load it from a gallery Uri or app resources, and resize large bitmaps to speed up processing—no need to scan every pixel of a 4K image if your layout is smaller.

Example of loading a Bitmap from a Uri:

fun loadBitmapFromUri(context: Context, uri: Uri): Bitmap? {
    return try {
        val inputStream = context.contentResolver.openInputStream(uri)
        BitmapFactory.decodeStream(inputStream)
    } catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

2. Detect Red Dot Positions

The core task is identifying which pixels belong to red dots, then finding their center points (to avoid placing multiple components on the same dot).

Key Steps:

  • Color Thresholding: Define ranges for red, green, blue values that match your dots. Bright red dots typically have high red values and low green/blue values—tweak these numbers to match your blueprint.
  • Avoid Duplicates: Since dots are clusters of pixels, only record a center if it’s not too close to an already recorded point.
  • Coordinate Conversion: Convert bitmap pixel coordinates to your layout’s screen coordinates (the bitmap’s size might not match the layout’s size).

Example code for detection & conversion:

fun detectRedDotsAndPlaceComponents(backgroundBitmap: Bitmap, layout: ViewGroup) {
    val layoutWidth = layout.width.toFloat()
    val layoutHeight = layout.height.toFloat()
    val bitmapWidth = backgroundBitmap.width.toFloat()
    val bitmapHeight = backgroundBitmap.height.toFloat()

    // Adjust these values to match your red dots' actual color
    val minRed = 200
    val maxGreen = 60
    val maxBlue = 60

    val redDotCenters = mutableListOf<Pair<Float, Float>>()

    // Sample every 4 pixels to speed up processing (adjust based on dot size)
    for (y in 0 until backgroundBitmap.height step 4) {
        for (x in 0 until backgroundBitmap.width step 4) {
            val pixel = backgroundBitmap.getPixel(x, y)
            val red = Color.red(pixel)
            val green = Color.green(pixel)
            val blue = Color.blue(pixel)

            if (red >= minRed && green <= maxGreen && blue <= maxBlue) {
                val centerX = x.toFloat()
                val centerY = y.toFloat()

                // Only add if this center isn't too close to an existing one
                val isUnique = redDotCenters.none { existing ->
                    Math.abs(existing.first - centerX) < 20 && Math.abs(existing.second - centerY) < 20
                }
                if (isUnique) {
                    redDotCenters.add(centerX to centerY)
                }
            }
        }
    }

    // Convert bitmap coordinates to layout coordinates and add components
    redDotCenters.forEach { (bitmapX, bitmapY) ->
        val screenX = (bitmapX / bitmapWidth) * layoutWidth
        val screenY = (bitmapY / bitmapHeight) * layoutHeight

        // Create a Button (replace with TextView or other components as needed)
        val button = Button(layout.context)
        button.text = "Tap Me"
        button.setPadding(16, 8, 16, 8)
        button.setBackgroundColor(Color.parseColor("#FF4081"))

        // Set layout params to center the button on the red dot
        when (layout) {
            is RelativeLayout -> {
                val params = RelativeLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
                )
                params.leftMargin = (screenX - button.width / 2).toInt()
                params.topMargin = (screenY - button.height / 2).toInt()
                layout.addView(button, params)
            }
            is ConstraintLayout -> {
                val constraintSet = ConstraintSet()
                constraintSet.clone(layout)
                layout.addView(button)
                constraintSet.connect(
                    button.id, ConstraintSet.START,
                    ConstraintSet.PARENT_ID, ConstraintSet.START,
                    (screenX - button.width / 2).toInt()
                )
                constraintSet.connect(
                    button.id, ConstraintSet.TOP,
                    ConstraintSet.PARENT_ID, ConstraintSet.TOP,
                    (screenY - button.height / 2).toInt()
                )
                constraintSet.applyTo(layout)
            }
        }
    }
}

3. Optimizations & Pro Tips

  • Performance: Sampling pixels (stepping by 4) cuts down processing time. For very large images, resize the bitmap first with Bitmap.createScaledBitmap().
  • Color Flexibility: Add a settings option for admins to adjust color thresholds—different blueprints might have slightly different red shades.
  • Precision Upgrade: If you need better detection for small/irregular dots, integrate OpenCV’s SimpleBlobDetector—it’s built for finding circular blobs. Just add the OpenCV Android SDK to your project.
  • Screen Adaptation: Ensure your layout uses match_parent or consistent dimensions so coordinate conversion works across device sizes.

4. Testing

Test with different blueprint images to tweak your color thresholds and sampling step size. Verify components are centered correctly on dots across different screen densities.

内容的提问来源于stack exchange,提问作者alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:38