Take monochrome picture (black and white) with Android

12,308

Solution 1

If you like the image to be 1bit black/white you can use a simple (& slow) threshold algorithm

public static Bitmap createBlackAndWhite(Bitmap src) {
    int width = src.getWidth();
    int height = src.getHeight();
    // create output bitmap
    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
    // color information
    int A, R, G, B;
    int pixel;

    // scan through all pixels
    for (int x = 0; x < width; ++x) {
        for (int y = 0; y < height; ++y) {
            // get pixel color
            pixel = src.getPixel(x, y);
            A = Color.alpha(pixel);
            R = Color.red(pixel);
            G = Color.green(pixel);
            B = Color.blue(pixel);
            int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B);

            // use 128 as threshold, above -> white, below -> black
            if (gray > 128) 
                gray = 255;
            else
                gray = 0;
            // set new pixel color to output bitmap
            bmOut.setPixel(x, y, Color.argb(A, gray, gray, gray));
        }
    }
    return bmOut;
}

But depending on what that will not look good, for better results you need a dithering algorithm, see Algorithm overview - this one is the threshold method.


For 256 levels of gray conversion:

according to http://www.mathworks.de/help/toolbox/images/ref/rgb2gray.html you calculate the gray value of each pixel as gray = 0.2989 * R + 0.5870 * G + 0.1140 * B which would translate to

public static Bitmap createGrayscale(Bitmap src) {
    int width = src.getWidth();
    int height = src.getHeight();
    // create output bitmap
    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
    // color information
    int A, R, G, B;
    int pixel;

    // scan through all pixels
    for (int x = 0; x < width; ++x) {
        for (int y = 0; y < height; ++y) {
            // get pixel color
            pixel = src.getPixel(x, y);
            A = Color.alpha(pixel);
            R = Color.red(pixel);
            G = Color.green(pixel);
            B = Color.blue(pixel);
            int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B);
            // set new pixel color to output bitmap
            bmOut.setPixel(x, y, Color.argb(A, gray, gray, gray));
        }
    }
    return bmOut;
}

But that is pretty slow since you have to do that for millions of pixels separately.

https://stackoverflow.com/a/9377943/995891 has a much nicer way of achieving the same.

// code from that answer put into method from above
public static Bitmap createGrayscale(Bitmap src) {
    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap bmOut = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bmOut);
    ColorMatrix ma = new ColorMatrix();
    ma.setSaturation(0);
    Paint paint = new Paint();
    paint.setColorFilter(new ColorMatrixColorFilter(ma));
    canvas.drawBitmap(src, 0, 0, paint);
    return bmOut;
}

Solution 2

G = Color.red(pixel);

G = Color.green(pixel);

B = Color.red(pixel);

B = Color.blue(pixel);

See if this changes (in bold) helps.

Share:
12,308
Vince
Author by

Vince

Updated on June 09, 2022

Comments

  • Vince
    Vince almost 2 years

    I would like to take a picture in true black and white in my app. I searched for solutions (in this site too), but I always found solution to put a photo in gray scale (for example in this topic), but it's not what I am looking for ...

    I also found a topic proposing this :

    public static Bitmap createContrast(Bitmap src, double value) {
    // image size
    
                    int width = src.getWidth();
                    int height = src.getHeight();
                    // create output bitmap
                    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
                    // color information
                    int A, R, G, B;
                    int pixel;
                    // get contrast value
                    double contrast = Math.pow((100 + value) / 100, 2);
    
            // scan through all pixels
            for (int x = 0; x < width; ++x) {
                for (int y = 0; y < height; ++y) {
                    // get pixel color
                    pixel = src.getPixel(x, y);
                    A = Color.alpha(pixel);
                    // apply filter contrast for every channel R, G, B
                    R = Color.red(pixel);
                    R = (int) (((((R / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
                    if (R < 0) {
                        R = 0;
                    } else if (R > 255) {
                        R = 255;
                    }
    
                    G = Color.red(pixel);
                    G = (int) (((((G / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
                    if (G < 0) {
                        G = 0;
                    } else if (G > 255) {
                        G = 255;
                    }
    
                    B = Color.red(pixel);
                    B = (int) (((((B / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
                    if (B < 0) {
                        B = 0;
                    } else if (B > 255) {
                        B = 255;
                    }
    
                    // set new pixel color to output bitmap
                    bmOut.setPixel(x, y, Color.argb(A, R, G, B));
                }
            }
    
            return bmOut;
    
        }
    

    But the image quality is horrible ...

    Is anyone having an idea please?

    Thank you

  • Vince
    Vince over 11 years
    Thank you a lot, your "createBlackAndWhite()" method is what I was looking for. A bit slow as you say, but this is true black and white. I'm gonna try to optimize it a bit, but it works fine
  • Vince
    Vince over 11 years
    One question : in this piece of code "(0.2989 * R + 0.5870 * G + 0.1140 * B)", why do you use thoses values : 0.2989 , 0.5870 & 0.1140 ?
  • zapl
    zapl over 11 years
    @Vince That is one of the possible ways to convert color to grayscale. Humans perceive the brightness of colors differently (e.g. blue is much brighter than green) so this tries to match the human perception. Have a look at the images here or read stackoverflow.com/questions/687261/….