Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

camerasource.takePicture() save rotated images in some device

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.

like image 614
sumit singh Avatar asked Sep 12 '16 05:09

sumit singh


3 Answers

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;
}
 }
like image 147
sumit singh Avatar answered Nov 02 '22 10:11

sumit singh


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
}
like image 32
TormundThunderfist Avatar answered Nov 02 '22 09:11

TormundThunderfist


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.

like image 37
cliffroot Avatar answered Nov 02 '22 10:11

cliffroot