Search code examples
androidkotlinandroid-color

Android: improving conversion speed of grayscale bitmap to monochromatic


How can I improve the speed of the following process of conversion?

For grayscale bitmap of 384x524 pixels, this takes around 2,1 seconds on the target device.

fun convertToMonochromatic(bitmap: Bitmap): Bitmap {
    val result = Bitmap.createBitmap(bitmap.width, bitmap.height, bitmap.config)

    for (row in 0 until bitmap.height) {
        for (col in 0 until bitmap.width) {
            val hsv = FloatArray(3)
            Color.colorToHSV(bitmap.getPixel(col, row), hsv)

            if (hsv[2] > 0.7f) {
                result.setPixel(col, row, Color.WHITE)
            } else {
                result.setPixel(col, row, Color.BLACK)
            }
        }
    }

    return result
}

Is there some "mass operation", transforming all the pixels at once directly based on the HSV and the Value particulary


Solution

  • Using this access, to iterate the pixels one by one, it seems the main time consuming part is the colorToHSV function. It computes more info, than the needed Value.

    Description of the transform https://www.rapidtables.com/convert/color/rgb-to-hsv.html

    After the following adjustion, the needed time for the given size, is reduced from 2,1 seconds to 0,1 second.

        fun convertToMonochromatic(bitmap: Bitmap): Bitmap {
            val result = Bitmap.createBitmap(bitmap.width, bitmap.height, bitmap.config)
    
            val size = bitmap.width * bitmap.height
            val input = IntArray(size)
            val output = IntArray(size)
    
            bitmap.getPixels(input, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
    
            var current = 0
            input.forEach {
    
                val red = it shr 16 and 0xFF
                val green = it shr 8 and 0xFF
                val blue = it and 0xFF
    
                val max = maxOf(red, green, blue)
                if (max > 175) {
                    output[current++] = Color.WHITE
                } else {
                    output[current++] = Color.BLACK
                }
            }
    
            result.setPixels(output, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
    
            return result
        }