在 Android 中将位图转换为灰度

Posted

技术标签:

【中文标题】在 Android 中将位图转换为灰度【英文标题】:Convert a Bitmap to GrayScale in Android 【发布时间】:2011-03-23 09:04:54 【问题描述】:

我是这个网站的新手,我有一个关于 android 的问题。

有没有办法将位图转换为灰度?我知道如何绘制灰度位图(使用画布操作:http://www.mail-archive.com/android-developers@googlegroups.com/msg38890.html),但我真的需要灰色的实际位图(或者至少可以稍后转换为位图的东西)。 我必须手动实现吗(逐像素操作)?

我找了很多,还是没找到。有人知道一种简单/有效的方法吗?

非常感谢!

【问题讨论】:

参考这个 *** 问题:***.com/questions/1793338/drawable-grayscale 所以在将其转换为黑白后图像尺寸会减小吗? (大小以 kb 为单位) 【参考方案1】:

哦,是的,确实如此。 我用错了,谢谢指点。 (对不起这个无用的问题) 这是结束代码(主要基于链接的那个),因为它可能对某人有所帮助:

public Bitmap toGrayscale(Bitmap bmpOriginal)
        
    int width, height;
    height = bmpOriginal.getHeight();
    width = bmpOriginal.getWidth();    

    Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(bmpGrayscale);
    Paint paint = new Paint();
    ColorMatrix cm = new ColorMatrix();
    cm.setSaturation(0);
    ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
    paint.setColorFilter(f);
    c.drawBitmap(bmpOriginal, 0, 0, paint);
    return bmpGrayscale;

非常欢迎任何评论或评论。

谢谢

【讨论】:

这不会保留完整的 8 位灰度,它会将其裁剪为 5 或 6 位,并且每个像素消耗 16 位而不是 8 位。差异可能不是很明显,但请查看渐变看看最坏的情况。 必须说,使用RGB_565会使透明区域变成全黑。在这种情况下,使用 Bitmap.Config.ARGB_8888 来保持灰度图像的透明度。 所以在将其转换为黑白后图像尺寸会减小吗? (大小以 kb 为单位) 我使用 bmpOriginal.getConfig() 而不是 Config.RGB_565 或 Config.ARGB_8888 我会说使用 ARGB_4444,因为当它是灰度时,每个通道不需要 8 位,但仍然需要保持透明度。【参考方案2】:

如果您要在ImageView 上显示该位图。然后,您可以尝试以下代码,而不是将位图转换为灰度:

ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);

ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
imageview.setColorFilter(filter);

For reference

【讨论】:

【参考方案3】:

这不正是您链接到的代码的作用吗?它需要一个彩色位图(“bmp”),创建一个重复的位图(“bm”),然后使用过滤器将彩色位图绘制成“bm”以将其转换为灰度。从那时起,您可以将“bm”用作实际的灰度位图,并使用它做任何您想做的事情。

您需要稍微调整示例(它使用硬编码的大小,您可能只想克隆原始位图的大小),但除此之外,这似乎是随时可用的随心所欲,取决于你想要什么。

【讨论】:

【参考方案4】:

我想提一下,这种方法必须考虑一个重要方面。 Android 上的 BitMap 存储在 NativeHeap 中。通过仅仅“创建位图”,你最终会阻塞内存,得到一个OutOfMemoryException (OOM)。

因此,位图必须始终为.recycled()

【讨论】:

你能提供参考吗? API 文档state 指出“[recycle] 是一个高级调用,通常不需要调用,因为当没有更多对该位图的引用时,正常的 GC 进程将释放此内存。” 只有当 Java 对象被 GC'd 时,GC 才会调用 recycle()。但是,当 VM 内存不足时调用 GC,而不是响应本机堆上的内存可用性。所以,如果你没有做那么多的图像工作,你可能不用回收就可以逃脱。但是,如果你做了很多事情,如果你不回收,你很快就会遇到麻烦。【参考方案5】:

这是一种更有效的方法,我已经为支持所有版本的 Android 做出了贡献:

    //    https://xjaphx.wordpress.com/2011/06/21/image-processing-grayscale-image-on-the-fly/
    @JvmStatic
    fun getGrayscaledBitmapFallback(src: Bitmap, redVal: Float = 0.299f, greenVal: Float = 0.587f, blueVal: Float = 0.114f): Bitmap 
        // create output bitmap
        val bmOut = Bitmap.createBitmap(src.width, src.height, src.config)
        // pixel information
        var A: Int
        var R: Int
        var G: Int
        var B: Int
        var pixel: Int
        // get image size
        val width = src.width
        val height = src.height
        // scan through every single pixel
        for (x in 0 until width) 
            for (y in 0 until height) 
                // get one pixel color
                pixel = src.getPixel(x, y)
                // retrieve color of all channels
                A = Color.alpha(pixel)
                R = Color.red(pixel)
                G = Color.green(pixel)
                B = Color.blue(pixel)
                // take conversion up to one single value
                B = (redVal * R + greenVal * G + blueVal * B).toInt()
                G = B
                R = G
                // set new pixel color to output bitmap
                bmOut.setPixel(x, y, Color.argb(A, R, G, B))
            
        
        // return final image
        return bmOut
    

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    @JvmStatic
    fun getGrayscaledBitmap(context: Context, src: Bitmap): Bitmap 
//        https://gist.github.com/imminent/cf4ab750104aa286fa08
//        https://en.wikipedia.org/wiki/Grayscale
        val redVal = 0.299f
        val greenVal = 0.587f
        val blueVal = 0.114f
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
            return getGrayscaledBitmapFallback(src, redVal, greenVal, blueVal)
        val render = RenderScript.create(context)
        val matrix = Matrix4f(floatArrayOf(-redVal, -redVal, -redVal, 1.0f, -greenVal, -greenVal, -greenVal, 1.0f, -blueVal, -blueVal, -blueVal, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f))
        val result = src.copy(src.config, true)
        val input = Allocation.createFromBitmap(render, src, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT)
        val output = Allocation.createTyped(render, input.type)
        // Inverts and do grayscale to the image
        @Suppress("DEPRECATION")
        val inverter =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
                    ScriptIntrinsicColorMatrix.create(render)
                else
                    ScriptIntrinsicColorMatrix.create(render, Element.U8_4(render))
        inverter.setColorMatrix(matrix)
        inverter.forEach(input, output)
        output.copyTo(result)
        src.recycle()
        render.destroy()
        return result
    

【讨论】:

以上是关于在 Android 中将位图转换为灰度的主要内容,如果未能解决你的问题,请参考以下文章

将位图(RGB_565)转换为灰度(8位)Android

如何在android中将图标转换为位图? [复制]

在android中将位图转换为棕褐色

如何在xamarin android中将位图图像转换为gif?

在 NetworkOnMainThreadException 中将 url 转换为位图 [重复]

如何在 android 中将 Bitmap 转换为 Drawable?