googlesamples / android-vision

Deprecated: The Mobile Vision API is now a part of ML Kit: Check out this repo:
https://github.com/firebase/quickstart-android/tree/master/mlkit
Apache License 2.0
2.92k stars 1.73k forks source link

CameraSource.takephoto rotated #124

Open WMariusz opened 8 years ago

WMariusz commented 8 years ago

Photo taken by CameraSource.takephoto on some phones is rotated by 90degrees. ExifInterface(imageUri.getPath()).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED) return 0.

How can i get photo in good orientation?

kudlatyIE commented 8 years ago

You need to rotate the photo yourself. This is a hardware camera problem: some camera (as sony experia, most samsung) create photo on landmark orientation only, some other (like nexus 5) use autorotation and give you output with device orientation.

WMariusz commented 8 years ago

yes i know but its look like a bug. In android.hardware.Camera i was able to detect photo rotation and set proper orientation in CameraSource i dont have that option

pm0733464 commented 8 years ago

Yes, you are right that rotation is not indicated in takePicture.

However, the Frame instances that are generated by the CameraSource will indicate the rotation:

https://developers.google.com/android/reference/com/google/android/gms/vision/Frame.Metadata.html#getRotation()

If you are using a Tracker to receive detections from an associated Detector, you can access this via Detections.getFrameMetadata().

kinghsumit commented 8 years ago
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;
}
 }
relativokobe commented 5 years ago

Hello. Are there any other ways that CameraSource takes a photo in Portrait mode? I used @kinghsumit's answer and it works perfectly but the problem is I need to make that rotated image as the background of my layout which makes the bitmap bloated vertically because the bitmap is originally for landscape.