5

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

Community
  • 1
  • 1
Vince
  • 219
  • 1
  • 4
  • 18
  • Um..this may be a silly question, but what's the difference between 'true black and white' and grayscale? Both are made in various shades of gray, but is there a specific definition for the terms? Btw , '50 shades' jokes are banned! :) – Alex Aug 20 '12 at 15:52
  • @Alex - I was wondering the same thing and stumbled across this example http://farm3.staticflickr.com/2393/3533241378_04c2b649c2_b.jpg. The top image is monochrome and bottom is greyscale. I suppose it depends on what the OP is trying to achieve - capture a true monochromatic image from the phone camera in his app or convert existing images? Only reason I ask is because he has already posted examples of converting them after the fact :-) – shri046 Aug 20 '12 at 16:00
  • @shri046 without some words, your example isn't an explanation. Monochrome means one colour against a background of some other colour. Black and white means it's black against a white background. I would guess that the examples you've shown are just converted from colour to B&W using different filters. – slim Aug 20 '12 at 16:19
  • I think this is probably appropriate: http://stackoverflow.com/questions/2499545/getting-greyscale-pixel-value-from-rgb-colourspace-in-java-using-bufferedimage – slim Aug 20 '12 at 16:21
  • @slim I'd assumed the OP didn't want a true monochrome (i.e. pixel values 0 and 255 only), which is what confused me. Can't check most of the linked images as I'm at work (blocked, grr). Btw, black and white could also mean white on a black background! Is a zebra black with white stripes or white with black stripes? :) – Alex Aug 21 '12 at 08:20

2 Answers2

18

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;
}
Community
  • 1
  • 1
zapl
  • 63,179
  • 10
  • 123
  • 154
  • 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 Aug 21 '12 at 08:13
  • 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 ? – Vince Aug 21 '12 at 08:25
  • 1
    @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](http://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages) or read http://stackoverflow.com/questions/687261/converting-rgb-to-grayscale-intensity. – zapl Aug 21 '12 at 09:28
1

G = Color.red(pixel);

G = Color.green(pixel);

B = Color.red(pixel);

B = Color.blue(pixel);

See if this changes (in bold) helps.

C2940680
  • 111
  • 1