3
\$\begingroup\$

I have piece of code where I draw output from model pixel by pixel:

fun draw(
        canvas: Canvas) {
        val rotated = sensorOrientation % 180 == 90
        var rotatedW = frameWidth;
        var rotatedH = frameHeight;
        if(rotated) {
            rotatedW = frameHeight
            rotatedH = frameWidth
        }

        val multiplier =
            min(
                canvas.height.toFloat() / rotatedH.toFloat(),
                canvas.width.toFloat() / rotatedW.toFloat()
            )
        val w = (rotatedW * multiplier).toInt()
        val h = (rotatedH * multiplier).toInt()

        results?.let {
            var pos: Int
            val xw = w / 256f
            val xh = h / 256f
            val rectF = RectF()
            for (y in 0 until 256) {
                for (x in 0 until 256) {
                    pos = getIndexOfMax(results!![0][y][x])
                    if (pos > 0) {
                        val vertical = y / 256f * h
                        val horizontal = x / 256f * w
                        rectF.left = horizontal - xw
                        rectF.top = vertical - xh
                        rectF.right = horizontal + xw
                        rectF.bottom = vertical + xh
                        canvas.drawRect(rectF, boxPaint)
                    }
                   
                }
            }
        }
    }

However this works really slow. Nested for is probably one of the main issues here. I don't know how can I speed it up. As you can see both counters are necessary here because I need to get certain field from results. I've heard that Bitmap uasage instead of Canvas would be partial solution because I am able to get certain pixels but I don't really know how to use it right. Method draw() is called here inside MainActivity:

trackingOverlay.addCallback(
            object : OverlayView.DrawCallback {
                override fun drawCallback(canvas: Canvas?) = tracker.draw(canvas!!)
            }
        )

And the trackingOverlay is an instance of OverlayView class which inherits from View:

class OverlayView(ctx: Context, attr: AttributeSet) : View(ctx, attr) {

    private val callbacks: MutableList<DrawCallback> =
        LinkedList()

    fun addCallback(callback: DrawCallback) { callbacks.add(callback) }

    @Synchronized
    override fun draw(canvas: Canvas) {
        for (callback in callbacks) {
            callback.drawCallback(canvas)
        }
    }

    interface DrawCallback {
        fun drawCallback(canvas: Canvas?)
    }
}

I've tried creating Bitmap before draw (to reuse it) and then use canvas.drawBitmap() but it needs transformation Matrix to fill whole screen, otherwise it covers only small chunk of it and even then I haven't noticed any performance increase.

EDIT That's how getIndexOfMax() looks like:

private fun getIndexOfMax(array: FloatArray): Int {
        if (array.isEmpty()) return -1
        return when {
            array[0] > array[1] -> 0
            else -> 1
        }
    }

Results is 4 dim Array of Float

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

TL;DR

Introduce coroutines in your code.

Analysis

It is difficult to optimize a code, where multiple parts are missing, like:

  • what does getIndexOfMax do? Maybe this is the bottleneck
  • How do results look like? Arrays? Maps? HashMaps?

I found also some strange code parts, like:

  • Why results?.let {? This can be removed completely.
  • var pos: Int should be moved inside the inner loop and made a val

Suggestion

I have too little context and I'm not familiar with Android, so the only thing I can suggest is using coroutines. My Example:

suspend fun draw(canvas: Canvas) = coroutineScope {
    val rotated = sensorOrientation % 180 == 90
    var rotatedW = frameWidth;
    var rotatedH = frameHeight;
    if (rotated) {
        rotatedW = frameHeight
        rotatedH = frameWidth
    }

    val multiplier =
        min(
            canvas.height.toFloat() / rotatedH.toFloat(),
            canvas.width.toFloat() / rotatedW.toFloat()
        )
    val w = (rotatedW * multiplier).toInt()
    val h = (rotatedH * multiplier).toInt()
    
    val xw = w / 256f
    val xh = h / 256f
    val rectF = RectF()
    for (y in 0 until 256) {
        for (x in 0 until 256) {
            launch {   // <-----
                val pos = getIndexOfMax(results!![0][y][x])
                if (pos > 0) {
                    val vertical = y / 256f * h
                    val horizontal = x / 256f * w
                    rectF.left = horizontal - xw
                    rectF.top = vertical - xh
                    rectF.right = horizontal + xw
                    rectF.bottom = vertical + xh
                    canvas.drawRect(rectF, boxPaint)
                }
            }
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ I've made edit to my question. Instead of making index of max I can just get value of array[1] and check if pos > 0.6 (or 0.7) \$\endgroup\$
    – SkypeDogg
    Commented Sep 26, 2020 at 10:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.