Applying KMeans clustering from OpenCV cannot return a Bitmap with alpha channel

I’m working in a task for applying kmeans clustering algorithm from OpenCV with Kotlin inside of an android app.

I’m got a piece of code that works, and result is an a reduced colors of the original bitmap. But the original bitmap has a lot of transparent pixels but the result bitmap is returning black pixels in the place of the transparent pixels, but I need that the result keep untouched the transparent pixels.

My method is:

suspend fun calculateDominantQuantizationColors(inputBitmap: Bitmap): Bitmap? {
    return withContext(Dispatchers.IO) {
        try {
            val imagen = Mat()
            Utils.bitmapToMat(inputBitmap, imagen, true)

            // Convertir el bitmap a una matriz de OpenCV (Mat)
            val ny = Mat()
            Utils.bitmapToMat(inputBitmap, ny, true)

            // Convertir la imagen de RGB a BGR
            val bgrImage = Mat()
            Imgproc.cvtColor(ny, bgrImage, Imgproc.COLOR_RGB2BGR)
            bgrImage.convertTo(bgrImage, CvType.CV_32F, 1.0 / 255.0)

            // Convertir la imagen a escala de grises
            val gres = Mat()
            Imgproc.cvtColor(ny, gres, Imgproc.COLOR_BGR2GRAY)

            // Calcular la intensidad mediana
            val medianIntensity = Core.mean(gres).`val`[0]

            // Calcular los umbrales inferior y superior iniciales
            val lowerThreshold = 0.8 * medianIntensity
            val upperThreshold = 1.6 * medianIntensity

            // Aplicar detección de bordes Canny con los umbrales calculados
            val edges = Mat()
            Imgproc.Canny(gres, edges, lowerThreshold, upperThreshold)

            // Encontrar contornos
            val contours: MutableList<MatOfPoint> = ArrayList()
            val hierarchy = Mat()
            Imgproc.findContours(edges, contours, hierarchy, Imgproc.THRESH_BINARY, Imgproc.CHAIN_APPROX_SIMPLE)

            // Reformar la imagen para el agrupamiento k-means
            val reshapedImage = bgrImage.reshape(1, bgrImage.rows() * bgrImage.cols())

            // Realizar agrupamiento k-means
            val labels = Mat()
            val K = 8
            val centers = Mat(K, reshapedImage.cols(), reshapedImage.type())

            val maxCount = 8
            val criteria = TermCriteria(TermCriteria.EPS, maxCount, 2.0)
            Core.kmeans(reshapedImage, K, labels, criteria, 1, Core.KMEANS_PP_CENTERS, centers)

            // Reconstruir la imagen agrupada
            var rec = Mat(bgrImage.rows() * bgrImage.cols(), 3, CvType.CV_32F)
            for (i in 0 until reshapedImage.rows()) {
                val j = labels.get(i, 0)[0].toInt()
                centers.row(j).copyTo(rec.row(i))
            }

            // Reformar la imagen reconstruida al tamaño y tipo original
            rec.convertTo(rec, CvType.CV_8U, 255.0)
            rec = rec.reshape(3, bgrImage.rows())
            Imgproc.cvtColor(rec, rec, Imgproc.COLOR_BGR2RGB)

            // Aplicar un filtro bilateral
            val biletra = Mat()
            Imgproc.bilateralFilter(rec, biletra, 50, 50.0, 50.0, Core.BORDER_DEFAULT)
            Imgproc.drawContours(biletra, contours, -1, Scalar(1.0, 1.0, 1.0), 1)

            // Convertir la imagen procesada a Bitmap
            val resultBitmap = Bitmap.createBitmap(biletra.cols(), biletra.rows(), Bitmap.Config.ARGB_8888)
            Utils.matToBitmap(biletra, resultBitmap, true)

            return@withContext resultBitmap
        } catch (ex: Exception) {
            ex.printStackTrace()
            return@withContext null
        }
    }
}

I cannot upload images because my account is new. I’ve asked the same with images in the next stackoverflow post.

https://stackoverflow.com/questions/78234919/applying-kmeans-clustering-from-opencv-cannot-return-a-bitmap-with-alpha-channel

Any advice to achieve this will be appreciated.

Thank you

the color of (or under?) an ‘alpha’ pixel is basically undefined.
(it can be anything, and a lot of artists don’t bother)

you could try to extractChannel() the alpha before the kmeans, and merge int back to your reconstructed image.

however, you’re also sampling nonsense rgb pixels for the kmeans algorithm.

all in all, it’s a bad choice of input. avoid images with alpha in for computer-vision.

1 Like