1
\$\begingroup\$

I wrote the following code below to extract the least significant bit (LSB) of each pixel and store it into a byte[] array. I was wondering if Java has a more efficient way to do this?

Right now, I am looping over every pixel, extracting the LSB, and updating the current byte by shifting it to the left and adding the LSB. When a byte is filled up, the next byte is then set as the current one. The process is repeated until all pixels' LSBs have been extracted and fed into the byte array.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] pixels = new int[] {
            0xFFFF0000, 0xFFFF0001, 0xFFFF0000, 0xFFFF0001,
            0xFFFF0000, 0xFFFF0001, 0xFFFF0001, 0xFFFF0001,
            0x00000001, 0x00000000
        };
        // Result should be 3 bytes [0101 (5), 0111 (7), 10 (2)]
        System.out.println(Arrays.toString(extractLSB(pixels)));
    }

    public static byte[] extractLSB(int[] pixels) {
        int pixelCount = pixels.length;
        int numberOfBytes = (int) Math.ceil(pixelCount / 4.0);
        int extraBits = pixelCount % 4;
        int overflowIndex = pixelCount - extraBits;
        int currByteIndex = 0;
        int currByteValue = 0;
        byte[] byteBuffer = new byte[numberOfBytes];

        for (int i = 0; i < pixelCount; i++) {
            int currPixel = pixels[i];
            int bitIndex = i % 4;
            int bit = currPixel & 1;

            currByteValue |= bit;

            if (i > overflowIndex && bitIndex == extraBits - 1) {
                byteBuffer[currByteIndex] = (byte) currByteValue;
            } else if (bitIndex < 3) {
                currByteValue <<= 1;
            } else if (bitIndex == 3) {
                byteBuffer[currByteIndex++] = (byte) currByteValue;
                currByteValue = 0;
            }
        }

        return byteBuffer;
    }
}
\$\endgroup\$
1
\$\begingroup\$

I believe your basic algorithm is fine. Shifting only 4 bits on to each byte is not exactly filling the byte, but, there are ways I would consider neatening up the process. Note that this does not change the algorithm much at all.

Here are some tricks to note, though...

  1. You should pass in the number of bits you want in a byte as a parameter value, or it should be a constant. The value 4 is a magic number that should not be there in the method.

    private static final int BITS_IN_BYTE = 4;
    
  2. You have: int numberOfBytes = (int) Math.ceil(pixelCount / 4.0);. This can be done "better" with:

    int numberOfBytes = (pixelCount + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
    
  3. You declare convenience variables which really are unnecessary....

  4. You are working with the data 'backwards'.... you build up the byte, then set the value in to the output, instead of just shifting the bits in to the output.

Here's how I would have written your code. It is more concise, but essentially the same.

public static byte[] extractLSBRL(final int perByte, final int[] pixels) {
    final int outSize = (pixels.length + perByte - 1) / perByte;
    final byte[] lsbs = new byte[outSize];
    for (int i = 0; i < pixels.length; i++) {
        // which output byte is the one for this pixel
        final int j = i / perByte;
        // make space for the new bit to arrive.
        lsbs[j] <<= 1;
        // copy the bit in to the output byte's position.
        lsbs[j] |= pixels[i] & 1;
    }
    return lsbs;

}

Note that I have a concern about the output here. are you sure the last byte should be 10, and not 1000 (i.e. shifted two bits to the left?). Just checking... different algorithms expect different things....

\$\endgroup\$
  • \$\begingroup\$ I will leave this question open for more answers, but as of now your's is definitely the best and most concise. \$\endgroup\$ – Mr. Polywhirl Apr 27 '15 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.