4

I have the open sourced code for the Google Mobile Vision - CameraSource and this is the method I call to click a photo : cameraSource.takePicture();

In the open sourced version of CameraSource.java, the method for determining screen orientation is the stock one:

private void setRotation(Camera camera, Camera.Parameters parameters, int cameraId) {
        WindowManager windowManager =
                (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        int degrees = 0;
        int rotation = windowManager.getDefaultDisplay().getRotation();
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
            default:
                Log.e(TAG, "Bad rotation value: " + rotation);
        }

        CameraInfo cameraInfo = new CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);

        int angle;
        int displayAngle;
        if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
            angle = (cameraInfo.orientation + degrees) % 360;
            displayAngle = (360 - angle) % 360; // compensate for it being mirrored
        } else {  // back-facing
            angle = (cameraInfo.orientation - degrees + 360) % 360;
            displayAngle = angle;
        }

        // This corresponds to the rotation constants in {@link Frame}.
        mRotation = angle / 90;

        camera.setDisplayOrientation(displayAngle);
        parameters.setRotation(angle);
    }

Here, the displayAngle and the angle are the same for Samsung, Lenovo and Yuntab H8. But the bitmap returned for backCamera is rotated differently in each of the device. I have to manually rotate the bitmap for each of the devices (Samsung : 90, Lenovo : 0 and Yuntab : 180)

My requirement is that onPictureTaken should return a bitmap which matches the current display orientation. I am looking into this, since a long time but yet have to figure a way to the solution. Here below is my onPicturetaken() (called after taking picture):

  @Override
        public void onPictureTaken(byte[] bytes) {
            try {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2;
                bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, currentCameraId == 0 ? options : null);
            }catch (Exception ex){
                ex.printStackTrace();
                Log.e("PictureTaken",ex.toString());
        }
    };
V.Bhat
  • 63
  • 10
  • I followed [this answer](https://stackoverflow.com/a/35431231/1827254) last time I had to implement something like that, and it seems to work fine. However it looks similar to your current code – Eselfar Jan 29 '18 at 13:53
  • The answer in your suggestion is already being implemented in the CamerSource.java class. The value it returns is always the same for all devices in the same configurations (Landscape/Portrait) but the images are of different rotation. – V.Bhat Jan 31 '18 at 06:13

1 Answers1

2

You should rotate the image when it is already saved in the device.

Then you can rotate it to match the position it was when the photo was taken.

Example code (It might need some cleanup and improvement, but it works...):

Method do calculate the rotation an image has:

private static int rotationNeeded(String path) {
    try {
        File file = new File(path);

        if (!file.getName().contains(".jpg")) {
            return 0;
        }

        ExifInterface exif = new ExifInterface(file.getAbsolutePath());
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
            return 270;
        }

        if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
            return 180;
        }

        if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
            return 90;
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return 0;
}

Apply the rotation needed to the image:

public static void rotateImage(String filePath) {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);

        //check image rotation
        int rotate = rotationNeeded(filePath);
        if (rotate != 0) {
            //rotate image if needed
            Matrix matrix = new Matrix();
            matrix.postRotate(rotate);
            Bitmap rotatedImage = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                    bitmap.getHeight(), matrix, true);

            bitmap.recycle();
            bitmap = rotatedImage;

            //save image
            byte[] dataPicture = bao.toByteArray();
            FileOutputStream fos = new FileOutputStream(filePath);
            fos.write(dataPicture);
            fos.flush();
            fos.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Eduardo Herzer
  • 2,033
  • 21
  • 24
  • Actually, some devices save wrong EXIF orientation. You don't need to rely on this information, you can [use orientation sensor instead](https://stackoverflow.com/questions/27515487/android-camera-picture-orientation-issues-with-samsung-galaxy-s3-s4-s5#comment82991781_48009944). – Alex Cohn Jan 29 '18 at 15:01
  • @AlexCohn Weird. I made some research back when I used it. I found wrong EXIF when dealing with the returned byte array from intent. But when I read from the already saved imagePath, I had no issues. Can you confirm issues about reading EXIF from already saved image? I also tested successfully on Galaxy S5, which is one device your link reports problem... – Eduardo Herzer Jan 29 '18 at 15:30
  • I just realized I used the normal camera intent to take a picture and then rotate the image. So I'm not sure it will work using this approach... If you confirm it doesn't work, I'll delete the answer – Eduardo Herzer Jan 29 '18 at 15:50
  • 1
    You definitely should not delete your answer. It shows how rotation can be correctly applied to captured picture. But EXIF is a different story. With Intent, it's the only reliable data you have, because your app is suspended while the Camera app is fulfilling the Intent for you. Luckily, all reasonable devices have stock Camera app that writes correct EXIF. For custom camera (i.e. in **onPictureTaken()**) some devices may be less strict about EXIF, so it's prudent to query the orientation sensor. – Alex Cohn Jan 29 '18 at 21:53
  • 1
    @EduardoHerzer Thank you for your answer. I have already tried getting the ExifInterface data from the bitmap which I store in the external storage. But it gave me the orientation value as '0' on both the Samsung and Lenovo Tabs. The image still has to be rotated manually according to the device. Lenovo Tab does not require to be rotated whereas on the Samsung tab, the image needs to be rotated by 90 degrees on the back camera. – V.Bhat Jan 31 '18 at 05:35