I am doing an image processing which require to convert RGB bitmap image to YCbCr color space. I retrieved RGB value for each pixel and apply the conversion matrix to it.
public void convertRGB (View v) {
if (imageLoaded) {
int width = inputBM.getWidth();
int height = inputBM.getHeight();
int pixel;
int alpha, red, green, blue;
int Y,Cb,Cr;
outputBM = Bitmap.createBitmap(width, height, inputBM.getConfig());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
pixel = inputBM.getPixel(x, y);
alpha = Color.alpha(pixel);
red = Color.red(pixel);
green = Color.green(pixel);
blue = Color.blue(pixel);
Y = (int) (0.299 * red + 0.587 * green + 0.114 * blue);
Cb = (int) (128-0.169 * red-0.331 * green + 0.500 * blue);
Cr = (int) (128+0.500 * red - 0.419 * green - 0.081 * blue);
int p = (Y << 24) | (Cb << 16) | (Cr<<8);
outputBM.setPixel(x,y,p);
}
}
comImgView.setImageBitmap(outputBM);
}
}
The problem is he output color is different with original. I tried to use BufferedImage but it do not work in Android
Original:
After Conversion:
May I know what is the correct way to handle YCbCr image in android java.
Try setting using below code
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuvImage = new YuvImage(your_yuv_data, ImageFormat.NV21, width, height, null);
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 50, out);
byte[] imageBytes = out.toByteArray();
Bitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
iv.setImageBitmap(image);
Check documentation for detailed description for YuvImage Class.
Related
I've spend a lot of time trying to figure this out but can't see what I am doing wrong.
This is my original image
Image recaptured 5 times
Recapturing the image multiple times clearly shows that there is something not right. Capturing it once is just ok but twice is enough to clearly see the difference.
I found these similar issues on stackoverflow:
Bitmap quality using glReadPixels with frame buffer objects
Extract pixels from TextureSurface using glReadPixels resulting in bad image Bitmap
(sorry limited to links I can add)
Unfortunately none of the proposed suggestions/solutions fixed my issue.
This is my code:
private Bitmap createSnapshot (int x, int y, int w, int h) {
int bitmapBuffer[] = new int[w * h];
int bitmapSource[] = new int[w * h];
IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
intBuffer.position(0);
try {
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, intBuffer);
int offset1, offset2;
for (int i = 0; i < h; i++) {
offset1 = i * w;
offset2 = (h - i - 1) * w;
for (int j = 0; j < w; j++) {
int texturePixel = bitmapBuffer[offset1 + j];
int blue = (texturePixel >> 16) & 0xff;
int red = (texturePixel << 16) & 0x00ff0000;
int pixel = (texturePixel & 0xff00ff00) | red | blue;
bitmapSource[offset2 + j] = pixel;
}
}
} catch (GLException e) {
return null;
}
return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
}
I'm using OpenGL 2. For bitmap compression I am using PNG. Tested it using JPEG (quality 100) and the result is the same but slightly worse.
There is also a slight yellowish tint added to the image.
I have camera (the "deprecated" API) and camera PreviewCallback, in which I get frames. I use that to take pictures (not takePicure or PictureCallback, because I want it fast over quality) and save as jpeg, code snippset below:
#Override
public synchronized void onPreviewFrame(byte[] frame, Camera camera) {
YuvImage yuv = new YuvImage(frame, previewFormat, width, height, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out);
out.toByteArray(); // this is my JPEG
// ... save this JPEG
}
I want to draw text, timestamp, into frame. I know how to do this by converting to Bitmap, using Canvas, drawing text and converting to Bitmap again. But this method is relatively slow (it's not biggest issue), but also i need the frame byte array to other things i.e. put into video and I would like to know if there is some method or lib to directly write text into that frame. I don't use preview (or rather, I use dummy preview), I'm asking how to change the frame byte array. I bet I'd need to do some mumble-jumble into byte array to make it work. The frame is in standard(?) format (YUV420/NV21).
Edit:
I managed to get a working function (getNV21). Of course it is far from efficient at it creates new Bitmap every frame and draws text to it but at least i have something I can work on, directly into yuv image. Still, answers would be appreciated.
private Bitmap drawText(String text, int rotation) {
Bitmap bitmap = Bitmap.createBitmap(maxWidth, maxHeight, Bitmap.Config.ARGB_8888);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.rgb(255, 255, 255));
paint.setStrokeWidth(height/36);
paint.setTextSize(height/36);
paint.setShadowLayer(5f, 0f, 0f, Color.BLACK);
paint.setTypeface(Typeface.MONOSPACE);
if (rotation == 0 || rotation == 180) {
canvas.rotate(rotation,width/2,height/2);
} else {
canvas.translate(Math.abs(width-height), 0);
int w = width/2 - Math.abs(width/2-height/2);
int h = height/2;
canvas.rotate(-rotation,w,h);
}
canvas.drawText(text, 10, height-10, paint);
return bitmap;
}
byte [] getNV21(byte[] yuv, int inputWidth, int inputHeight, Bitmap scaled) {
int [] argb = new int[inputWidth * inputHeight];
scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
scaled.recycle();
return yuv;
}
private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
final int frameSize = width * height;
int yIndex = 0;
int uvIndex = frameSize;
int a, R, G, B, Y, U, V;
int index = 0;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++,index++,yIndex++) {
a = (argb[index] & 0xff000000) >> 24; // a is not used obviously
R = (argb[index] & 0xff0000) >> 16;
G = (argb[index] & 0xff00) >> 8;
B = (argb[index] & 0xff) >> 0;
if (R == 0 && G == 0 && B == 0) {
if (j % 2 == 0 && index % 2 == 0) uvIndex+=2;
continue;
}
// well known RGB to YUV algorithm
Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
// meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
// pixel AND every other scanline.
yuv420sp[yIndex] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (j % 2 == 0 && index % 2 == 0) {
yuv420sp[uvIndex] = (byte)((V<0) ? 0 : ((V > 255) ? 255 : V));
yuv420sp[uvIndex] = (byte)((U<0) ? 0 : ((U > 255) ? 255 : U));
}
}
}
}
I want to convert white background to transparent background in android bitmap.
My situation:
Original Image : I cannot post a image
public Bitmap replaceColor(Bitmap src){
if(src == null)
return null;
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
src.getPixels(pixels, 0, width, 0, 0, width, height);
for(int x = 0;x < pixels.length;++x){
pixels[x] = ~(pixels[x] << 8 & 0xFF000000) & Color.BLACK;
}
Bitmap result = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
return result;
}
Processing After
It was detect pixel to pixel, one by one.
It's good but this bitmap image doesn't remain original color.
So, I append code to filter.
if (pixels[x] == Color.white)
public Bitmap replaceColor(Bitmap src){
if(src == null)
return null;
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
src.getPixels(pixels, 0, width, 0, 0, width, height);
for(int x = 0;x < pixels.length;++x){
if(pixels[x] == Color.WHITE){
pixels[x] = ~(pixels[x] << 8 & 0xFF000000) & Color.BLACK;
}
}
Bitmap result = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
return result;
}
Processing After,
But, this picture can not remove completely color white.
So, It is not pretty.
I really want remove white background in android bitmap
My code is following in under stackoverflow article.
Android bitmap mask color, remove color
public Bitmap replaceColor(Bitmap src) {
if (src == null)
return null;
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
src.getPixels(pixels, 0, 1 * width, 0, 0, width, height);
for (int x = 0; x < pixels.length; ++x) {
// pixels[x] = ~(pixels[x] << 8 & 0xFF000000) & Color.BLACK;
if(pixels[x] == Color.WHITE) pixels[x] = 0;
}
return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
}
Just replace one line as in code above and it should do what you want it to - replace white color with transperent
Working on Mi Note 7 - Oreo
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 am trying to draw the 'Y' component as greyscale from the image I get from the Camera via onPreviewFrame.
I am using the version of Canvas.drawBitmap that takes an array of 'colors' as a parameter. The Android docs don't mention what format the Color is in, so I'm assuming ARGB 8888.
I do get an image showing up, but it is showing up with an odd Yellow tint.
Here is my code below:
public void onPreviewFrame(byte[] bytes, Camera camera) {
Canvas canvas = null;
try {
synchronized(mSurfaceHolder) {
canvas = mSurfaceHolder.lockCanvas();
Size size = camera.getParameters().getPreviewSize();
int width = size.width;
int height = size.height;
if (mHeight * mWidth != height * width)
{
mColors = new int[width * height];
mHeight = height;
mWidth = width;
Log.i(TAG, "prewviw size = " + width + " x " + height);
}
for (int x = 0; x < width; x ++) {
for (int y = 0; y < height; y++) {
int yval = bytes[x + y * width];
mColors[x + y * width] = (0xFF << 24) | (yval << 16) | (yval << 8) | yval;
}
}
canvas.drawBitmap(mColors, 0, width, 0.f, 0.f, width, height, false, null);
}
}
finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
I've also tried using another version of Canvas.drawBitmap that takes a Bitmap as a parameter. I constructed the Bitmap in a similar way from the same array and I told it to use ARGB explicitly. But it still ended up being tinted Yellow!
What am I doing wrong here?
It's a different approach, but the following works, and don't suffer for from the color problems of your solution:
YuvImage yuv = new YuvImage(data, previewFormat, size.width, size.height, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, size.width, size.height), 50, out);
byte[] bytes = out.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
canvas.drawBitmap(bitmap, null, new Rect(0, 0, size.width, size.height), null);
Note: This probably needs to allocate data for every frame, which your solution doesn't.