RedApparat / Fotoapparat

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

Rotation degrees of Frame object is incorrect #253

Closed siralam closed 5 years ago

siralam commented 6 years ago

Not sure if this is by design or a bug,

The rotation value of a Frame obtained in FrameProcessor.process(Frame frame) is always 270, even if the device is held in landscape (But OS is in portrait mode)

Of course, if I made the activity / device setting rotatable, the activity is recreated with a new orientation, and now the rotation degree is correct.

However, this is not a good camera app UX since user would feel significant freeze when the activity is rotating.

I would definitely want the frame to have correct rotation degrees without changing activity orientation.

So... is it a bug or you expect us to implement by ourselves using orientation sensor?

(I suspect it is a bug more than by design because, same thing does not happen in taking a picture, so it means you did detect rotation using orientation sensor in your library. Probably just didn't implemented for Frame.)

AlCabohne commented 6 years ago

When I try, it is not always 270. When Im in Portrait Mode it shows 270 when it should actually be 90. When I rotate clockwise to 180, it shows correctly 180. So in my case it shows 270 when its is actually 90. And it shows 90 when it is actually 270. So these two are somehow flipped. 0 degress and 180 works correct.

dmitry-zaitsev commented 6 years ago

@siralam it does seem to be a bug. Most likely the same as #262

@AlCabohne are you using the front or the rear camera?

AlCabohne commented 6 years ago

@dmitry-zaitsev Im using the rear camera (main camera)

PaulWoitaschek commented 6 years ago

Any news on this? Can I assume that it's always off by 180 degrees on every device? Or is this device specifid? For me it happens on a Google Pixel and I need the frame to have the correct orientation for further processing.

PaulWoitaschek commented 6 years ago

While further observing I have exactly the behavior @AlCabohne descriped.

I don't understand the code base but this looked like a smell:

https://github.com/RedApparat/Fotoapparat/blob/b55ebc6c6891775a848e2faa22ef8761e2d69bf0/fotoapparat/src/main/java/io/fotoapparat/routine/camera/StartRoutine.kt#L49

        setDisplayOrientation(
                orientationState = OrientationState(
                        deviceOrientation = Orientation.Vertical.Portrait,
                        screenOrientation = getScreenOrientation()
                )
        )

here upon start it's hardcoded.

fazalBykea commented 6 years ago

is there any update? Have you fixed the issue or not?

XinyueZ commented 6 years ago

I solved rotation problem @ frame process with following codes, it might be little outof scope of this ticket, however, it would help you a little.

Front

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, true))
                        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
        )
    }
PaulWoitaschek commented 6 years ago

@XinyueZ The issue is not how to rotate a yuv array, the issue is that the given rotation of the frame is not the actual rotation.

FireZenk commented 5 years ago

Still no fix for this issue? Im getting always CameraScreen$onCreate: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Horizontal$ReverseLandscape@997b270.

FireZenk commented 5 years ago

Also seeing the samples you do this "hack": imageView.rotation = (-it.rotationDegrees).toFloat() ???!!!

That comes from your BitmapPhoto but what happens if we want to use the photo another moment? we gonna get the File, and then it will be rotated with no solution at all

dmitry-zaitsev commented 5 years ago

@siralam image orientation might differ from device orientation all because the camera hardware sensor can be mounted in whatever rotation a vendor decided. Hence, we can't guarantee what the rotation of the image will be. However, that is exactly why we are providing you rotation in degrees as part of the frame.

@FireZenk you can read EXIF information from the file - it contains information about image orientation. Setting image rotation via imageView.rotation is a recommended solution as it requires much less memory and less CPU expensive.

PaulWoitaschek commented 5 years ago

Why is this closed?

However, that is exactly why we are providing you rotation in degrees as part of the frame.

The issue is that the orientation is always 270.