Im currently working on a program which applies edge detection to an area of the preview frame. I have used previewcallback and got my cropped bitmap, have converted to grayscale using the following method.
int height1=120;
int width2=120;
final Bitmap resizedBitmap = Bitmap.createBitmap(bmp, 260, 15,
width2, height1);
try {
int bWidth = resizedBitmap.getWidth();
int bHeight = resizedBitmap.getHeight();
int[] pixels = new int[bWidth * bHeight];
resizedBitmap.getPixels(pixels, 0, bWidth, 0, 0, bWidth, bHeight);
for (int y = 0; y < bHeight; y++){
for (int x = 0; x < bWidth; x++){
int index = y * bWidth + x;
int R = (pixels[index] >> 16) & 0xff; //bitwise shifting
int G = (pixels[index] >> 8) & 0xff;
int B = pixels[index] & 0xff;
int gray = (int) (.299 * R + .587 * G + .114 * B);
}
}
I am very new to this, and would like to know whether gray is a 2D array of 120x120 pixels, or whether the value of gray is just being overwritten for each loop.
Apologies if this is very basic
Well, maybe I'm missing something, but as far as I can see gray is overwritten. You'd need something like
int[][] gray = new int[width][height];
// start loop
In the Loop:
gray[x][y] = ...;
Related
I post this question as I didn’t find answers solving my issues in the following posts :
Converting preview frame to bitmap
Android decodeYUV420SP results in green images?
Displaying YUV Image in Android
I got my data and cameraResolution (after selecting BestPreviewSize) from :
public void onPreviewFrame(byte[] data, Camera camera) {
Point cameraResolution = configManager.getCameraResolution();
Handler thePreviewHandler = previewHandler;
if (cameraResolution != null && thePreviewHandler != null) {
Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
cameraResolution.y, data);
message.sendToTarget();
previewHandler = null;
} else {
Log.d(TAG, "Got preview callback, but no handler or resolution available");
}
}
Later from conversion to RGB, I use :
int[] pixels=new int[yuvData.length];
// int size = dataWidth*dataHeight;
// int[] pixels=new int[size]; replacing yuvData.length by size is not working too
pixels=decodeYUV420SP(pixels,yuvData, dataWidth, dataHeight);
Bitmap bitmap = Bitmap.createBitmap(dataWidth, dataHeight, Bitmap.Config.RGB_565);
bitmap.setPixels(pixels, 0, dataWidth, 0, 0, dataWidth, dataHeight);
I tried 2 methods, one with RenderScript and one with decodeYUV420SP:
public Bitmap convertYUV420_NV21toRGB8888_RenderScript(byte [] data,int W, int H, CaptureActivityOCR fragment) {
// https://stackoverflow.com/questions/20358803/how-to-use-scriptintrinsicyuvtorgb-converting-byte-yuv-to-byte-rgba
RenderScript rs;
ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
rs = RenderScript.create(fragment.getActivity());
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs)); //Create an intrinsic for converting YUV to RGB.
Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(data.length);
Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT); //an Allocation will be populated with empty data when it is first created
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(W).setY(H);
Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT); //an Allocation will be populated with empty data when it is first created
in.copyFrom(data);//Populate Allocations with data.
yuvToRgbIntrinsic.setInput(in); //Set the input yuv allocation, must be U8(RenderScript).
yuvToRgbIntrinsic.forEach(out); //Launch the appropriate kernels,Convert the image to RGB.
Bitmap bmpout = Bitmap.createBitmap(W, H, Bitmap.Config.ARGB_8888);
out.copyTo(bmpout); //Copy data out of Allocation objects.
return bmpout;
}
or
int[] decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
Log.e("camera", " decodeYUV420SP ");
// TODO Auto-generated method stub
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
return rgb;
}
But still I don't know why I got some good image but in green. (I have another similar method in black and white which is working)
In addition of the links above,i tried all posts linked to this subject on SO , so If someone could help for another tip please? I may be confused with the size to apply?
Somewhere after OnPreviewFrame method and before conversion to RGB method, I use another method with instruction below as I need to rotate the data received. I am wondering if this is not the origin of issue :
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;
Please help me?
I think your rotation routine is wrong.
It works for Luma (Y), hence you get good results for black and white picture but not for chroma. If width and height are dimensions of your picture you do not rotate chroma values at all.
So you will need to add second loop for chroma and move pairs of bytes (V and U).
If you tried it without this rotation and you still have greenish picture the problem might be also in your decodeYUV420SP function.
There is not one universal formula for yuv to rgba conversion. It must match the opposite one.
Look here http://www.codeproject.com/Articles/402391/RGB-to-YUV-conversion-with-different-chroma-sampli and here http://www.fourcc.org/fccyvrgb.php
This one works for me
B = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)
It seems to be the same as you are using only float version so you may try other.
I suggest to rotate the bitmap after RenderScript rotation:
if (bmpout == null) {
bmpout = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
rgbaAllocation.copyTo(bmpout);
if (matrixPostRotate90 == null) {
matrixPostRotate90 = new Matrix();
matrixPostRotate90.postRotate(90);
}
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpout, 0, 0, w, h, matrixPostRotate90, true);
I have two bitmaps, namely bm1 and bm2, and I'd like to create (as quick as possible) another bitmap which is a fading mix between bm1 and bm2 where bm1 is weighted with weight and bm2 with 1-weight.
My current implementation is as follows:
private Bitmap Fade(Bitmap bm1, Bitmap bm2, double weight)
{
int width = bm1.getWidth();
int height = bm1.getHeight();
if (width != bm2.getWidth() || height != bm2.getHeight())
return null;
Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
int pix_s = bm1.getPixel(x, y);
int pix_d = bm2.getPixel(x, y);
int r_s = (pix_s >> 16) & 0xFF;
int g_s = (pix_s >> 8) & 0xFF;
int b_s = pix_s & 0xFF;
int r_d = (pix_d >> 16) & 0xFF;
int g_d = (pix_d >> 8) & 0xFF;
int b_d = pix_d & 0xFF;
int r = (int)((1-weight) * r_s + weight * r_d);
int g = (int)((1-weight) * g_s + weight * g_d);
int b = (int)((1-weight) * b_s + weight * b_d);
int pix = 0xff000000 | (r << 16) | (g << 8) | b;
bm.setPixel(x, y, pix);
}
return bm;
}
As you can see, it simply sets the RGB components of each pixel of the generated image with an interpolation between the corresponding RGB components of pixels in bm1 and bm2.
However, this function is very slow as it scans all the pixels of the two input bitmaps.
Is there a more efficient way to do the same?
For instance, by someway acting onto the transparency attributes?
Here it shows you how to change the opacity of your bitmap, it changes the alpha channel of the RGB color.
and Here it shows you how to overlay in a canvas two bitmaps
If I make a picture with mine samsung galaxy s2, the picture is 3264 x 2448 px.
I want to use a color range check on that, but it doesn't work.
However, if I make the picture smaller for example, 2500 x 2500 (so less pixels), then it does work. But I want the use the picture size of the galaxy s2 (3264 x 2448).
I think it is a memory issue?
I don't exactly know anymore what the limit is.
But is their another way to "bypass" this issue?
This is a piece of code, how I do it now:
bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.four_colors);
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] pixels = new int[width * height];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
int index = y * width + x;
int R = (pixels[index] >> 16) & 0xff; //bitwise shifting
int G = (pixels[index] >> 8) & 0xff;
int B = pixels[index] & 0xff;
total++;
if ((G > R)&&(G > B)){
counter++;
}
}
}
It crashes because the picture is to big, smaller pics work.
So is their something to "bypass" this issue? instead of using smaller images :)
I tried some other things, without succes, I try to explain what I tried.
I tried to "cut" the image in two, and then scan it separate (didn't work).
and I tried to only scan the half of it (1632 x 1224) then rotate the image (180 degrees) and scan it again, but also this didn't work out.
Hit me :)
When playing around with huge images you really should be using the BitmapRegionDecoder to process it in chunks.
Edit - now with a simple example:
try {
// Processes file in res/raw/huge.jpg or png
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(getResources().openRawResource(R.raw.huge), false);
try {
final int width = decoder.getWidth();
final int height = decoder.getHeight();
// Divide the bitmap into 1024x768 sized chunks and process it.
int wSteps = (int) Math.ceil(width / 1024.0);
int hSteps = (int) Math.ceil(height / 768.0);
Rect rect = new Rect();
long total = 0L, counter = 0L;
for (int h = 0; h < hSteps; h++) {
for (int w = 0; w < wSteps; w++) {
int w2 = Math.min(width, (w + 1) * 1024);
int h2 = Math.min(height, (h + 1) * 768);
rect.set(w * 1024, h * 768, w2, h2);
Bitmap bitmap = decoder.decodeRegion(rect, null);
try {
int bWidth = bitmap.getWidth();
int bHeight = bitmap.getHeight();
int[] pixels = new int[bWidth * bHeight];
bitmap.getPixels(pixels, 0, bWidth, 0, 0, bWidth, bHeight);
for (int y = 0; y < bHeight; y++){
for (int x = 0; x < bWidth; x++){
int index = y * bWidth + x;
int R = (pixels[index] >> 16) & 0xff; //bitwise shifting
int G = (pixels[index] >> 8) & 0xff;
int B = pixels[index] & 0xff;
total++;
if ((G > R)&&(G > B)){
counter++;
}
}
}
} finally {
bitmap.recycle();
}
}
}
} finally {
decoder.recycle();
}
You can limit the amount of pixel data of the image that you are loading at once by using some of the parameters of the getPixels method. I did something very similar, and I would read them one row at a time, for example.
for(int y = 0; y < height; y++)
{
bitmap.getPixels(pixels, 0, width, 0, y, width, 1);
I have Black and White picture - RGB 565, 200x50.
as I can calculate the intensity 0..255 of each pixel?
That's what I meant, thanks. ma be this can someone help. I get Intensity 0..255 of each pixel and get the average.
Bitmap cropped = Bitmap.createBitmap(myImage, 503, 270,myImage.getWidth() - 955, myImage.getHeight() - 550);
Bitmap cropped2 = Bitmap.createBitmap(cropped, 0, 0,cropped.getWidth() , cropped.getHeight() / 2 );
final double GS_RED = 0.35;
final double GS_GREEN = 0.55;
final double GS_BLUE = 0.1;
int R, G, B;
int result = 0;
int g = 0;
int ff;
for(int x = 0; x < cropped2.getWidth(); x++)
{
int ff_y = 0;
for(int y = 0; y < cropped2.getHeight(); y++)
{
Pixel = cropped.getPixel(x, y);
R = Color.red(Pixel);
G = Color.green(Pixel);
B = Color.blue(Pixel);
ff = (int)(GS_RED * R + GS_GREEN * G + GS_BLUE * B) ;
ff_y += ff;
}
result += ff_y;
g = result / (cropped2.getWidth()*cropped2.getHeight());
}
Toast.makeText(this, "00" + g, Toast.LENGTH_LONG).show();
You could try to convert it using a color model with a luminance and two chrominance components. The luminance component accounts for the brightness while the two chrominance components represent the colors. You might want to check out http://en.wikipedia.org/wiki/YUV.
Otherwise: If I'm correct, the white over gray to black colors have equal values in a RGB format which has the same number of bits for each channel (e.g. from (0, 0, 0) to (255, 255, 255)). Assuming this is true you could just take one of the channels to represent the intensity as you could determine the other values from that. No guarantee if this works.
Edit:
I wrote a snippet demonstrating the idea described above. I used RGB888 but it should also work with RGB 565 after dropping the assertion and modifying the maximum intensity of a pixel as described in the comments. Mind that there are only 2^5 different intensity levels per pixel. Hence you might want to use a scaled version of the average intensity.
I tested it using images from http://www.smashingmagazine.com/2008/06/09/beautiful-black-and-white-photography/. I hope it will work out porting this to android for you.
// 2^5 for RGB 565
private static final int MAX_INTENSITY = (int) Math.pow(2, 8) - 1;
public static int calculateIntensityAverage(final BufferedImage image) {
long intensitySum = 0;
final int[] colors = image.getRGB(0, 0, image.getWidth(),
image.getHeight(), null, 0, image.getWidth());
for (int i = 0; i < colors.length; i++) {
intensitySum += intensityLevel(colors[i]);
}
final int intensityAverage = (int) (intensitySum / colors.length);
return intensityAverage;
}
public static int intensityLevel(final int color) {
// also see Color#getRed(), #getBlue() and #getGreen()
final int red = (color >> 16) & 0xFF;
final int blue = (color >> 0) & 0xFF;
final int green = (color >> 8) & 0xFF;
assert red == blue && green == blue; // doesn't hold for green in RGB 565
return MAX_INTENSITY - blue;
}
I want to get RGB values of a Bitmap on Android but I can't do this so far. My aim is to obtain RGB values for each pixel of a Bitmap. Is there any specific function for Android or anything else?
Also I wonder that do I need colorMatrix() function?
It is very important for my project.
Bitmap#getPixel(x, y) returns an int with the colour values and alpha value embedded into it.
int colour = bitmap.getPixel(x, y);
int red = Color.red(colour);
int green = Color.green(colour);
int blue = Color.blue(colour);
int alpha = Color.alpha(colour);
This may be slightly late, but to clear up the confusion with the use of &0xff:
In Java ints are 32 bits, so the (A)RGB values for each pixel are packed in 4 bytes.
In other words, a pixel with the values R(123), G(93), B(49) = FF7B 5D31 in the ARGB_8888 model. Where Alpha = FF, R = 7B, G = 5D, B = 31. But this is stored as an int as -8692431.
So, to extract the Green value from -8692431, we need to shift the 5D by 8 bits to the right, as you know. This gives 00FF 7B5D. So, if we were just to take that value we would be left with 16743261 as our Green value. Therefore, we bitwise-and that value with the mask of 0xFF (which is equivalent to 0000 00FF) and will result in 00FF 7B5D being 'masked' to 0000 005D. So we have extracted our Green value of 5D (or 93 decimal).
We can use the same mask of 0xFF for each extraction because the values have all been shifted to expose the desired two bytes as the least significant. Hence the previously suggested code of:
int p = pixel[index];
int R = (p >> 16) & 0xff;
int G = (p >> 8) & 0xff;
int B = p & 0xff;
If it makes it clearer, you can perform the equivalent operation of:
int R = (p & 0xff0000) >> 16;
int G = (p & 0x00ff00) >> 8;
int B = (p & 0x0000ff) >> 0;
For brevity, the extra 0s can be dropped, and it can be written as
int R = (p & 0xff0000) >> 16;
int G = (p & 0xff00) >> 8;
int B = p & 0xff;
Note however, that alternative colour models may be used, such as RGB_555 which stores each pixel as just 2 bytes, with varying precision for the RGB channels. So you should check the model that your bitmap is using before you perform the extraction, because the colours may be stored differently.
This is how I am trying to get that value. Use bitmap.getPixel() to get the corresponding bitmap in
integer array. By using bitwise rotation operation, we will get RGB values.
int[] pix = new int[picw * pich];
bitmap.getPixels(pix, 0, picw, 0, 0, picw, pich);
int R, G, B,Y;
for (int y = 0; y < pich; y++){
for (int x = 0; x < picw; x++)
{
int index = y * picw + x;
int R = (pix[index] >> 16) & 0xff; //bitwise shifting
int G = (pix[index] >> 8) & 0xff;
int B = pix[index] & 0xff;
//R,G.B - Red, Green, Blue
//to restore the values after RGB modification, use
//next statement
pix[index] = 0xff000000 | (R << 16) | (G << 8) | B;
}}
Arbitrary Bitmap Color Handling
You can read about the various Color methods here that will extract the components of color from a pixel int.
You might want to apply a filter to the bitmap, and return a byte array. Otherwise, you can cut this example down to the for-loop and roll through the pixels generating your array of bytes.
private byte[] rgbValuesFromBitmap(Bitmap bitmap)
{
ColorMatrix colorMatrix = new ColorMatrix();
ColorFilter colorFilter = new ColorMatrixColorFilter(
colorMatrix);
Bitmap argbBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(argbBitmap);
Paint paint = new Paint();
paint.setColorFilter(colorFilter);
canvas.drawBitmap(bitmap, 0, 0, paint);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int componentsPerPixel = 3;
int totalPixels = width * height;
int totalBytes = totalPixels * componentsPerPixel;
byte[] rgbValues = new byte[totalBytes];
#ColorInt int[] argbPixels = new int[totalPixels];
argbBitmap.getPixels(argbPixels, 0, width, 0, 0, width, height);
for (int i = 0; i < totalPixels; i++) {
#ColorInt int argbPixel = argbPixels[i];
int red = Color.red(argbPixel);
int green = Color.green(argbPixel);
int blue = Color.blue(argbPixel);
rgbValues[i * componentsPerPixel + 0] = (byte) red;
rgbValues[i * componentsPerPixel + 1] = (byte) green;
rgbValues[i * componentsPerPixel + 2] = (byte) blue;
}
return rgbValues;
}
One for statement less :D
imagen.getPixels(pix, 0, picw, 0, 0, picw, pich);
for (i = 0; i < pix.length; i++) {
r = (pix[i]) >> 16 & 0xff;
g = (pix[i]) >> 8 & 0xff;
b = (pix[i]) & 0xff;
}
In addition to #Cobbles' answer, you can also use Bitmap#getColor(x, y) and a Color object.
for (int y = 0; y < bitmap.getHeight(); y++) {
for (int x = 0; x < bitmap.getWidth(); x++) {
Color color = bitmap.getColor(x, y);
float red = color.red();
float green = color.green();
float blue = color.blue();
float alpha = color.alpha();
Log.d(TAG, String.format(
"(R, G, B, A) = (%f, %f, %f, %f)", red, green, blue, alpha
));
}
}
With above code, you can get float (0..1) RGBA values. When you just want to get integer (0..255) values, #Cobble's way is rather straight forward (recommended). Though there is still a way to get integer values with this Color object by using Color#toArgb.
for (int y = 0; y < bitmap.getHeight(); y++) {
for (int x = 0; x < bitmap.getWidth(); x++) {
int color = bitmap.getColor(x, y).toArgb();
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
int alpha = Color.alpha(color);
Log.d(TAG, String.format(
"(R, G, B, A) = (%3d, %3d, %3d, %3d)", red, green, blue, alpha
));
}
}