Android开发:如何将Bitmap部分转为黑白,其余保留彩色?
嘿,这个需求我之前做类似的艺术化图片效果时折腾过,其实分两种场景来实现会更清晰——一种是整体按比例混合黑白与原色彩(比如60%的黑白质感+40%的彩色),另一种是指定区域比例转为黑白(比如图片60%的区域变成黑白,剩下保留彩色)。我给你具体说说两种方案的实现:
方案一:整体比例混合(推荐,性能更优)
这种适合做那种半黑白的艺术过渡效果,核心是用ColorMatrix来混合原色彩和黑白效果的比例,代码非常简洁:
// 假设你已经拿到了要处理的原Bitmap val originalBitmap = ... val blackWhiteRatio = 0.6f // 60%黑白,40%保留原色彩 val resultBitmap = Bitmap.createBitmap( originalBitmap.width, originalBitmap.height, originalBitmap.config ) val canvas = Canvas(resultBitmap) // 第一步:创建黑白化的ColorMatrix(饱和度设为0就是全黑白) val bwMatrix = ColorMatrix() bwMatrix.setSaturation(0f) // 第二步:创建混合矩阵,让原色彩保留(1-ratio)的比例,黑白占ratio比例 val mixMatrix = ColorMatrix() mixMatrix.setScale( 1 - blackWhiteRatio, 1 - blackWhiteRatio, 1 - blackWhiteRatio, 1f ) mixMatrix.postConcat(bwMatrix) // 第三步:用混合矩阵创建ColorFilter,绘制到新Bitmap val paint = Paint() paint.colorFilter = ColorMatrixColorFilter(mixMatrix) canvas.drawBitmap(originalBitmap, 0f, 0f, paint) // 最后设置到ImageView yourImageView.setImageBitmap(resultBitmap)
这段代码是通过GPU层面的着色器处理,比直接操作像素快很多,而且不会有明显的性能问题,适合大部分场景。
方案二:指定区域比例转为黑白
如果是要让图片的某一部分(比如60%的区域)变成黑白,剩下的保留彩色,那可以用遮罩或者直接绘制区域的方式实现。这里举两个常见的例子:
示例1:固定矩形区域(横向占60%)
val originalBitmap = ... val targetRatio = 0.6f // 60%区域为黑白 val resultBitmap = Bitmap.createBitmap( originalBitmap.width, originalBitmap.height, originalBitmap.config ) val canvas = Canvas(resultBitmap) // 先绘制原彩色图片 canvas.drawBitmap(originalBitmap, 0f, 0f, null) // 创建黑白效果的Paint val bwPaint = Paint() val bwMatrix = ColorMatrix() bwMatrix.setSaturation(0f) bwPaint.colorFilter = ColorMatrixColorFilter(bwMatrix) // 计算黑白区域的宽度(横向占60%) val bwWidth = (originalBitmap.width * targetRatio).toInt() // 把对应区域的图片绘制成黑白,覆盖在原图片上 canvas.drawBitmap( originalBitmap, Rect(0, 0, bwWidth, originalBitmap.height), Rect(0, 0, bwWidth, originalBitmap.height), bwPaint ) yourImageView.setImageBitmap(resultBitmap)
示例2:随机不规则区域(60%面积为黑白)
如果想要更自然的随机黑白区域,可以用遮罩+Xfermode来实现:
val originalBitmap = ... val targetRatio = 0.6f val resultBitmap = Bitmap.createBitmap( originalBitmap.width, originalBitmap.height, originalBitmap.config ) val canvas = Canvas(resultBitmap) // 第一步:创建Alpha遮罩,生成60%面积的黑色区域(用来标记黑白范围) val maskBitmap = Bitmap.createBitmap( originalBitmap.width, originalBitmap.height, Bitmap.Config.ALPHA_8 ) val maskCanvas = Canvas(maskBitmap) val randomPaint = Paint().apply { color = Color.BLACK } val totalPixels = originalBitmap.width * originalBitmap.height var drawnPixels = 0 val targetPixels = (totalPixels * targetRatio).toInt() // 随机绘制矩形,直到覆盖60%的像素 while (drawnPixels < targetPixels) { val x = Random.nextInt(originalBitmap.width) val y = Random.nextInt(originalBitmap.height) val w = Random.nextInt(originalBitmap.width - x) val h = Random.nextInt(originalBitmap.height - y) maskCanvas.drawRect(x.toFloat(), y.toFloat(), (x+w).toFloat(), (y+h).toFloat(), randomPaint) drawnPixels += w * h } // 第二步:用遮罩绘制黑白区域 val bwPaint = Paint().apply { val bwMatrix = ColorMatrix() bwMatrix.setSaturation(0f) colorFilter = ColorMatrixColorFilter(bwMatrix) // 只在遮罩的黑色区域绘制黑白效果 xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) } // 先画原彩色图,再画遮罩,最后画黑白图(只在遮罩区域生效) canvas.drawBitmap(originalBitmap, 0f, 0f, null) canvas.drawBitmap(maskBitmap, 0f, 0f, null) canvas.drawBitmap(originalBitmap, 0f, 0f, bwPaint) // 记得清空Xfermode,避免影响后续绘制 bwPaint.xfermode = null yourImageView.setImageBitmap(resultBitmap)
一些注意事项
- 如果处理的是大尺寸Bitmap,一定要注意内存问题!建议先用
BitmapFactory.Options的inSampleSize采样缩小,避免OOM。 - 方案一的性能远优于直接操作像素,优先考虑用ColorMatrix的方式。
- 如果需要实时调整黑白比例(比如滑动条控制),可以把逻辑放到自定义ImageView的
onDraw方法里,动态修改ratio值重新绘制即可。
内容的提问来源于stack exchange,提问作者KOG
相关产品推荐
相关产品推荐

