This question already has answers here:
Convert a Bitmap to GrayScale in Android
(5 answers)
Closed 9 years ago.
I am trying to convert color image into grayscale using the average of red, green, blue. But it comes out with errors.
Here is my code
imgWidth = myBitmap.getWidth();
imgHeight = myBitmap.getHeight();
for(int i =0;i<imgWidth;i++) {
for(int j=0;j<imgHeight;j++) {
int s = myBitmap.getPixel(i, j)/3;
myBitmap.setPixel(i, j, s);
}
}
ImageView img = (ImageView)findViewById(R.id.image1);
img.setImageBitmap(myBitmap);
But when I run my application on Emulator, it's force close. Any idea?
I have solved my problem use the following code:
for(int x = 0; x < width; ++x) {
for(int y = 0; y < height; ++y) {
// 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
R = G = B = (int)(0.299 * R + 0.587 * G + 0.114 * B);
// set new pixel color to output bitmap
bmOut.setPixel(x, y, Color.argb(A, R, G, B));
}
}
You can do this too :
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
imageview.setColorFilter(new ColorMatrixColorFilter(matrix));
Try the solution from this previous answer by leparlon:
public Bitmap toGrayscale(Bitmap bmpOriginal)
{
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
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;
}
Lalit has the most practical answer. However, you wanted the resulting grey to be the average of the red, green and blue and should set up your matrix like so:
float oneThird = 1/3f;
float[] mat = new float[]{
oneThird, oneThird, oneThird, 0, 0,
oneThird, oneThird, oneThird, 0, 0,
oneThird, oneThird, oneThird, 0, 0,
0, 0, 0, 1, 0,};
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(mat);
paint.setColorFilter(filter);
c.drawBitmap(original, 0, 0, paint);
And finally, as I have faced the problem of converting an image to grayscale before - the most visually pleasing result in all cases is achieved by not taking the average, but through giving each colour different weight depending on its percieved brightness, I tend to use these values:
float[] mat = new float[]{
0.3f, 0.59f, 0.11f, 0, 0,
0.3f, 0.59f, 0.11f, 0, 0,
0.3f, 0.59f, 0.11f, 0, 0,
0, 0, 0, 1, 0,};
Related
I need to grayscale a bitmap in Android using only the blue color channel.
I managed to get rid of the green and red channels by using a colormatrix, but when I set the saturation of that matrix to 0 it is ignoring the previously made changes to the red and green channels.
Is there a way to accomplish my task? Iterating through the whole pixel array is not an option as it is way too slow.
I'm using this piece of code:
public Bitmap ConvertToGrayscale(Bitmap bitmap) {
int height = super.getHeight();
int width = super.getWidth();
float[] arrayForColorMatrix = new float[] {0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0};
Bitmap.Config config = bitmap.getConfig();
Bitmap grayScaleBitmap = Bitmap.createBitmap(width, height, config);
Canvas c = new Canvas(grayScaleBitmap);
Paint paint = new Paint();
ColorMatrix matrix = new ColorMatrix(arrayForColorMatrix);
matrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
paint.setColorFilter(filter);
c.drawBitmap(bitmap, 0, 0, paint);
return grayScaleBitmap;
}
The correct matrix for getting a gray scaled image from the blue channel is the following:
float[] arrayForColorMatrix = new float[] {0, 0, 1, 0, 0,
0, 0, 1, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0};
From the Android documentation:
When applied to a color [r, g, b, a], the resulting color is computed as (after clamping):
R' = a*R + b*G + c*B + d*A + e;
G' = f*R + g*G + h*B + i*A + j;
B' = k*R + l*G + m*B + n*A + o;
A' = p*R + q*G + r*B + s*A + t;
Using my arrayForColorMatrix I get the blue value for every value of the RGB color components, which results in a gray scaled bitmap based on the blue channel.
In my Business Card Reader android application, I need to convert color image bitmap to black & white image bitmap (not gray scale image) for OCR text reading. so please help me to to convert color image bitmap to black & white image bitmap in android.
This question is long ago, but probably I could help other users.
I also had a long search for creating (fast)a pure Black and White bitmap.
My first methode used bitmap.getPixel() and bitmap.setPixel()
This took about 8s (832 x 1532)
New Method took 0.4s! Thanks factor 20!
Now I load all Pixels in an int array and go so through all pixels with getPixels(..) and setPixels(..):
Here my method:
public static Bitmap createBlackAndWhite(Bitmap src) {
int width = src.getWidth();
int height = src.getHeight();
Bitmap bmOut = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final float factor = 255f;
final float redBri = 0.2126f;
final float greenBri = 0.2126f;
final float blueBri = 0.0722f;
int length = width * height;
int[] inpixels = new int[length];
int[] oupixels = new int[length];
src.getPixels(inpixels, 0, width, 0, 0, width, height);
int point = 0;
for(int pix: inpixels){
int R = (pix >> 16) & 0xFF;
int G = (pix >> 8) & 0xFF;
int B = pix & 0xFF;
float lum = (redBri * R / factor) + (greenBri * G / factor) + (blueBri * B / factor);
if (lum > 0.4) {
oupixels[point] = 0xFFFFFFFF;
}else{
oupixels[point] = 0xFF000000;
}
point++;
}
bmOut.setPixels(oupixels, 0, width, 0, 0, width, height);
return bmOut;
}
You could convert the image applying a color filter in this way:
Bitmap bwBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bwBitmap );
//set contrast
ColorMatrix contrastMatrix = new ColorMatrix();
//change contrast
float contrast = 50.f;
float shift = (-.5f * contrast + .5f) * 255.f;
contrastMatrix .set(new float[] {
contrast , 0, 0, 0, shift ,
0, contrast , 0, 0, shift ,
0, 0, contrast , 0, shift ,
0, 0, 0, 1, 0 });
//apply contrast
Paint contrastPaint = new Paint();
contrastPaint.setColorFilter(new ColorMatrixColorFilter(contrastMatrix ));
canvas.drawBitmap(colorBitmap, 0, 0, contrastPaint);
//set saturation
ColorMatrix saturationMatrix = new ColorMatrix();
saturationMatrix.setSaturation(0); //you set color saturation to 0 for b/w
//apply new saturation
Paint saturationPaint = new Paint();
saturationPaint.setColorFilter(new ColorMatrixColorFilter(saturationPaint));
canvas.drawBitmap(colorBitmap, 0, 0, saturationPaint);
You need to use ColorMatrix and ColorFilterclass in android to convert to Black and White.
Use this ColorMatrix - ColorMatrix cm1 = new ColorMatrix(new float[]{0.5f,0.5f,0.5f,0,0,
0.5f,0.5f,0.5f,0,0,
0.5f,0.5f,0.5f,0,0,
0,0,0,1,0,0,
0,0,0,0,1,0
});
I wanted to mask a Bitmap with a black and white alpha mask. My mask image is black and white, the BLACK area means TRANSPARENT and WHITE area means OPAQUE.
What I need is:
When I use this mask image to mask any other image, the area of resultant image should TRANSPARENT if the corresponding area of mask image is BLACK. Otherwise the area of resultant image should be OPAQUE.
I have attached the sample images. Please help me with this guys.
Sample Image:
Sample Image for Masking
What I have tried so far:
The following methods work fine. But they are very slow. I needed some solution that is efficient in terrms of speed and memory than these methods.
First Method:
int width = rgbDrawable.getWidth();
int height = rgbDrawable.getHeight();
if (width != alphaDrawable.getWidth() || height != alphaDrawable.getHeight()) {
throw new IllegalStateException("image size mismatch!");
}
Bitmap destBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
int[] pixels = new int[width];
int[] alpha = new int[width];
for (int y = 0; y < height; y++)
{
rgbDrawable.getPixels(pixels, 0, width, 0, y, width, 1);
alphaDrawable.getPixels(alpha, 0, width, 0, y, width, 1);
for (int x = 0; x < width; x++)
{
// Replace the alpha channel with the r value from the bitmap.
pixels[x] = (pixels[x] & 0x00FFFFFF) | ((alpha[x] << 8) & 0xFF000000);
}
destBitmap.setPixels(pixels, 0, width, 0, y, width, 1);
}
alphaDrawable.recycle();
rgbDrawable.recycle();
return destBitmap;
Second Method
float[] nlf = new float[] {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 0, 0, 0, 0};
ColorMatrix sRedToAlphaMatrix = new ColorMatrix(nlf);
ColorMatrixColorFilter sRedToAlphaFilter = new ColorMatrixColorFilter(sRedToAlphaMatrix);
// Load RGB data
Bitmap rgb = rgbDrawable;
// Prepare result Bitmap
Bitmap target = Bitmap.createBitmap(rgb.getWidth(), rgb.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(target);
c.setDensity(Bitmap.DENSITY_NONE);
// Draw RGB data on our result bitmap
c.drawBitmap(rgb, 0, 0, null);
// At this point, we don't need rgb data any more: discard!
rgb.recycle();
rgb = null;
// Load Alpha data
Bitmap alpha = alphaDrawable;
// Draw alpha data on our result bitmap
final Paint grayToAlpha = new Paint();
grayToAlpha.setColorFilter(sRedToAlphaFilter);
grayToAlpha.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
c.drawBitmap(alpha, 0, 0, grayToAlpha);
// Don't need alpha data any more: discard!
alpha.recycle();
alpha = null;
return target;
I have a method that take a Bitmap and a color, and converts all the pixels on the Bitmap to that color, while keeping the original's alpha values. In addition, the method is written so that as the color passed in gets darker, the Bitmap is made more transparent rather than darker. If the color passed in is completely black, then the Bitmap should be made entirely transparent instead of black.
public static Bitmap colorImage(Bitmap img, int red, int green, int blue) {
int max = blue;
if (red >= green && red >= blue)
max = red;
else if (green >= red && green >= blue)
max = green;
double scale = 255.0 / max;
red = (int)(scale * red);
green = (int)(scale * green);
blue = (int)(scale * blue);
Bitmap resultBitmap = img.copy(Bitmap.Config.ARGB_8888, true);
float[] colorTransform = {
0, 0, 0, 0, red,
0, 0, 0, 0, green,
0, 0, 0, 0, blue,
0, 0, 0, (float) (1f / scale), 0};
ColorMatrix colorMatrix = new ColorMatrix(colorTransform);
ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix);
Paint paint = new Paint();
paint.setColorFilter(colorFilter);
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(resultBitmap, 0, 0, paint);
return resultBitmap;
}
There is an issue with making the Bitmap transparent.
If I set the color matrix so that it is as follows,
float[] colorTransform = {
0, 0, 0, 0, red,
0, 0, 0, 0, green,
0, 0, 0, 0, blue,
0, 0, 0, 0, 0};
the entire Bitmap should be made completely transparent. However, this only works correctly if the original Bitmap has no transparency at all. If the Bitmap has only alpha values of 255 for all of its pixels, then the result is completely transparent. However, if the Bitmap has any pixel with an alpha value of less than 255, the final image will not be transparent, but will have the same alpha values as the original.
Can anybody tell why?
Thanks in advance.
Bitmaps by default have black backgrounds. Setting the Bitmap's background to transparent resolves the issue.
This can be done by adding resultBitmap.eraseColor(Color.argb(0, 0, 0, 0)); after resultBitmap is declared, changing canvas.drawBitmap(resultBitmap, 0, 0, paint); to canvas.drawBitmap(img, 0, 0, paint);.
The following function changes contrast and brightness in the picture successfully.
Bitmap bmp;
ImageView alteredImageView;
...
public void drawAlteredImage(float contr,float bright) {
Bitmap alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(alteredBitmap);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.set(new float[] {
contr, 0, 0, 0, bright,
0, contr, 0, 0, bright,
0, 0, contr, 0, bright,
0, 0, 0, 1, 0 });
paint.setColorFilter(new ColorMatrixColorFilter(cm));
Matrix matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
alteredImageView.setImageBitmap(alteredBitmap);
}
But when I added setSaturation method to ColorMatrix the contrast and brightness altering ceased to work. The code:
public void drawAlteredImage(float contr,float bright,float satur) {
Bitmap alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(alteredBitmap);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.set(new float[] {
contr, 0, 0, 0, bright,
0, contr, 0, 0, bright,
0, 0, contr, 0, bright,
0, 0, 0, 1, 0 });
cm.setSaturation(satur);
paint.setColorFilter(new ColorMatrixColorFilter(cm));
Matrix matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
alteredImageView.setImageBitmap(alteredBitmap);
}
Only the saturation effect is applying in this case. Why does this problem happening? How can I fix it?
The setSaturation(...) method replaces all the values in the matrix. You can either create a matrix that combines both operations, or do the operations with two separate drawBitmap(...) calls.
Try:
cm.setSaturation(satur);
final float m[] = cm.getArray();
final float c = contr;
cm.set(new float[] {
m[ 0] * c, m[ 1] * c, m[ 2] * c, m[ 3] * c, m[ 4] * c + bright,
m[ 5] * c, m[ 6] * c, m[ 7] * c, m[ 8] * c, m[ 9] * c + bright,
m[10] * c, m[11] * c, m[12] * c, m[13] * c, m[14] * c + bright,
m[15] , m[16] , m[17] , m[18] , m[19] });
Another option is to create a new ColorMatrix, set the saturation in this new ColorMatrix, then call postConcat on the Brightness/Contrast ColorMatrix. Something like:
public void drawAlteredImage(float contr,float bright,float satur) {
Bitmap alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(alteredBitmap);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.set(new float[] {
contr, 0, 0, 0, bright,
0, contr, 0, 0, bright,
0, 0, contr, 0, bright,
0, 0, 0, 1, 0 });
ColorMatrix saturationCM = new ColorMatrix();
saturationCM.setSaturation(satur);
cm.postConcat(saturationCM);
paint.setColorFilter(new ColorMatrixColorFilter(cm));
Matrix matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
alteredImageView.setImageBitmap(alteredBitmap);
}
If someone still looking for this , the easy way to change saturation, brightness, and contrast of an image is just use ImageFilterView which is the subclass of ImageView
source:
https://developer.android.com/reference/kotlin/androidx/constraintlayout/utils/widget/ImageFilterView
example in kotlin:
val myImage = findViewById<ImageFilterView>(R.id.my_image)
myImage.setSaturation(1.2f)
myImage.setBrightness(1.5f)
myImage.setContrast(2f)