Take monochrome picture (black and white) with Android
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.
Vince
Updated on June 09, 2022Comments
-
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 over 11 yearsThank 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 over 11 yearsOne 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 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/….