RobotRock
RobotRock

Reputation: 4459

Subtraction/difference between bytes in Java

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.

Upvotes: 3

Views: 1828

Answers (4)

Raffaele
Raffaele

Reputation: 20885

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).

Upvotes: 2

RobotRock
RobotRock

Reputation: 4459

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;
        }
    }

Upvotes: 0

Anonymous Coward
Anonymous Coward

Reputation: 3200

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

Upvotes: 0

Louis Wasserman
Louis Wasserman

Reputation: 198341

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.

Upvotes: 2

Related Questions