RedApparat / Fotoapparat

Making Camera for Android more friendly. 📸
Apache License 2.0
3.82k stars 405 forks source link

Front facing camera always rotates the result #193

Closed SAGARSURI closed 5 years ago

SAGARSURI commented 6 years ago

I am using the latest version of your library. I am using only front facing camera. I took the code from your sample app. But when I take the picture from front facing camera, the result is rotated and zoomed. I have to rotate the result bitmap to 270 degree and then flip it to get the proper result. May I know the actual issue and How can it be fixed ? Here is my code:

private Fotoapparat createFotoapparat() {
        return Fotoapparat
                .with(this)
                .photoResolution(standardRatio(
                    highestResolution()
                ))
                .into(cameraView)
                .previewScaleType(ScaleType.CenterCrop)
                .lensPosition(front())
                .focusMode(firstAvailable(
                        continuousFocusPicture(),
                        autoFocus(),
                        fixed()
                ))
                .flash(firstAvailable(
                        autoRedEye(),
                        autoFlash(),
                        torch(),
                        off()
                ))
                .previewFpsRange(highestFps())
                .sensorSensitivity(highestSensorSensitivity())
                .logger(loggers(
                        logcat(),
                        fileLogger(this)
                ))
                .cameraErrorCallback(new CameraErrorListener() {
                    @Override
                    public void onError(CameraException e) {
                        Toast.makeText(CameraActivity.this, e.toString(), Toast.LENGTH_LONG).show();
                    }
                })
                .build();
    }
Diolor commented 6 years ago

Hi, thanks for reporting this. Which phone/(s) is affected? Is the sample app affected too?

SAGARSURI commented 6 years ago

Samsung galaxy s8, Google pixel. Both running on 7.0

Diolor commented 6 years ago

Is the sample app affected too?

SAGARSURI commented 6 years ago

In the sample app its flipping the image. But in my case its rotating as well as flipping. I am using the same code from your sample app.

andrewcking commented 6 years ago

EDIT: Here is a video: https://photos.app.goo.gl/nziWI3GMOqmMRul93

I am also seeing this on my devices. I don't think it is specific to the front facing camera though, rather it is something I see occurring after resuming the app while in landscape mode.

  1. Open the sample fotoapparat app
  2. Rotate the device into landscape mode
  3. Change to front facing camera
  4. Take photo and see incorrectly rotated result

Step # 3 can be replaced with any thing that switches or reinitializes the activity while in landscape mode. For instance, if you rotate to landscape, hit your recents button then return to the fotoapparat sample app and take a photo with the back facing camera the result will be rotated.

I would guess this might be due to an issue with #90 assuming the default orientation is portrait at some point and not updating the orientation to landscape because no orientation update triggers it.

Regarding #90 just in general, I do believe there are valid use cases for using system orientation instead of device orientation (which #90 reverses). For my use case I don't allow my camera activity into all 4 orientations (I think most camera apps prevent the upsidedown/reverse portrait orientation). For any application that doesn't allow all orientations, #90 complicates things.

Diolor commented 6 years ago

I think the issue in the video is different than the one in the reproduction steps. There reason for the first might be as @andrewcking decribed the updated orientation state is lost. For the second just a wrong calculation.

ptrbrynt commented 6 years ago

@Diolor Also having this issue on Galaxy Tab S2. Taking a front-facing photo with the device in landscape orientation is fine, but portrait orientation photos are rotated by 90 degrees clockwise.

emilany commented 6 years ago

@Diolor Having the same issue with a BV6000s. How can this be addressed?

Diolor commented 6 years ago

@emilany The calculations inside OrientationResolver#computeImageOrientation need to be fixed

xxxifan commented 6 years ago

I noticed that when I specify camera resolution parameter, like I want width:1080xheight:1440, but I have to set width=1440, height=1080, or I cannot get camera preview interface. Is this one of reason why it change orientation?

andrewcking commented 6 years ago

For anyone curious, I was able to resolve the issue for my purposes by setting Device.startOrientationMonitoring in StartOrientationRoutine.kt to just be an empty method. This perhaps isn't a great practice but it prevents fotoapparat from trying to correct orientation based on the way you are holding the device. I then am handling the rotation myself (as was the case pre version 2) and saving the file myself.

I use the following for rotating the image:

  Matrix matrix = new Matrix();
  if (camera.equals("Front")) {
    matrix.postRotate(amountRotated-result.rotationDegrees);
  } else {
    matrix.postRotate(-amountRotated-result.rotationDegrees);
  }
  Bitmap rotated = Bitmap.createBitmap(result.bitmap, 0, 0, result.bitmap.getWidth(), result.bitmap.getHeight(), matrix, true);

Where amountRotated is the device orientation offset, calculated however you want (using your own orientation listener or basing it on activity orientation). Doing it this way allows me to prevent photos from being taken in certain orientations.

This seems to be working on front and back cameras and in all orientations on my devices including those with non-standard camera mountings like the Nexus 5x.

XinyueZ commented 5 years ago

I rotate bitmap and flip it while processing frames.

   override fun process(frame: Frame) {
        Log.d(TAG, "frame: ${frame.image.size}, ${frame.size}")

        YuvImage(
            frame.image,
            ImageFormat.NV21,
            frame.size.width,
            frame.size.height,
            null
        ).let { yuvImage ->
            ByteArrayOutputStream().use { output ->
                yuvImage.compressToJpeg(
                    Rect(0, 0, frame.size.width, frame.size.height),
                    100,
                    output
                )
                output.toByteArray().apply {
                    BitmapFactory.decodeByteArray(this, 0, size)?.let { bitmap ->
                        process(
                            bitmap.rotate(90f).flip(
                                false, activeCamera == Camera.Front
                            )
                        )
                        bitmap.recycle()
                    }
                }
            }
        }
    }

    private fun Bitmap.flip(horizontal: Boolean, vertical: Boolean): Bitmap {
        val matrix = Matrix()
        matrix.preScale((if (horizontal) -1 else 1).toFloat(), (if (vertical) -1 else 1).toFloat())
        return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
    }

    private fun Bitmap.rotate(degrees: Float): Bitmap {
        val matrix = Matrix()
        matrix.postRotate(degrees)
        val scaledBitmap = Bitmap.createScaledBitmap(this, width, height, true)
        return Bitmap.createBitmap(
            scaledBitmap,
            0,
            0,
            scaledBitmap.width,
            scaledBitmap.height,
            matrix,
            true
        )
    }