camerasource.takePicture() save rotated images in some device
Asked Answered
D

4

5

I am using vision api for tracking face. I applied a mask on the basis of face position.When i take a picture from front camera i call camerasource.takePicture() to save images.I am facing issue of image rotation in some device like samsung and capture image shows mask and face in different different position.I use Exif class to get orientation of images but it always return 0 so i am unable to rotate the image. I am using following class to getOrientation and rotate image.

 public class ExifUtils {
 public Bitmap rotateBitmap(String src, Bitmap bitmap) {
    try {
        int orientation = getExifOrientation(src);

        if (orientation == 1) {
            return bitmap;
        }

        Matrix matrix = new Matrix();
        switch (orientation) {
            case 2:
                matrix.setScale(-1, 1);
                break;
            case 3:
                matrix.setRotate(180);
                break;
            case 4:
                matrix.setRotate(180);
                matrix.postScale(-1, 1);
                break;
            case 5:
                matrix.setRotate(90);
                matrix.postScale(-1, 1);
                break;
            case 6:
                matrix.setRotate(90);
                break;
            case 7:
                matrix.setRotate(-90);
                matrix.postScale(-1, 1);
                break;
            case 8:
                matrix.setRotate(-90);
                break;
            default:
                return bitmap;
        }

        try {
            Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0,
                    bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            bitmap.recycle();
            return oriented;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return bitmap;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    return bitmap;
}

private int getExifOrientation(String src) throws IOException {
    int orientation = 1;

    try {

        if (Build.VERSION.SDK_INT >= 5) {
            Class<?> exifClass = Class
                    .forName("android.media.ExifInterface");
            Constructor<?> exifConstructor = exifClass
                    .getConstructor(new Class[]{String.class});
            Object exifInstance = exifConstructor
                    .newInstance(new Object[]{src});
            Method getAttributeInt = exifClass.getMethod("getAttributeInt",
                    new Class[]{String.class, int.class});
            Field tagOrientationField = exifClass
                    .getField("TAG_ORIENTATION");
            String tagOrientation = (String) tagOrientationField.get(null);
            orientation = (Integer) getAttributeInt.invoke(exifInstance,
                    new Object[]{tagOrientation, 1});

        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (Fragment.InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (java.lang.InstantiationException e) {
        e.printStackTrace();
    }

    return orientation;
}

}

I found this issue in vision api is there any solution.

Darkroom answered 12/9, 2016 at 5:8 Comment(3)
See the comment that I added on the vision api issue. It's possible to get the orientation from the frame metadata.Impenetrability
Thanks@Impenetrability can you please provide any example?Darkroom
I solved my problem see the comment added by me on vision api issue.github.com/googlesamples/android-vision/issues/124Darkroom
D
6

I solve my problem myself. I get orientation from byte data then rotate my images according to orientation.

private CameraSource.PictureCallback mPicture = new CameraSource.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] bytes) {
       int orientation = Exif.getOrientation(bytes);
       Bitmap   bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        switch(orientation) {
            case 90:
                bitmapPicture= rotateImage(bitmap, 90);

                break;
            case 180:
                bitmapPicture= rotateImage(bitmap, 180);

                break;
            case 270:
                bitmapPicture= rotateImage(bitmap, 270);

                break;
            case 0:
           // if orientation is zero we don't need to rotate this 

            default:
                break;
        }
          //write your code here to save bitmap 
        }

    }

};
  public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(),   source.getHeight(), matrix,
            true);
  }

Below class is used to get orientation from byte[] data.

public class Exif {
private static final String TAG = "CameraExif";

// Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
public static int getOrientation(byte[] jpeg) {
    if (jpeg == null) {
        return 0;
    }

    int offset = 0;
    int length = 0;

    // ISO/IEC 10918-1:1993(E)
    while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
        int marker = jpeg[offset] & 0xFF;

        // Check if the marker is a padding.
        if (marker == 0xFF) {
            continue;
        }
        offset++;

        // Check if the marker is SOI or TEM.
        if (marker == 0xD8 || marker == 0x01) {
            continue;
        }
        // Check if the marker is EOI or SOS.
        if (marker == 0xD9 || marker == 0xDA) {
            break;
        }

        // Get the length and check if it is reasonable.
        length = pack(jpeg, offset, 2, false);
        if (length < 2 || offset + length > jpeg.length) {
            Log.e(TAG, "Invalid length");
            return 0;
        }

        // Break if the marker is EXIF in APP1.
        if (marker == 0xE1 && length >= 8 &&
                pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
                pack(jpeg, offset + 6, 2, false) == 0) {
            offset += 8;
            length -= 8;
            break;
        }

        // Skip other markers.
        offset += length;
        length = 0;
    }

    // JEITA CP-3451 Exif Version 2.2
    if (length > 8) {
        // Identify the byte order.
        int tag = pack(jpeg, offset, 4, false);
        if (tag != 0x49492A00 && tag != 0x4D4D002A) {
            Log.e(TAG, "Invalid byte order");
            return 0;
        }
        boolean littleEndian = (tag == 0x49492A00);

        // Get the offset and check if it is reasonable.
        int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
        if (count < 10 || count > length) {
            Log.e(TAG, "Invalid offset");
            return 0;
        }
        offset += count;
        length -= count;

        // Get the count and go through all the elements.
        count = pack(jpeg, offset - 2, 2, littleEndian);
        while (count-- > 0 && length >= 12) {
            // Get the tag and check if it is orientation.
            tag = pack(jpeg, offset, 2, littleEndian);
            if (tag == 0x0112) {
                // We do not really care about type and count, do we?
                int orientation = pack(jpeg, offset + 8, 2, littleEndian);
                switch (orientation) {
                    case 1:
                        return 0;
                    case 3:
                        return 180;
                    case 6:
                        return 90;
                    case 8:
                        return 270;
                }
                Log.i(TAG, "Unsupported orientation");
                return 0;
            }
            offset += 12;
            length -= 12;
        }
    }

    Log.i(TAG, "Orientation not found");
    return 0;
}

private static int pack(byte[] bytes, int offset, int length,
                        boolean littleEndian) {
    int step = 1;
    if (littleEndian) {
        offset += length - 1;
        step = -1;
    }

    int value = 0;
    while (length-- > 0) {
        value = (value << 8) | (bytes[offset] & 0xFF);
        offset += step;
    }
    return value;
}
 }
Darkroom answered 15/9, 2016 at 12:9 Comment(0)
N
1

Sounds like an issue with Exif tags to me. Basically, modern cameras save images in the same orientation, but also save a tag that tells you, what the original orientation was.

You could use Exif Interface, which comes bundled with the java api. I prefer Alessandro Crugnola's Android-Exif-Interface library, which doesn't require you to keep filepaths around

How I used Android-Exif-Interface in my project:

ExifInterface exif = new ExifInterface();
Matrix matrix = new Matrix();
try {
    exif.readExif(context.getContentResolver().openInputStream(fileUri), ExifInterface.Options.OPTION_ALL);
    ExifTag tag = exif.getTag(ExifInterface.TAG_ORIENTATION);
    int orientation = tag.getValueAsInt(1);
    switch (orientation) {
        case 3: /* 180° */
            matrix.postRotate(180);
            break;
        case 6: /*  90° */
            matrix.postRotate(90);
            break;
        case 8: /* 270° */
            matrix.postRotate(-90);
            break;
    }
} catch (IOException e) {
    Log.i("INFO","expected behaviour: IOException");
    //not every picture comes from the phone, should that be the case,
    // we can't get exif tags anyway, since those aren't being transmitted
    // via http (atleast I think so. I'd need to save the picture on the SD card to
    // confirm that and I don't want to do that)
} catch(NullPointerException e){
    Log.i("INFO","expected behaviour: NullPointerException");
    //same as above, not every picture comes from the phone
}
Novia answered 12/9, 2016 at 5:45 Comment(5)
I tried many more codes but each time i get orientation 0 so i am unable to rotate images i am facing this issue in samsung devices.Darkroom
Could you post the code you use to get the orientation? Difficult to say much without itNovia
I can only guess that the issue lies with android.media.ExifInterface. I mentioned it above, try the library. The library worked for me, when android.media.ExifInterface did not. I'll edit my post a bit to provide an example.Novia
NullPointerException happens either when the file doesn't exist (wrong Uri maybe?) or if the supplied image doesn't have exif tags. If it doesn't have exif tags, you can simply ignore the exif tags and assume that it has standard rotationNovia
Let us continue this discussion in chat.Darkroom
M
1

I have encountered similar problem with Samsung devices, ExifInterface doesn't seem to work correctly with images saved by them. In order to solve the problem I used code from Glide image library, it seems to handle checking original image rotation correctly.

Check out this link: Glide source

getOrientation method from there seems to do the job most of the time.

Mosstrooper answered 12/9, 2016 at 9:49 Comment(0)
M
0

In many cases, the pictureCallback() receives a Jpeg with undefined orientation tag. But you can calculate the actual device orientation either by looking at the display rotation, or running an orientation listener, as for Camera.takePicture returns a rotated byteArray.

Meave answered 16/8, 2020 at 19:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.