I am using the following code to get the R,G,B values for a grayscale image(they all will be the same)
But the output gives me negative values.
Why is this so? I am totally confused.
for (int i=0;i<bb.getWidth();i++){
for (int j=0;j<bb.getHeight();j++){
long temp=bb.getPixel(i, j);
int a=(int)temp;
ByteBuffer b = ByteBuffer.allocate(4);
b.putInt(a );
byte[] result = b.array();
Log.d("gray value is=", String.valueOf((int)result[1]));
// Log.d("gray value is=", String.valueOf(getLastBytes(a)));
}
}
Here result[1] should correspond to the 'R' value. So how is it negative?
Try this
long temp=bb.getPixel(i, j);
R = Color.red(temp);
G = Color.green(temp);
B = Color.blue(temp);
It is because of all type changes, the casting associated with it, and the fact that those types are signed.
First of all, the initial return value of getPixel() is an int (32-bits or 4 bytes, one byte for ARGB). Placing it into a long seems unnecessary here, because you just cast it back into an int. But so far so good.
When you get the byte array, you are correct that the four bytes for ARGB are in order so result[1] should be the Red value. However, when you implicitly cast that byte into an int in your log statement, a problem happens.
Because the int is four bytes long, and both int and byte are signed types, sign extension applies to the result, i.e.
The byte 0x48 will extend into an int as 0x00000048
The byte 0x88 will extend into an int as 0xFFFFFF88
So when you print the result to the log, it interprets the decimal value of your data to be a negative number. Admittedly, even if you didn't cast and just printed the value of byte, that type is still signed (i.e. it goes from -128 to 127 and not 0 to 255, and 0x88 means -120), so the log output would be the same.
If you want to log out the actual 0-255 value, modify your code to bitmask the pixel data to defeat sign extension in the int:
int red = (int)(result[1] & 0xFF);
Log.d("gray value is=", String.valueOf(red));
Related
I am trying to divide two integer and get a decimal number
I am keep getting 0 result when I divide 10/29
I would like to get 0.34
my code is :
private int totalCount_Games;
private int totalCount_Hints_Statistics;
double avgHints;
avgHints=totalCount_Hints_Statistics/totalCount_Games;
In Java, when you divide two integers, the result is another integer.
In your case, 10/29 will result in 0 as you mentioned. If you want to get the results of these in floating digits, then change the above two integers to float or double.
In that case, the result for the above calculation will be 0.34.
PS: This is really basic. You should do more research in the official java site for documentation on datatypes.
The result of a int division is another int, rounded.
Casting int to double (or float) in the expression part will make the division occurs on doubles instead of int, note that this is different from casting the result from the int division to double.
int i = 5, b = 10;
double result = ((double)i)/((double)b);
result is 0.5
The above code will result in 0.0 as int/int will always be a int which is further type casted into double thus output is 0.0
Use the below code, Using big decimal for Rounding
double totalCount_Games_double = totalCount_Games;
double totalCount_Hints_Statistics_double = totalCount_Hints_Statistics;
double value = totalCount_Hints_Statistics/totalCount_Games_double;
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(2, RoundingMode.HALF_UP);
Suppose i have background color set as #AF000000(AARRGGBB) in android.
I want value of alpha channel(AA) in decimal (0-255) which is going to be 175.
How to accomplish that programatically?
Here is a pure Java solution that doesn't use the Android-specific getAlpha() method.
Do you have this value stored in a String or an int? If you have it in a String, first get rid of the # character then convert it to an int:
String hexString = "#05000000";
int color = Integer.parseInt(hexString.replaceAll("#", ""), 16);
Then we need to make some bit manipulation. This hex color representation means (in ARGB mode) you have the values #AARRGGBB. That is 2 bytes for each channel, including the alpha one. To get just the alpha channel (the AA part of the hex value), we need to "push it 6 bytes to the right" (Java is a Big Endian languange) so we can end up with something like #000000AA. Since each byte is comprised of 8 bits, we have to "push" the alpha values 6 * 8 = 24 bits "to the right":
int alpha = color >> 24;
This process is called Bit Shifting. All the rightmost RGB values are discarded and we then have the alpha value stored in an int with a decimal value between 0 and 255.
EDIT: If you already have the alpha value as returned from getAlpha(), you can always multiply it by 255 and floor it:
int alpha = Math.floor(myView.getAlpha() * 255);
You may convert your HEX to Decimal Simply use,
int i= Integer.parseInt(HEX_STR,16);
or if you need to pass long hex value means use like,
public static long hexToLong(String hex) {
return Long.parseLong(hex, 16);
}
To get each individual int value:
int argb = Color.parseColor("#00112233");
int alpha = 0xFF & (argb >> 24);
int red = 0xFF & (argb >> 16);
int green = 0xFF & (argb >> 8);
int blue = 0xFF & (argb >> 0);
I want to read some bytes of a file, from "offset" and it'length is "size". So i use FIleInputStream and this code:
byte[] data = new byte[size];
FileInputStream fis=new FileInputStream(inputFile);
System.out.println("offset:"+offset+","+"size:"+size);
fis.read(data, offset, size);
So I have true values of offset and size, but I receiver error: indexoutofbound. I don't understand. Can anyone show how I fall and whether have any other right way to do it?
The JavaDoc tells you:
public int read(byte[] b, int off, int len) throws IOException
Throws:
IndexOutOfBoundsException - If off is negative, len is negative, or len is
greater than b.length - off
be aware that the indexes are 0-based.
I'm not too sure what you've got in offset here, but offset is meant to be the offset (i.e. starting index) in the array where you want to store the bytes.
So you're trying to read size bytes into your array, starting at position offset - hence an IndexOutOfBounds if offset > 0. You need offset to be 0, and it should work.
What am I doing:
I add four integers in C. on the way, I lose information.
See code below:
//c-file
jbyte *inputByteArray = (*env)->GetDirectBufferAddress (env, obj);
// checked every value, also sizeof(val1)= 4 etc...
int val1 = (int) *(inputByteArray + 1); //120
int val2 = (int) *(inputByteArray + 2); //120
int val3 = (int) *(inputByteArray + 3); //180
int val4 = (int) *(inputByteArray + 4); //180
int result = val1 + val2 + val3 + val4;
return result;
//return type is int
//output: 88, should be 600
// 88 binary: 0000 0000 0101 1000
//600 binary: 0000 0010 0101 1000
The special thing about this is the following, which might be causing the problem:
The 4 values for the input are from a handed-over Buffer from Java, which is a direct ByteBuffer. It is directly allocated in Java, in order NOT be moved by the garbage collector. On the c-side I hand the buffer over via pointer from "GetDirectBufferAddress" (see code), and the single values do match the values in the array.
Does anyone know about this strange behaviour?
When I am using IntBuffer to hand over the numbers, it works by the way.
Im working here on performance, so I want small buffers and my data values are small enough to use ByteBuffer. (this is only a fragment of a larger calculation on the c-code side)
Since this is on Android, I did not manage to debug into the c-code...
Edit: I am using eclipse/SDK/NDK in current versions on Android 3.2.1 testing device
As #Deucalion says your array looks dodgy. Unless you are trying to add array1, array[2], array[3] and array[4]. Without using array[0].
Anyway assuming that what you have done is what you intend, your value is exactly what you will get.
Byte range is -128 t0 +127. And so 180 is actually stored as -76. And voila !!
I am trying to access the raw data of a Bitmap in ARGB_8888 format on Android, using the copyPixelsToBuffer and copyPixelsFromBuffer methods. However, invocation of those calls seems to always apply the alpha channel to the rgb channels. I need the raw data in a byte[] or similar (to pass through JNI; yes, I know about bitmap.h in Android 2.2, cannot use that).
Here is a sample:
// Create 1x1 Bitmap with alpha channel, 8 bits per channel
Bitmap one = Bitmap.createBitmap(1,1,Bitmap.Config.ARGB_8888);
one.setPixel(0,0,0xef234567);
Log.v("?","hasAlpha() = "+Boolean.toString(one.hasAlpha()));
Log.v("?","pixel before = "+Integer.toHexString(one.getPixel(0,0)));
// Copy Bitmap to buffer
byte[] store = new byte[4];
ByteBuffer buffer = ByteBuffer.wrap(store);
one.copyPixelsToBuffer(buffer);
// Change value of the pixel
int value=buffer.getInt(0);
Log.v("?", "value before = "+Integer.toHexString(value));
value = (value >> 8) | 0xffffff00;
buffer.putInt(0, value);
value=buffer.getInt(0);
Log.v("?", "value after = "+Integer.toHexString(value));
// Copy buffer back to Bitmap
buffer.position(0);
one.copyPixelsFromBuffer(buffer);
Log.v("?","pixel after = "+Integer.toHexString(one.getPixel(0,0)));
The log then shows
hasAlpha() = true
pixel before = ef234567
value before = 214161ef
value after = ffffff61
pixel after = 619e9e9e
I understand that the order of the argb channels is different; that's fine. But I don't
want the alpha channel to be applied upon every copy (which is what it seems to be doing).
Is this how copyPixelsToBuffer and copyPixelsFromBuffer are supposed to work? Is there any way to get the raw data in a byte[]?
Added in response to answer below:
Putting in buffer.order(ByteOrder.nativeOrder()); before the copyPixelsToBuffer does change the result, but still not in the way I want it:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff
Seems to suffer from essentially the same problem (alpha being applied upon each copyPixelsFrom/ToBuffer).
One way to access data in Bitmap is to use getPixels() method. Below you can find an example I used to get grayscale image from argb data and then back from byte array to Bitmap (of course if you need rgb you reserve 3x bytes and save them all...):
/*Free to use licence by Sami Varjo (but nice if you retain this line)*/
public final class BitmapConverter {
private BitmapConverter(){};
/**
* Get grayscale data from argb image to byte array
*/
public static byte[] ARGB2Gray(Bitmap img)
{
int width = img.getWidth();
int height = img.getHeight();
int[] pixels = new int[height*width];
byte grayIm[] = new byte[height*width];
img.getPixels(pixels,0,width,0,0,width,height);
int pixel=0;
int count=width*height;
while(count-->0){
int inVal = pixels[pixel];
//Get the pixel channel values from int
double r = (double)( (inVal & 0x00ff0000)>>16 );
double g = (double)( (inVal & 0x0000ff00)>>8 );
double b = (double)( inVal & 0x000000ff) ;
grayIm[pixel++] = (byte)( 0.2989*r + 0.5870*g + 0.1140*b );
}
return grayIm;
}
/**
* Create a gray scale bitmap from byte array
*/
public static Bitmap gray2ARGB(byte[] data, int width, int height)
{
int count = height*width;
int[] outPix = new int[count];
int pixel=0;
while(count-->0){
int val = data[pixel] & 0xff; //convert byte to unsigned
outPix[pixel++] = 0xff000000 | val << 16 | val << 8 | val ;
}
Bitmap out = Bitmap.createBitmap(outPix,0,width,width, height, Bitmap.Config.ARGB_8888);
return out;
}
}
My guess is that this might have to do with the byte order of the ByteBuffer you are using. ByteBuffer uses big endian by default.
Set endianess on the buffer with
buffer.order(ByteOrder.nativeOrder());
See if it helps.
Moreover, copyPixelsFromBuffer/copyPixelsToBuffer does not change the pixel data in any way. They are copied raw.
I realize this is very stale and probably won't help you now, but I came across this recently in trying to get copyPixelsFromBuffer to work in my app. (Thank you for asking this question, btw! You saved me tons of time in debugging.) I'm adding this answer in the hopes it helps others like me going forward...
Although I haven't used this yet to ensure that it works, it looks like that, as of API Level 19, we'll finally have a way to specify not to "apply the alpha" (a.k.a. premultiply) within Bitmap. They're adding a setPremultiplied(boolean) method that should help in situations like this going forward by allowing us to specify false.
I hope this helps!
This is an old question, but i got to the same issue, and just figured out that the bitmap byte are pre-multiplied, you can set the bitmap (as of API 19) to not pre-multiply the buffer, but in the API they make no guarantee.
From the docs:
public final void setPremultiplied(boolean premultiplied)
Sets whether the bitmap should treat its data as pre-multiplied.
Bitmaps are always treated as pre-multiplied by the view system and Canvas for performance reasons. Storing un-pre-multiplied data in a Bitmap (through setPixel, setPixels, or BitmapFactory.Options.inPremultiplied) can lead to incorrect blending if drawn by the framework.
This method will not affect the behaviour of a bitmap without an alpha channel, or if hasAlpha() returns false.
Calling createBitmap or createScaledBitmap with a source Bitmap whose colors are not pre-multiplied may result in a RuntimeException, since those functions require drawing the source, which is not supported for un-pre-multiplied Bitmaps.