Sarah
Sarah

Reputation: 145

Flip an image stored as a byte array

I have an image which is stored as a byte[] array, and I want to flip the image vertically before writing the bytes to disk elsewhere.

The image bytes come from a compressed jp2 image file. I've looked into implementing something like Flip image stored as a byte[] array, but I'm not working in android and don't have access to BitmapFactory. I've also looked into converting the byte array to a BufferedImage first, then flipping it, but the height and width of the image isn't known in the current context (EDIT: I've modified the code so the height and width are now known).

Is there a way to do this just with strict array manipulation?

EDIT: Attempted flip code

 public static byte[] flip(byte[] imageBytes) {
    //separate out the sub arrays
    byte[] holder = new byte[imageBytes.length];
    byte[] subArray = new byte[dimWidth];//dimWidth is the image width, or number of matrix columns
    int subCount = 0;
    for (int i = 0; i < imageBytes.length; i++) {
        subArray[subCount] = imageBytes[i];
        subCount++;
        if (i% dimWidth == 0) {
            subArray = reverse(subArray);
            if (i == (dimWidth)) {
                holder = subArray;
            } else {
                holder = concat(holder, subArray);
            }
            subCount = 0;
            subArray = new byte[dimWidth];
        }
    }
    subArray = new byte[dimWidth];
    System.arraycopy(imageBytes, imageBytes.length - dimWidth, subArray, 0, subArray.length);
    holder = concat(holder, subArray);
    imageBytes = holder;
    return imageBytes;
}

Upvotes: 5

Views: 1497

Answers (2)

VC.One
VC.One

Reputation: 15881

I've also looked into converting the byte array to a BufferedImage first, then flipping it, but the height and width of the image isn't known in the current context.

You could just check the height & width directly from the header section of the JP2 file bytes.

import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;

import java.nio.ByteBuffer;
import javax.xml.bind.DatatypeConverter;


public class parse_Header_JP2 
{
    static File     myFile;     static FileInputStream  fileInStream = null;
    static byte[]   myBytes;    static String           myString = ""; 

    static int myNum = 0; static int myWidth = 0; static int myHeight = 0;
    static ByteBuffer byteBuff;

    public static void main(String[] args) 
    {
        myFile = new File("c:/test/image.jp2");
        myBytes = getBytes_Header_JP2(myFile);
        checkBytes_Header_JP2(myBytes); //# update myWidth & myHeight

        //# Shows HEX of whole bytearray
        System.out.println("First 64 bytes (as HEX) : \n" + bytesToHex( myBytes ) );
    }

    private static byte[] getBytes_Header_JP2(File file)
    {
        myBytes = new byte[64]; //will hold first 64 bytes of file as header bytes

        try
        {
           //# convert file into array of bytes
           fileInStream = new FileInputStream(file);
           fileInStream.read(myBytes, 0, 64); //# Read only first 64 bytes
           fileInStream.close();
        }
        catch (Exception e) { e.printStackTrace(); } //# error catching

        byteBuff = ByteBuffer.wrap(myBytes); //associate ByteBuffer with bytes

        return myBytes;
   }

   public static void checkBytes_Header_JP2(byte[] bytes)
   {
       int offset = 0; myHeight = 0; myWidth = 0; // resets

       while(true)
       {
           //# set as byte value reading from offset
           myNum = bytes[offset]; myString = Integer.toHexString(myNum).toUpperCase();

           //# Check byte as : Hex value
           System.out.println("Byte Value at [" + offset + "] : " + decimalToHex(myNum) );

           //# Check byte as : Decimal value
           //System.out.println("Byte Value at [" + offset + "] : " + myNum ); 

           //# Find byte 0x69 (or Decimal = 105) which is letter "i" from "ihdr"
           if (myNum == 0x69) //# if "i" is found at this offset within bytes
           {
               //# From this offset check if reading 4 bytes gives "ihdr" (as bytes 69-68-64-72)
               if ( byteBuff.getInt(offset) == 0x69686472 ) //# if the 4 bytes make "ihdr"
               {
                   System.out.println("found \"ihdr\" section at offset : " + offset ); 

                   //# extract Width or Height from this offset
                   myHeight = byteBuff.getInt(offset+4); //# +4 from "ihdr" is Height entry
                   myWidth = byteBuff.getInt(offset+8); //# +8 from "ihdr" is Width entry

                   //# check values
                   System.out.println("Image Width  : " + myWidth);
                   System.out.println("Image Height : " + myHeight);

                   break; //# end the While loop (otherwise runs forever)
               }    
           }    

           offset++; //# increment offset during While loop process
       }    
    }

    //# Convert byte values to Hex string for checking
    private static String bytesToHex(byte[] bytes)
    { myString = ""; myString = DatatypeConverter.printHexBinary(bytes); return myString; } 

    private static int bytesToNumber( ByteBuffer input ) //throws IOException 
    { input.rewind(); myNum = input.getInt(); return myNum; }

    private static String decimalToHex(int input) 
    {
        input = input & 0xFF; myString = Integer.toHexString(input);
        if(myString.length() == 1) {myString="0"+myString;} 
        return myString.toUpperCase();
    }
} //end Class

Upvotes: 0

Sarah
Sarah

Reputation: 145

Nevermind guys, I got it working. I just had to flip the high and low bytes BEFORE interleaving them into the final array. To any with the same problem as me, use the above flip function on your high and low byte arrays separately before interleaving them.

Thank you for your help!

Upvotes: 1

Related Questions