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

Android移除图片背景后保存至相册显示为黑色的问题

背景移除后图片保存到相册背景变黑色的问题

我通过以下代码实现图片背景移除功能,处理后的图片在应用内显示为透明背景,但保存到设备相册后,背景却变成了黑色。

实现代码

object BackgroundRemover {

private val segment: Segmenter
private var buffer = ByteBuffer.allocate(0)
private var width = 0
private var height = 0


init {
    val segmentOptions = SelfieSegmenterOptions.Builder()
        .setDetectorMode(SelfieSegmenterOptions.SINGLE_IMAGE_MODE)
        .build()
    segment = Segmentation.getClient(segmentOptions)
}


/**
 * Process the image to get buffer and image height and width
 * @param bitmap Bitmap which you want to remove background.
 * @param trimEmptyPart After removing the background if its true it will remove the empty part of bitmap. by default its false.
 * @param listener listener for success and failure callback.
 **/
fun bitmapForProcessing(
    bitmap: Bitmap,
    trimEmptyPart: Boolean = false,
    listener: OnBackgroundChangeListener
) {
    //Generate a copy of bitmap just in case the if the bitmap is immutable.
    val copyBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
    val input = InputImage.fromBitmap(copyBitmap, 0)
    segment.process(input)
        .addOnSuccessListener { segmentationMask ->
            buffer = segmentationMask.buffer
            width = segmentationMask.width
            height = segmentationMask.height

            CoroutineScope(Dispatchers.IO).launch {
                val time = measureTimeMillis {
                    val resultBitmap = if (trimEmptyPart) {
                        val bgRemovedBitmap = removeBackgroundFromImage(copyBitmap)
                        trim(bgRemovedBitmap)
                    } else {
                        removeBackgroundFromImage(copyBitmap)
                    }
                    withContext(Dispatchers.Main) {
                        listener.onSuccess(resultBitmap)
                    }
                }
                Log.e("TAG", "bitmapForProcessingTime: $time")
            }

        }
        .addOnFailureListener { e ->
            println("Image processing failed: $e")
            listener.onFailed(e)

        }
}


/**
 * Change the background pixels color to transparent.
 * */
private suspend fun removeBackgroundFromImage(
    image: Bitmap
): Bitmap {
    val bitmap = CoroutineScope(Dispatchers.IO).async {
        for (y in 0 until height) {
            for (x in 0 until width) {
                val bgConfidence = ((1.0 - buffer.float) * 255).toInt()
                if (bgConfidence >= 100) {
                    image.setPixel(x, y, Color.TRANSPARENT)
                }
            }
        }
        buffer.rewind()
        return@async image
    }
    return bitmap.await()
}


/**
 * trim the empty part of a bitmap.
 **/
private suspend fun trim(
    bitmap: Bitmap
): Bitmap {
    val result = CoroutineScope(Dispatchers.Default).async {
        var firstX = 0
        var firstY = 0
        var lastX = bitmap.width
        var lastY = bitmap.height
        val pixels = IntArray(bitmap.width * bitmap.height)
        bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
        loop@ for (x in 0 until bitmap.width) {
            for (y in 0 until bitmap.height) {
                if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                    firstX = x
                    break@loop
                }
            }
        }
        loop@ for (y in 0 until bitmap.height) {
            for (x in firstX until bitmap.width) {
                if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                    firstY = y
                    break@loop
                }
            }
        }
        loop@ for (x in bitmap.width - 1 downTo firstX) {
            for (y in bitmap.height - 1 downTo firstY) {
                if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                    lastX = x
                    break@loop
                }
            }
        }
        loop@ for (y in bitmap.height - 1 downTo firstY) {
            for (x in bitmap.width - 1 downTo firstX) {
                if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                    lastY = y
                    break@loop
                }
            }
        }
        return@async Bitmap.createBitmap(bitmap, firstX, firstY, lastX - firstX, lastY - firstY)
    }
    return result.await()
}

效果说明

  • 处理后:图片在应用内显示为透明背景
  • 保存到相册后:背景变为黑色

问题原因

相册默认通常会以JPG格式展示图片,而JPG格式不支持透明通道,透明区域会被自动填充为黑色。另外如果你的保存代码默认使用JPEG格式写入图片,也会直接丢失透明信息,导致背景变黑。

解决方案

要保留透明背景,必须以PNG格式保存图片,因为PNG支持Alpha透明通道。

以下是Android中保存透明Bitmap到相册的示例代码:

fun saveBitmapToGallery(context: Context, bitmap: Bitmap) {
    val filename = "transparent_image_${System.currentTimeMillis()}.png"
    val outputStream: OutputStream?

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
            put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/YourAppName")
        }
        val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
        outputStream = uri?.let { context.contentResolver.openOutputStream(it) }
    } else {
        val picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath
        val dir = File(picturesDir, "YourAppName")
        if (!dir.exists()) dir.mkdirs()
        val file = File(dir, filename)
        outputStream = FileOutputStream(file)
    }

    outputStream?.use {
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
        context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(File(filename))))
        Toast.makeText(context, "图片保存成功", Toast.LENGTH_SHORT).show()
    }
}

关键注意点:

  • 压缩格式必须指定为Bitmap.CompressFormat.PNG
  • MIME类型设置为image/png
  • 文件名后缀用.png

这样保存后的图片在相册中就能正确显示透明背景了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:34:54