google / cameraview

[DEPRECATED] Easily integrate Camera features into your Android app
Apache License 2.0
4.74k stars 1.03k forks source link

Front Camera issue #84

Open rajeshct opened 7 years ago

rajeshct commented 7 years ago

Image rotated 180 degree while capturing from front camera

Android version:7.1 Model: Yureka

n0m0r3pa1n commented 7 years ago

Yup, this is normal on Android. You have to implement a method to analyze and rotate the image:

    // isOldCameraApi is a flag I use if the Preview is Camera1 or Camera2 because on older devices
   // you just have to rotate the image on certain types
    public static Bitmap correctImageOrientation(File file, boolean isOldCameraApi) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            bitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
            ExifInterface ei = new ExifInterface(file.getAbsolutePath());
            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_UNDEFINED);

            return getRotatedBitmap(bitmap, orientation, isOldCameraApi);

        } catch (IOException e) {
            e.printStackTrace();
            return bitmap;
        }
    }

    private static Bitmap getRotatedBitmap(Bitmap bitmap, int orientation, boolean isOldCameraApi) {
        Timber.d("Orientation: " + orientation + " OldCameraAPI: " + isOldCameraApi);
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                bitmap = rotateImage(bitmap, 90);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                bitmap = rotateImage(bitmap, 180);
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                bitmap = rotateImage(bitmap, 270);
                break;
            case ExifInterface.ORIENTATION_NORMAL:
            case ExifInterface.ORIENTATION_UNDEFINED:
                if (isOldCameraApi) {
                    bitmap = rotateImage(bitmap, 180);
                }
                break;
            default:
                break;
        }
        return bitmap;
    }

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

        return bitmap;
    }
yaraki commented 7 years ago

The library should provide the right orientation on any device. I don't have Yureka with me, so I'd really appreciate someone with these devices to contribute.

protocol10 commented 7 years ago

Same appeared on Asus zenphone for rear camera

deepikaopenxcell commented 7 years ago

same appeared in samsung note3 for front camera

rajeshct commented 7 years ago

I have made some changes in project please have a look their are some changes in project file:

1) Fix crash in android 6.0 2) Front camera image rotation issue 3) Camera opened onResume (Bug in sample code) 4) Added preview for image

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.cameraview.demo;

import android.Manifest;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.cameraview.AspectRatio;
import com.google.android.cameraview.CameraView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Set;

/**
 * This demo app saves the taken picture to a constant file.
 * $ adb pull /sdcard/Android/data/com.google.android.cameraview.demo/files/Pictures/picture.jpg
 */
public class MainActivity extends AppCompatActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        AspectRatioFragment.Listener {

    private static final String TAG = "MainActivity";

    private static final int REQUEST_CAMERA_PERMISSION = 1;

    private static final String FRAGMENT_DIALOG = "dialog";

    private static final int[] FLASH_OPTIONS = {
            CameraView.FLASH_AUTO,
            CameraView.FLASH_OFF,
            CameraView.FLASH_ON,
    };

    private static final int[] FLASH_ICONS = {
            R.drawable.ic_flash_auto,
            R.drawable.ic_flash_off,
            R.drawable.ic_flash_on,
    };

    private static final int[] FLASH_TITLES = {
            R.string.flash_auto,
            R.string.flash_off,
            R.string.flash_on,
    };

    private int mCurrentFlash;

    private FrameLayout cameraLayout;
    private CameraView mCameraView;

    private Handler mBackgroundHandler;

    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.take_picture:
                    if (mCameraView != null) {
                        mCameraView.takePicture();
                    }
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        cameraLayout = (FrameLayout) findViewById(R.id.camera);
        FloatingActionButton takePicture = (FloatingActionButton) findViewById(R.id.take_picture);
        if (takePicture != null) {
            takePicture.setOnClickListener(mOnClickListener);
        }
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayShowTitleEnabled(false);
        }
       // addCameraToLayout();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mCameraView == null)
            addCameraToLayout();
    }

    private void addCameraToLayout() {
        cameraLayout.removeAllViews();
        mCameraView = new CameraView(this);
        mCameraView.setId(R.id.camera);
        mCameraView.setAutoFocus(true);
        mCameraView.setFacing(CameraView.FACING_BACK);
        mCameraView.setFlash(CameraView.FLASH_OFF);
        cameraLayout.addView(mCameraView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mCameraView.addCallback(mCallback);
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            mCameraView.start();
        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
            ConfirmationDialogFragment
                    .newInstance(R.string.camera_permission_confirmation,
                            new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
                            REQUEST_CAMERA_PERMISSION,
                            R.string.camera_permission_not_granted)
                    .show(getSupportFragmentManager(), FRAGMENT_DIALOG);
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_CAMERA_PERMISSION);
        }
    }

    @Override
    protected void onPause() {
        if (mCameraView != null)
            mCameraView.stop();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mBackgroundHandler != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                mBackgroundHandler.getLooper().quitSafely();
            } else {
                mBackgroundHandler.getLooper().quit();
            }
            mBackgroundHandler = null;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CAMERA_PERMISSION:
                if (permissions.length != 1 || grantResults.length != 1) {
                    throw new RuntimeException("Error on requesting camera permission.");
                }
                if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, R.string.camera_permission_not_granted,
                            Toast.LENGTH_SHORT).show();
                }
                mCameraView.start();
                // No need to start camera here; it is handled by onResume
                break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.aspect_ratio:
                if (mCameraView != null) {
                    final Set<AspectRatio> ratios = mCameraView.getSupportedAspectRatios();
                    final AspectRatio currentRatio = mCameraView.getAspectRatio();
                    AspectRatioFragment.newInstance(ratios, currentRatio)
                            .show(getSupportFragmentManager(), FRAGMENT_DIALOG);
                }
                break;
            case R.id.switch_flash:
                if (mCameraView != null) {
                    mCurrentFlash = (mCurrentFlash + 1) % FLASH_OPTIONS.length;
                    item.setTitle(FLASH_TITLES[mCurrentFlash]);
                    item.setIcon(FLASH_ICONS[mCurrentFlash]);
                    mCameraView.setFlash(FLASH_OPTIONS[mCurrentFlash]);
                }
                break;
            case R.id.switch_camera:
                if (mCameraView != null) {
                    int facing = mCameraView.getFacing();
                    mCameraView.setFacing(facing == CameraView.FACING_FRONT ? CameraView.FACING_BACK : CameraView.FACING_FRONT);
                }
                break;
        }
        return false;
    }

    @Override
    public void onAspectRatioSelected(@NonNull AspectRatio ratio) {
        if (mCameraView != null) {
            Toast.makeText(this, ratio.toString(), Toast.LENGTH_SHORT).show();
            mCameraView.setAspectRatio(ratio);
        }
    }

    private Handler getBackgroundHandler() {
        if (mBackgroundHandler == null) {
            HandlerThread thread = new HandlerThread("background");
            thread.start();
            mBackgroundHandler = new Handler(thread.getLooper());
        }
        return mBackgroundHandler;
    }

    private CameraView.Callback mCallback
            = new CameraView.Callback() {

        @Override
        public void onCameraOpened(CameraView mCameraView) {
            Log.d(TAG, "onCameraOpened");
        }

        @Override
        public void onCameraClosed(CameraView mCameraView) {
            Log.d(TAG, "onCameraClosed");
        }

        @Override
        public void onPictureTaken(final CameraView mCameraView, final byte[] data) {

            Toast.makeText(mCameraView.getContext(), R.string.picture_taken, Toast.LENGTH_SHORT)
                    .show();
            getBackgroundHandler().post(new Runnable() {
                @Override
                public void run() {
                    File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES),
                            "picture.jpg");
                    OutputStream os = null;
                    try {
                        os = new FileOutputStream(file);
                        os.write(data);
                        os.close();
                    } catch (IOException e) {
                        Log.w(TAG, "Cannot write to " + file, e);
                    } finally {
                        if (os != null) {
                            try {
                                os.close();
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                    new ImageRotation(file.getAbsolutePath(), new ImageRotator() {
                        @Override
                        public void rotateImage(Bitmap data) {
                            showImage(data);
                        }
                    }, mCameraView.getFacing()).execute();
                }
            });
        }

    };

    private void showImage(Bitmap data) {
        Dialog dialog = new Dialog(this);
        dialog.setContentView(R.layout.dialog_image);
        ImageView imageView = (ImageView) dialog.findViewById(R.id.imageView);
        imageView.setImageBitmap(data);
        dialog.show();
    }

    interface ImageRotator {
        void rotateImage(Bitmap data);
    }

    public static class ConfirmationDialogFragment extends DialogFragment {

        private static final String ARG_MESSAGE = "message";
        private static final String ARG_PERMISSIONS = "permissions";
        private static final String ARG_REQUEST_CODE = "request_code";
        private static final String ARG_NOT_GRANTED_MESSAGE = "not_granted_message";

        public static ConfirmationDialogFragment newInstance(@StringRes int message,
                                                             String[] permissions, int requestCode, @StringRes int notGrantedMessage) {
            ConfirmationDialogFragment fragment = new ConfirmationDialogFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_MESSAGE, message);
            args.putStringArray(ARG_PERMISSIONS, permissions);
            args.putInt(ARG_REQUEST_CODE, requestCode);
            args.putInt(ARG_NOT_GRANTED_MESSAGE, notGrantedMessage);
            fragment.setArguments(args);
            return fragment;
        }

        @NonNull
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Bundle args = getArguments();
            return new AlertDialog.Builder(getActivity())
                    .setMessage(args.getInt(ARG_MESSAGE))
                    .setPositiveButton(android.R.string.ok,
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String[] permissions = args.getStringArray(ARG_PERMISSIONS);
                                    if (permissions == null) {
                                        throw new IllegalArgumentException();
                                    }
                                    ActivityCompat.requestPermissions(getActivity(),
                                            permissions, args.getInt(ARG_REQUEST_CODE));
                                }
                            })
                    .setNegativeButton(android.R.string.cancel,
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Toast.makeText(getActivity(),
                                            args.getInt(ARG_NOT_GRANTED_MESSAGE),
                                            Toast.LENGTH_SHORT).show();
                                }
                            })
                    .create();
        }

    }

}
package com.google.android.cameraview.demo;

import android.graphics.Bitmap;
import android.os.AsyncTask;

import com.google.android.cameraview.ImageRotationHelper;

import java.io.File;

/**
 * Created by rajesh on 26/1/17.
 */

class ImageRotation extends AsyncTask<Void, Void, Bitmap> {
    private String path;
    private MainActivity.ImageRotator imageRotator;
    private String TAG = "ImageRotation";
    private int cameraType;

    ImageRotation(String path, MainActivity.ImageRotator imageRotator, int cameraType) {
        this.path = path;
        this.imageRotator = imageRotator;
        this.cameraType = cameraType;
    }

    @Override
    protected Bitmap doInBackground(Void... params) {
        return ImageRotationHelper.correctImageOrientation(new File(path), cameraType);
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        imageRotator.rotateImage(bitmap);
    }
}
package com.google.android.cameraview;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;

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

/**
 * Created by rajesh on 26/1/17.
 */

public class ImageRotationHelper {
    // isOldCameraApi is a flag I use if the Preview is Camera1 or Camera2 because on older devices
    // you just have to rotate the image on certain types
    public static Bitmap correctImageOrientation(File file, int cameraType) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap bitmap = null;
        try {
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            bitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
            ExifInterface ei = new ExifInterface(file.getAbsolutePath());
            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
            return getRotatedBitmap(bitmap, orientation, cameraType);
        } catch (IOException e) {
            e.printStackTrace();
            return bitmap;
        }
    }

    private static Bitmap getRotatedBitmap(Bitmap bitmap, int orientation, int cameraType) {

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                bitmap = rotateImage(bitmap, 90);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                bitmap = rotateImage(bitmap, 180);
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                bitmap = rotateImage(bitmap, 270);
                break;
            case ExifInterface.ORIENTATION_NORMAL:
            case ExifInterface.ORIENTATION_UNDEFINED:
                if (cameraType == CameraView.FACING_FRONT) {
                    bitmap = rotateImage(bitmap, 180);
                }
                break;
            default:
                break;
        }
        return 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);
    }
}

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">

<FrameLayout
    android:id="@+id/camera"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"/>

xp-vit commented 7 years ago

I think that lirary should not rotate image itself, instead it should provide clear info on the fact that image should be rotated. For back camera I was able to manage rotation based on Exif info (relying on ExifInterface.TAG_ORIENTATION) and then rotating image based on found value. But I'm having issues with detecting rotation angle for front camera for some devices. I'm also using same logic based on Exif. Here is result of my tests:

Phone Android version ExifInterface.TAG_ORIENTATION actual result
LG Nexus 4 5.1.1 ExifInterface.ORIENTATION_UNDEFINED = 0 image rotated 180
LG Nexus 5x 7.1.1 ExifInterface.ORIENTATION_UNDEFINED = 0 image has normal orientation
HTC Desire 320 4.4.2 ExifInterface.ORIENTATION_NORMAL = 1 image rotated 180

I also spend more time and tried same phones with different implementations of Camera in CameraView.java: Camera1 Camera2 and Camera2Api23. Here are the results:

LG Nexus 5x, Android 7.1.1

Implementation ExifInterface.TAG_ORIENTATION actual result
Camera1 ExifInterface.ORIENTATION_UNDEFINED = 0 image rotated 180
Camera2 ExifInterface.ORIENTATION_UNDEFINED = 0 image has normal orientation
Camera2Api23 ExifInterface.ORIENTATION_UNDEFINED = 0 HTC Desire 320 4.4.2

LG Nexus 4, Android 5.0.1

Implementation ExifInterface.TAG_ORIENTATION actual result
Camera1 ExifInterface.ORIENTATION_UNDEFINED = 0 image rotated 180
Camera2 ExifInterface.ORIENTATION_UNDEFINED = 0 image rotated 180
Camera2Api23 ExifInterface.ORIENTATION_UNDEFINED = 0 image rotated 180

I also have testes with Nexus 5, and the result was: image has normal orientation with default lib start, I don't have it handy to provide more details for now.

jinujayakumar commented 7 years ago

@xp-vit i am facing the same problem

I found this code from Camera2Basic and add Camera2 with these code

`private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); }

/**

change the line in Camera2

captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, (sensorOrientation + mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) + 360) % 360);

to this

captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(0,sensorOrientation));

let me know if it works or not coz i dint have the above devices

for the case of front camera if it is Camera1

if (rotate == 0) matrix.postRotate(180); else if (rotate == 90) matrix.postRotate(270);

snowpong commented 7 years ago

@yaraki I have the same problem on a Sony D6503 (Xperia Z2) running Android 6.0.1 (API23). The captured image is rotated 180 degrees.

@xp-vit Why shouldn't the library ensure the captured image has the right orientation? In my opinion any phone manufacturers idiosyncrasies should be ironed out by this library.

xp-vit commented 7 years ago

@snowpong

One of the reasons why I think that lib should NOT be rotating images itself, but just provide proper info is that Image rotation is memory consuming process and may fail with OOM anytime (we've seen this a lot in our app).That's why some developers may just decide to have this work done using their back-end services. However, in case if this is important, lib might need to have this behavior configurable.

BTW, have you tried solution provided by @jinujayakumar on that Sony D6503 (Xperia Z2)? I'm going to test this on Nexus 5X later today and will post the results.

snowpong commented 7 years ago

@xp-vit OK, nobody wants OOM. But at the moment when we (the users of the library) get to the data it's a JPEG byte[] array, and there is no way we can rotate it without first reading it into a Bitmap and then creating another rotated one from it, which is sure to use a lot of memory. Ideally it'd be fixed earlier in the pipeline by the library if possible.

I haven't tried @jinujayakumar suggestion yet. I think I'm just going to read the EXIF information (using the support library https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html) and rotate myself.

xp-vit commented 7 years ago

@snowpong Reading Exif just won't help. We have Exif reading in place, but for example on Nexus 5x Exif rotation = 0 when using Camera2, however image is upside down.

snowpong commented 7 years ago

@xp-vit You're right. EXIF seems to be the same on the Z2 for both front and back facing, yet front facing is upside down.

mkotyk commented 7 years ago

I'm seeing front facing camera orientation issues on Moto G and Samsung S5. The Exif data does not have an orientation specified in the final output. Not sure if it's being stripped, but it should be there even if it's 0 for normal.

mkotyk commented 7 years ago

@snowpong I found a cool library called LLJTran which will rotate a JPEG image without decompressing it.

snowpong commented 7 years ago

@mkotyk Did you get it working?

I'm thinking @jinujayakumar might be on to something and that https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L829 should be used at https://github.com/google/cameraview/blob/master/library/src/main/api21/com/google/android/cameraview/Camera2.java#L608) but the Camera2Basic example explicitly does not support front facing cameras https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L499 so I'm not sure if it's a proper solution for front facing - I haven't had time to test yet

mkotyk commented 7 years ago

I'm seeing a possible issue in the Camera1 api. The docs use two different formulas for calculating rotation. One for setDisplayOrientation and one for setRotation. In cameraview it uses the same function to calculate both. Going to experiment with that a bit. Haven't looked at what Camera2 does yet.

https://developer.android.com/reference/android/hardware/Camera.Parameters.html#setRotation(int) https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)

mkotyk commented 7 years ago

Hmm I solved it for my situation on Camera1. When switching to back/facing camera it wouldn't call setRotation because if (currentSize.width != previewSize.getWidth() || currentSize.height != previewSize.getHeight()) { would jump over that block. That why I wasn't getting any orientation tag in the exif data. I'll probably have a PR later today.

mkotyk commented 7 years ago

I submitted a PR that fixed Camera1 front and back orientation issues for all my test devices, which is sadly only 3. I would appreciate any willing testers and feedback.

https://github.com/google/cameraview/pull/115

snowpong commented 7 years ago

I now tested the latest commit (a9d1eb9bad93dd8da981d380e37f58e35f847af5) to capture a front facing photo on a Huawei Mate 9 Pro (API-24) and it's rotated by 90 degrees CW.

I haven't tested if @mkotyk PR fixes things as that is for Camera1 and my two devices use Camera2.

snowpong commented 7 years ago

@mkotyk Just verified your PR fixes the 180 issue I had on front facing photos taken on a Sony Xperia Z2 (turns out it falls back to Camera1)

However, regarding the front facing issues I experience on my Huawei Mate 9 Pro (API-24) , I've now tried all sorts of calculations to find the right JPEG_ORIENTATION to set in Camera2 (https://github.com/google/cameraview/blob/master/library/src/main/api21/com/google/android/cameraview/Camera2.java#L608) but it seems it doesn't matter what I specify there. The end result is the same.

I explicitly tried to set 0 and 90 directly, and verified the CaptureRequest contains this value for JPEG_ORIENTATION, but when I save the picture to file, the EXIF TAG_ORIENTATION stays the same and the displayed image has not changed in rotation.

It actually seems that whatever I put as JPEG_ORIENTATION is ignored?

I've verified by debugging and Log output that my codepath is actually run. Any ideas or similar experiences? Shouldn't changing the JPEG_ORIENTATION either result in an EXIF TAG_ORIENTATION change, or an actual change in the JPEG file saved? @xp-vit and @jinujayakumar did any of you resolve your Camera2 front facing issues?

snowpong commented 7 years ago

I moved on to https://github.com/flurgle/CameraKit-Android (a fork of this project). Front facing camera works fine there.