在Kotlin中用Google ML Kit人像分割实现图像背景去除并保存
解决方案:基于ML Kit Segmentation Mask实现人像背景去除与Bitmap保存
不需要使用Matrix来完成这个需求,核心是通过图像合成技术将Mask与原图融合,保留人像区域。以下是具体实现步骤:
1. 将ML Kit返回的Segmentation Mask转换为Bitmap并缩放
ML Kit的Selfie Segmentation返回的Mask是256×256的ByteBuffer,首先需要将其转换为Bitmap,再缩放到与目标原图一致的尺寸:
// 把ML Kit返回的SegmentationMask转为Bitmap val maskBitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888) val buffer = segmentationMask.buffer buffer.rewind() maskBitmap.copyPixelsFromBuffer(buffer) // 缩放Mask到与原图相同的宽高 val scaledMask = Bitmap.createScaledBitmap( maskBitmap, originalBitmap.width, originalBitmap.height, true // 启用双线性过滤保证缩放质量 )
2. 合成去除背景的Bitmap(高效方式)
使用Android Canvas的PorterDuffXfermode实现像素级融合,只保留Mask覆盖的人像区域:
// 创建与原图尺寸一致的空白Bitmap用于存放结果 val resultBitmap = Bitmap.createBitmap( originalBitmap.width, originalBitmap.height, Bitmap.Config.ARGB_8888 ) val canvas = Canvas(resultBitmap) val paint = Paint().apply { // SRC_IN模式:只保留原图与Mask重叠的区域 xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) } // 先绘制原图,再绘制缩放后的Mask(应用Xfermode) canvas.drawBitmap(originalBitmap, 0f, 0f, null) canvas.drawBitmap(scaledMask, 0f, 0f, paint)
3. Jetpack Compose中的绘制优化
你当前使用的drawPoints并不是合适的方式,直接在DrawScope中利用blendMode即可实现实时绘制去除背景的图像:
Canvas(modifier = Modifier.fillMaxSize()) { // 绘制原图 drawImage(originalBitmap) // 绘制缩放后的Mask,使用SrcIn混合模式保留人像区域 drawImage(scaledMask, blendMode = BlendMode.SrcIn) }
4. 保存合成后的Bitmap到设备
使用MediaStore(适配Android 10+)将结果Bitmap保存到图库:
fun savePortraitBitmap(bitmap: Bitmap, context: Context) { val filename = "portrait_${System.currentTimeMillis()}.png" val contentValues = ContentValues().apply { put(MediaStore.Images.Media.DISPLAY_NAME, filename) put(MediaStore.Images.Media.MIME_TYPE, "image/png") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.Images.Media.RELATIVE_PATH, "${Environment.DIRECTORY_PICTURES}/MyApp") put(MediaStore.Images.Media.IS_PENDING, 1) } } context.contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues )?.let { uri -> context.contentResolver.openOutputStream(uri)?.use { outputStream -> bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentValues.put(MediaStore.Images.Media.IS_PENDING, 0) context.contentResolver.update(uri, contentValues, null, null) } } }
关键说明
- Matrix主要用于图像的平移、旋转、缩放等几何变换,这里的核心需求是图像像素级融合,因此使用
PorterDuffXfermode或Compose的BlendMode是更直接高效的方案。 - 如果你的Mask中蓝色区域对应人像,需要先将Mask转为灰度图,确保人像区域为白色(高亮度),背景为黑色(低亮度),这样
SRC_IN模式才能正确生效。
内容的提问来源于stack exchange,提问作者Gregor Sotošek
相关产品推荐
相关产品推荐

