Convert a Bitmap to GrayScale in Android

81,298

Solution 1

Isn't that exactly what the code you're linking to does? It takes a color bitmap ("bmp"), creates a duplicate bitmap ("bm"), and then draws the color bitmap into "bm" using the filter to turn it into grayscale. From that point on, you can use "bm" as an actual grayscale bitmap and do whatever you want to do with it.

You'd need to tweak the sample a bit (it's using hard-coded sizes, you may want to just clone the size of the original bitmap), but other than that, this seems to be as ready-to-use as it gets, depending on what you want.

Solution 2

OH, yes, it does. I was using it wrong, thanks for pointing it out to me. (Sorry for the useless question) Here is the end code (heavily based on the one linked) since it may help someone:

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;
}

Any remarks or comments on it are very welcome.

Thanks

Solution 3

If you are going to show that Bitmap on ImageView. Then Instead of converting Bitmap to Gray Scale, you can try below code:

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

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

For reference

Solution 4

I'd like to mention that with this approach one important aspect must be taken in account. BitMap's on Android are stored in the NativeHeap. By just "creating bitmaps", you'll eventually clog the memory, getting an OutOfMemoryException (OOM).

Therefor, the bitmap must always be .recycled().

Solution 5

Here's a more efficient way, which I've made to support all versions of 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
    }
Share:
81,298
paulonogueira
Author by

paulonogueira

Updated on April 21, 2020

Comments

  • paulonogueira
    paulonogueira about 4 years

    I am new to this site, and I come with a question about Android.

    Is there any way to convert a Bitmap to grayscale? I know how to draw a grayscale bitmap (using canvas operations: http://www.mail-archive.com/[email protected]/msg38890.html) but I really need The actual bitmap in gray colors (or at least something that could be converted to a bitmap later on). Do I have to implement it by hand (pixel by pixel operations)?

    I've searched a lot, and still could not find. Anyone knows a easy/efficient way to do it?

    Thanks a lot!

  • Tamlyn
    Tamlyn over 12 years
    Can you provide a reference? The API docs state that "[recycle] is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap."
  • tomwhipple
    tomwhipple almost 12 years
    The GC will call recycle() only when the Java object is GC'd. BUT, GC is called when the VM runs low on memory, NOT in response to memory availability on the native heap. So, if you can probably get away without recycling if you're not doing that much image work. But, if you're doing a lot you'll quickly get in trouble if you don't recycle.
  • Mark Ransom
    Mark Ransom almost 12 years
    This doesn't retain a full 8 bits of grayscale, it clips it to 5 or 6 bits and consumes 16 bits per pixel rather than 8. The difference might not be highly visible but check out a gradient to see the worst case.
  • IronBlossom
    IronBlossom almost 11 years
    Must say, using RGB_565 will make the transparent area with full black. In that case use Bitmap.Config.ARGB_8888 for maintaining transparency on the grayscaled image.
  • Kalpesh Lakhani
    Kalpesh Lakhani almost 10 years
    so after converting it to black and white will image size reduced ? (size in kb)
  • Jared Rummler
    Jared Rummler over 9 years
    I use bmpOriginal.getConfig() instead of Config.RGB_565 or Config.ARGB_8888
  • RGrun
    RGrun over 7 years
    I'd say use ARGB_4444 because you don't need 8 bits for each channel when it's greyscale, but still need to maintain the transparency.
  • ManuelTS
    ManuelTS over 5 years
    Make sure to call call bmpOriginal.recycle() before returning the grayscale bitmap to free resources.