I am working with Local Binary Patterns (LBP) which produce numbers in the range 0-255.
That means that they can fit in a byte (256 different values may be included into a byte). So that explains why many (if not all) implementation in java I have found uses byte[] to store these values.
The problem is that since I am interested in the rank of these values when converted to byte (from int for example) they do not keep the previous rank they had (as int for example) since byte are signed (as all but chars in java I think) and so the greater 128 values (127 and after) of the range 0-255 becomes negative numbers. Furthermore I think they are inverted in order (the negative ones).
Some examples to be more specific:
(int) 0 = (byte) 0
(int) 20 = (byte) 20
(int) 40 = (byte) 40
(int) 60 = (byte) 60
(int) 80 = (byte) 80
(int) 100 = (byte) 100
(int) 120 = (byte) 120
(int) 140 = (byte) -116
(int) 160 = (byte) -96
(int) 180 = (byte) -76
(int) 200 = (byte) -56
(int) 220 = (byte) -36
(int) 240 = (byte) -16
My question is whether there is a specific way to maintain the order of int values when converted to byte (meaning 240 > 60 should hold true in byte also -16 < 60!) while keeping memory needs minimum (meaning use only 8bits if that many are required). I know I could consider comparing the byte in a more complex way (for example every negative > positive and if both bytes are negative inverse the order) but I think it's not that satisfactory.
Is there any other way to convert to byte besides (byte) i?
You could subtract 128 from the value:
byte x = (byte) (value - 128);
That would be order-preserving, and reversible later by simply adding 128 again. Be careful to make sure you do add 128 later on though... It's as simple as:
int value = x + 128;
So for example, if you wanted to convert between an int[] and byte[] in a reversible way:
public byte[] toByteArray(int[] values) {
byte[] ret = new byte[values.length];
for (int i = 0; i < values.length; i++) {
ret[i] = (byte) (values[i] - 128);
}
return ret;
}
public int[] toIntArray(int[] values) {
int[] ret = new byte[values.length];
for (int i = 0; i < values.length; i++) {
ret[i] = values[i] + 128;
}
return ret;
}
If you wanted to keep the original values though, the byte comparison wouldn't need to be particularly complex:
int unsigned1 = byte1 & 0xff;
int unsigned2 = byte2 & 0xff;
// Now just compare unsigned1 and unsigned2...
Related
I am wondering how can I turn my 32 character int into a 32-byte array as it is represented.
Example:
I have this int:
int test = 123456789;
And I want to turn it into this:
byte[] Write_Page_Four = new byte[] {
(byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x01, (byte) 0x23, (byte) 0x45,
(byte) 0x67, (byte) 0x89};
Currently, I'm thinking of splitting my int by 2 and just manually assigning them to the byte array but I am having some troubles in doing so, and I believe that this is not the best practice for my problem.
This is what I have ATM, which is returning error and still work on progress though and I could use some advice on it:
String test2 = "01";
String test1 = "0x"+test2;
byte test = Byte.valueOf(test1);
System.out.println("teeeeest-----"+test);
byte[] Write_Page_Four = new byte[] {(byte) test};
And this one is returning an error:
java.lang.NumberFormatException: For input string: "0x01"
What is causing problems
Byte.valueOf doesn't parse data like the Java compiler does: it expects as input as a decimal number.
What you can use however, is Byte.valueOf(String,int) with an arbitrary radix. In that case you can solve it using:
byte test = Byte.valueOf(test2,16); //using test2, not test1
Mind that should not add "0x" in the front. Nevertheless this is an inefficient way to do this.
Ints are 32-bits, not 32-bytes
A second problem is that you state that you can store a number like 12345678901234567890123456789011 into an int. You cannot. An int has 32 bits. This means its representation is limited to more or less 2.1B. So I think you mean you store 12345678901234567890123456789011 in a String?
Number systems
Mind that the number 12345678901234567890123456789011 is not represented internally as (byte) 0x12, (byte) 0x34,... unless you are working with binary coded decimals. This is because a computer uses the binary number system (and thus groups bytes with the hexadecimal representation), whereas humans use the decimal representation. 123456789 for instance will be represented as 0x07,0x5B,0xCD 0x15.
Serializing an int (or other datastructure) using an array of bytes
You can convert an int (and other datatypes) into an array of bytes using this code:
ByteBuffer b = ByteBuffer.allocate(4);
b.putInt(test);
byte[] result = b.array(); //result will be 4 bytes,
//since you can represent any int with four bytes.
Or, in case you want to represent the int like the way you do this, you could use the following method:
int t = test;
byte[] dat = new byte[5];//at most 5 bytes needed
for(int j = 4; test != 0; j--) {
int rm = t%100;
dat[j] = (byte) (rm%10+((rm/10)<<8));
t /= 100;
}
//result is dat
Instead of processing the textual representation of the number I'd recommend to simply calculate the single numbers:
Get two digits of the number each time:
int inp = 1234...;
for(int lowerBound = 1 ; lowerBound < Integer.MAX_VALUE ; lowerBound *= 100)
//twoDigit contains two digits of the input-number
int twoDigit = (inp /lowerBound) % 100;
Transform these two digits into a byte:
byte transform(int i){
if(i == 0)
return 0x00;
int lsd = (i % 10); //least significant digit of the input (decimal)
int msd = (i / 10); //most significant digit
//merge lsd and msd into a single number, where the lower 4 bits are reserved for
//lsd and the higher 4 bits for msd
return lsd | (msd << 4);
}
The complete code would look like this:
import java.util.Arrays;
public class test
{
private static final int BYTES = 4;
public static void main(String[] args){
int v = 12345678;
int at_arr = BYTES - 1;
byte[] result = new byte[BYTES];//int is 32-bit/4 byte long
for(int lowerBound = 1 ; lowerBound < Integer.MAX_VALUE && at_arr > -1; lowerBound *= 100, at_arr--)
result[at_arr] = transformDigits((v / lowerBound) % 100);
for(byte b : result)
System.out.print(" 0x" + Integer.toString(b , 16) + ",");
System.out.println();
}
static byte transformDigits(int i){
if(i == 0)
return 0x00;
int lsd = (i % 10); //least significant digit of the input (decimal)
int msd = (i / 10); //most significant digit
//merge lsd and msd into a single number, where the lower 4 bits are reserved for
//lsd and the higher 4 bits for msd
return (byte) (lsd | (msd << 4));
}
}
This code can be used basically for any integral type, if the types and value of BYTES are updated appropriately.
Here's how to convert an int to a byte[]:
int test = 123456789;
byte[] bytes = new byte[4];
bytes[0] = (byte)(test >> 24);
bytes[1] = (byte)(test >> 16);
bytes[2] = (byte)(test >> 8);
bytes[3] = (byte)test;
System.out.printf("%02x %02x %02x %02x%n", bytes[0], bytes[1], bytes[2], bytes[3]);
Output
07 5b cd 15
You can also inline it, if you want:
int test = 123456789;
byte[] bytes = new byte[] { (byte)(test >> 24),
(byte)(test >> 16),
(byte)(test >> 8),
(byte)test };
System.out.printf("%02x %02x %02x %02x%n", bytes[0], bytes[1], bytes[2], bytes[3]);
I'm trying to subtract one byte from another while making sure no overflow happens, but get unexpected results.
My case is that I have an byte black and white image, of which I want to subtract the background. Hence I need to work with bytes and prevent overflows from happening. I have some difficulty presumably with the signedness of the bytes when subtracting the background image from the other image. Data2 is the background array, and data1 is the other image.
In the following code, I expect data2 array to be subtracted from data1 array. However I get low values when I am sure there should be high ones.
for (int i = 0; i < data1.length; i++) {
if (data1[i] > data2[i]) {
dest[i] = (byte) (data1[i] - data2[i]);
} else {
dest[i] = 0;
}
}
I figured I should make sure data2 byte isn't negative and being added to data1.
So I came to:
for (int i = 0; i < data1.length; i++) {
if (data1[i] > data2[i]) {
dest[i] = (byte) ((int)data1[i] & 0xff - (int)data2[i] & 0xff - 128);
} else {
dest[i] = 0;
}
}
However this also doesn't give the right results.
My thoughts on this currently are:
(byte) ((int)data1[i] & 0xff - (int)data2[i] & 0xff - 128);
(int) cast: make sure bytes are cast to integer.
&0xff: make value unsigned.
- subtraction smaller value from bigger value.
- 128: subtract 128 to make signed again.
(byte): cast back to byte.
I hope I'm doing something stupidly wrong here, or my problem resides somewhere else of which I can't figure out where.
Edit
I seem to have figured out a part of the issue:
data1[i] > data2[i] is handled wrong (in my case) when the bytes are signed. Instead:
if ((data1[i] & 0xff) > (data2[i] & 0xff)) {
seems to produce the right results, instead of the previous comparison.
The point here is that your bytes come from an API that uses 8 bits to encode the light of the pixel, so they range 0; 0xFF. However Java bytes are -128; 127, so any bit pattern after 0x7F will be interpreted as a negative number. For example the bits in 0xF0 are 128 if the byte is unsigned and -16 if interpreted as a signed byte:
System.out.println(0xFF > 0); // true
System.out.println((byte) 0xFF > 0); // false
So when comparing your unsigned bytes you want to promote pixels to int with Byte.toUnsignedInt(byteVal) (or ((int) byteVal) & 0xFF on Java 7).
Always remember Java bytes are signed. If data1[i] is 127, and data2[i] is -128, then data1[i] > data2[i], but data1[i] - data2[i] does not fit into a signed byte; that result is 255.
If you treat the bytes as unsigned, that's fine. That more-or-less means printing them out after using & 0xFF, and such. That will work just fine; it will give the right results if you treat them as unsigned correctly.
To ensure that you only substract non negative bytes from from bytes that are greater you should use :
for (int i = 0; i < data1.length; i++) {
if (data1[i] > data2[i] && data2[i]>=0 ) {
dest[i] = (byte) (data1[i] - data2[i]);
} else {
dest[i] = 0;
}
}
Your second code does not work because the & operator promotes the values to type int.
Consider the case where data1=127 (0x7F) and data2=-128 (0x80).
data1 & 0xff is type int and has value 127 (0x0000007F)
data2 & 0xff is type int and has value 128 (0x00000080)
data1[i] & 0xff - data2[i] & 0xff is type int and has value -1 (0xFFFFFFFF)
(byte)(data1[i] & 0xff - data2[i] & 0xff) is type byte and has value -1 (0xFF)
So you still have gotten an overflow
For some reason, comparing bytes is handled weirdly. If I convert the bytes to unsigned ints in the comparison, the comparison works correctly and my results are as I expected them.
I can then subtract the bytes as if they were unsigned as Louis Wasserman pointed out, which was new to me.
for (int i = 0; i < data1.length; i++) {
if ((data1[i] & 0xff) > (data2[i] & 0xff)) {
dest[i] = (byte)(data1[i] - data2[i]);
} else {
dest[i] = 0;
}
}
So I am saving an audio file. And i have to convert float[] to byte[]. This works fine:
final byte[] byteBuffer = new byte[buffer.length * 2];
int bufferIndex = 0;
for (int i = 0; i < byteBuffer.length; i++) {
final int x = (int) (buffer[bufferIndex++] * 32767.0);
byteBuffer[i] = (byte) x;
i++;
byteBuffer[i] = (byte) (x >>> 8);
if (bufferIndex < 5) {
System.out.println(buffer[bufferIndex]);
System.out.println(byteBuffer[i - 1]);
System.out.println(byteBuffer[i]);
}
}
But when i want to read the bytes and convert it back to floats just the first 4 numbers match with the old ones:
for (int i =0; i < length; i++) {
i++;
float val = (((audioB[i]) & 0xff) << 8) | ((audioB[i-1]) & 0xff);
val = (float) (val /32767.0);
if (bufferindex < 5) {
System.out.println(val);
System.out.println(audioB[i-1]);
System.out.println(audioB[i]);
}
bufferindex++;
}
The output :
0.07973075
0
0
0.149165
52
10
0.19944257
23
19
0.22437502
-121
25
---------
0.0
0
0
0.07971435
52
10
0.14914395
23
19
0.19943845
-121
25
0.22437209
Why ?
Rather than implementing your own bit shifting magic, why not use the java.nio.ByteBuffer class?
byte[] bytes = ByteBuffer.allocate(8).putFloat(1.0F).putFloat(2.0F).array();
ByteBuffer bb = ByteBuffer.wrap(bytes);
float f1 = bb.getFloat();
float f2 = bb.getFloat();
You are stuffing 4 byte float data into just 2 bytes per sample. This will obviously loose some precisition. Hint: Do the backwards calculation (2 byte -> float) in your first loop and compare the result with the original value, and look at intermediate values like x.
I'm facing some problems with WAV files in Java.
WAV format: PCM_SIGNED 44100.0 Hz, 24-bit, stereo, 6 bytes/frame, little-endian.
I extracted the WAV data to a byte array with no problems.
I'm trying to convert the byte array to a double array, but some doubles come with NaN value.
Code:
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
double[] doubles = new double[byteArray.length / 8];
for (int i = 0; i < doubles.length; i++) {
doubles[i] = byteBuffer.getDouble(i * 8);
}
The fact of being 16/24/32-bit, mono/stereo makes me confused.
I intend to pass the double[] to a FFT algorithm and get the audio frequencies.
try this:
public static byte[] toByteArray(double[] doubleArray){
int times = Double.SIZE / Byte.SIZE;
byte[] bytes = new byte[doubleArray.length * times];
for(int i=0;i<doubleArray.length;i++){
ByteBuffer.wrap(bytes, i*times, times).putDouble(doubleArray[i]);
}
return bytes;
}
public static double[] toDoubleArray(byte[] byteArray){
int times = Double.SIZE / Byte.SIZE;
double[] doubles = new double[byteArray.length / times];
for(int i=0;i<doubles.length;i++){
doubles[i] = ByteBuffer.wrap(byteArray, i*times, times).getDouble();
}
return doubles;
}
public static byte[] toByteArray(int[] intArray){
int times = Integer.SIZE / Byte.SIZE;
byte[] bytes = new byte[intArray.length * times];
for(int i=0;i<intArray.length;i++){
ByteBuffer.wrap(bytes, i*times, times).putInt(intArray[i]);
}
return bytes;
}
public static int[] toIntArray(byte[] byteArray){
int times = Integer.SIZE / Byte.SIZE;
int[] ints = new int[byteArray.length / times];
for(int i=0;i<ints.length;i++){
ints[i] = ByteBuffer.wrap(byteArray, i*times, times).getInt();
}
return ints;
}
Your WAV format is 24 bit, but a double uses 64 bit. So the quantities stored in your wav can't be doubles. You have one 24 bit signed integer per frame and channel, which amounts to these 6 bytes mentioned.
You could do something like this:
private static double readDouble(ByteBuffer buf) {
int v = (byteBuffer.get() & 0xff);
v |= (byteBuffer.get() & 0xff) << 8;
v |= byteBuffer.get() << 16;
return (double)v;
}
You'd call that method once for the left channel and once for the right. Not sure about the correct order, but I guess left first. The bytes are read from least significant one to most significant one, as little-endian indicates. The lower two bytes are masked with 0xff in order to treat them as unsigned. The most significant byte is treated as signed, since it will contain the sign of the signed 24 bit integer.
If you operate on arrays, you can do it without the ByteBuffer, e.g. like this:
double[] doubles = new double[byteArray.length / 3];
for (int i = 0, j = 0; i != doubles.length; ++i, j += 3) {
doubles[i] = (double)( (byteArray[j ] & 0xff) |
((byteArray[j+1] & 0xff) << 8) |
( byteArray[j+2] << 16));
}
You will get samples for both channels interleaved, so you might want to separate these afterwards.
If you have mono, you won't have two channels interleaved but only once. For 16 bit you can use byteBuffer.getShort(), for 32 bit you can use byteBuffer.getInt(). But 24 bit isn't commonly used for computation, so ByteBuffer doesn't have a method for this. If you have unsigned samples, you'll have to mask all signs, and to offset the result, but I guess unsigned WAV is rather uncommon.
For floating-point types in DSP they usually prefer values in the range [0, 1] or [0, 1), so you should divide each element by 224-1. Do like the answer of MvG above but with some changes
int t = ((byteArray[j ] & 0xff) << 0) |
((byteArray[j+1] & 0xff) << 8) |
(byteArray[j+2] << 16);
return t/double(0xFFFFFF);
But double is really a waste of space and CPU for data process purposes. I would recommend convert it to 32-bit int instead, or float which has the same precision (24 bits) but bigger range. In fact 32-bit int or float is the biggest type for a data channel when you do audio or video processing
Finally you can utilize multithreading and SIMD to accelerate the conversion
Given
private int width = 400;
private byte [] data = new byte [2];
I want to split the integer "width" into two bytes and load data[0] with the high byte and data[1] with the low byte.
That is binary value of 400 = 1 1001 0000
so data[0] should contain 0000 0001
and data[1] should contain 1001 0000
Using simple bitwise operations:
data[0] = (byte) (width & 0xFF);
data[1] = (byte) ((width >> 8) & 0xFF);
How it works:
& 0xFF masks all but the lowest eight bits.
>> 8 discards the lowest 8 bits by moving all bits 8 places to the right.
The cast to byte is necessary because these bitwise operations work on an int and return an int, which is a bigger data type than byte. The case is safe, since all non-zero bits will fit in the byte. For more information, see Conversions and Promotions.
Edit: Taylor L correctly remarks that though >> works in this case, it may yield incorrect results if you generalize this code to four bytes (since in Java an int is 32 bits). In that case, it's better to use >>> instead of >>. For more information, see the Java tutorial on Bitwise and Bit Shift Operators.
For converting two bytes the cleanest solution is
data[0] = (byte) width;
data[1] = (byte) (width >>> 8);
For converting an integer to four bytes the code would be
data[0] = (byte) width;
data[1] = (byte) (width >>> 8);
data[2] = (byte) (width >>> 16);
data[3] = (byte) (width >>> 24);
It doesn't matter whether >> or >>> is used for shifting, any one bits created by sign extension will not end up in the resulting bytes.
See also this answer.
This should do what you want for a 4 byte int. Note, it stores the low byte at offset 0. I'll leave it as an exercise to the reader to order them as needed.
public static byte[] intToBytes(int x) {
byte[] bytes = new byte[4];
for (int i = 0; x != 0; i++, x >>>= 8) {
bytes[i] = (byte) (x & 0xFF);
}
return bytes;
}
Integer is 32 bits (=4 bytes) in java, you know?
width & 0xff will give you the first byte,
width & 0xff00 >> 8 will give you the second, etc.
To get the high byte, shift right by 8 bits then mask off the top bytes. Similarly, to get the low byte just mask off the top bytes.
data[0] = (width >> 8) & 0xff;
data[1] = width & 0xff;
int width = 400;
byte [] data = new byte [2];
data[0] = (byte) ((width & 0xFF00) >> 8);
data[1] = (byte) (width & 0xFF);
for(int b = 0; b < 2; b++) {
System.out.println("printing byte " + b);
for(int i = 7; i >= 0; i--) {
System.out.println(data[b] & 1);
data[b] = (byte) (data[b] >> 1);
}
}
I suggest you have a look at the source for HeapByteBuffer. It has the conversion code for all primitive data types. (In fact you could just use a ByteBuffer ;)