google-ai-edge / mediapipe-samples

Apache License 2.0
1.51k stars 387 forks source link

How to get segmentation image in a Bitmap? #315

Open victorjaviermartin opened 7 months ago

victorjaviermartin commented 7 months ago

Hi, I'm trying to work with this sample project, and I want to obtain a bitmap with the roi of the image only. But when I'm getting the result in the method onResults I need to build another Bitmap of the masked region only, but I'm doing something wrong because I can't build it from the ByteBuffer returned.

override fun onResults(result: InteractiveSegmentationHelper.ResultBundle?) {
        // Inform the overlap view to draw over the area of significance returned
        // from the helper
        result?.let {
            activityMainBinding.overlapView.setMaskResult(
                it.byteBuffer,
                it.maskWidth,
                it.maskHeight
            )
        } ?: kotlin.run {
            activityMainBinding.overlapView.clearAll()
        }
    }

I'm using the next two ways to perform this action, but the app crash this both.

fun InteractiveSegmentationHelper.ResultBundle.toBitmap(): Bitmap {
    val byteBuffer = this.byteBuffer
    val maskWidth = this.maskWidth
    val maskHeight = this.maskHeight

    // Crear un arreglo de bytes a partir del ByteBuffer
    val byteArray = ByteArray(byteBuffer.remaining())
    byteBuffer.get(byteArray)

    // Crear un mapa de bits a partir del arreglo de bytes
    val bitmap = Bitmap.createBitmap(maskWidth, maskHeight, Bitmap.Config.ARGB_8888)
    bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(byteArray))

    return bitmap
}

fun InteractiveSegmentationHelper.ResultBundle.toBitmap(): Bitmap {
    val byteBuffer = this.byteBuffer
    val maskWidth = this.maskWidth
    val maskHeight = this.maskHeight

    // Crear un Bitmap a partir del ByteBuffer
    val bitmap = Bitmap.createBitmap(maskWidth, maskHeight, Bitmap.Config.ARGB_8888)

    // Copiar píxeles desde el ByteBuffer al Bitmap
    var copiedPixels = 0
    while (byteBuffer.hasRemaining() && copiedPixels < maskWidth * maskHeight) {
        val pixelsToCopy = minOf(byteBuffer.remaining(), maskWidth * maskHeight - copiedPixels)
        bitmap.copyPixelsFromBuffer(byteBuffer)
        copiedPixels += pixelsToCopy
    }

    // Verificar si se copiaron todos los píxeles
    if (copiedPixels < maskWidth * maskHeight) {
        throw IllegalStateException("No se copiaron todos los píxeles al Bitmap")
    }

    return bitmap
}

The error getted was:

java.lang.RuntimeException: Buffer not large enough for pixels
at android.graphics.Bitmap.copyPixelsFromBuffer(Bitmap.java:662)...

I'm doing the next:

override fun onResults(result: InteractiveSegmentationHelper.ResultBundle?) {
        // Inform the overlap view to draw over the area of significance returned
        // from the helper
        result?.let {
            binding.overlapView.setMaskResult(
                it.byteBuffer,
                it.maskWidth,
                it.maskHeight
            )

            try {
                CoroutineScope(Dispatchers.Main).launch {
                    withContext(Dispatchers.IO){
                        val maskBitmap = it.toBitmap()
                        sharedViewModel.setBitmapPhoto2(maskBitmap)
                    }
                    withContext(Dispatchers.Main){
                        binding.btImagepickerContinue.isEnabled = true
                    }
                }
            }catch (ex: IllegalArgumentException){
                ColoredSnackBar.error(binding.root, "Upss!! Algo no ha ido bien")
                ex.printStackTrace()
            }finally {
                mainActivity.hideProgressIndicator()
            }

        } ?: kotlin.run {
            binding.overlapView.clearAll()
            mainActivity.hideProgressIndicator()

        }

    }
gerardoeg commented 7 months ago

@victorjaviermartin Lo que yo hice fue obtener el bounding box de la región identificada, y con el bounding box recortar la imágen analizada, usa el bitmap que se le pasa al ObjectDetector, ya que la referencia de los bounding boxes, hacen referencia a la imágen analizada.

victorjaviermartin commented 7 months ago

@victorjaviermartin Lo que yo hice fue obtener el bounding box de la región identificada, y con el bounding box recortar la imágen analizada, usa el bitmap que se le pasa al ObjectDetector, ya que la referencia de los bounding boxes, hacen referencia a la imágen analizada.

Me he perdido un poco, tengo poca experiencia con esto, algun sitio donde te informaras de esto? gracias de igual modo @gerardoeg

El tema esta en que me gustaria tener solo la imagen de la mascara, eliminando el resto de pixels que la mascara marca. Estoy perdidisimo