Camera.takePicture返回旋转的byteArray [英] Camera.takePicture returns a rotated byteArray

查看:264
本文介绍了Camera.takePicture返回旋转的byteArray的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用hardware.camera制作自定义相机应用.

I'm trying to make a custom camera app using hardware.camera.

我已经实现了PictureCallback,当拍摄照片时,它将以一定的路径写入文件.写入文件的data是相机API中的takePicture返回的ByteArray.

I've implemented a PictureCallback which will write into a file with a certain path when the picture is taken. The data written into the file is the ByteArray returned by takePicture in camera API.

因此,在写入文件后,我注意到垂直拍摄的图片是水平保存的.问题不是因为Exif标签导致byteArray在写入文件之前和之后都具有ORIENTATION_NORMAL.

So after writing into the file, I've noticed the picture taken vertically is saved horizontally. The problem wasn't because of the Exif tag cause the byteArray had ORIENTATION_NORMAL both before and after writing into the file.

写入文件中的data是相机API中takePicture返回的ByteArray.

The data written into the file is the ByteArray returned by takePicture in camera API.

这是Camera.JavaCamera.Java中的样子:

    public final void takePicture(ShutterCallback shutter, PictureCallback raw,
            PictureCallback jpeg) {
        takePicture(shutter, raw, null, jpeg);
    }

这是CameraPreview的一部分,它将捕获照片:

Here's part of the CameraPreview which will capture the photo :

相机预览代码

    val imageProcessor = ImageProcessor()
    private val fileSaver = FileSaver(context)
    fun capture() {
        val callback = PictureCallback { data, _ ->
            imageProcessor.process(data)?.apply {
                val file = fileSaver.saveBitmap(this, outputFileName ?: DEFAULT_FILE_NAME)
                onCaptureTaken?.invoke(file)
            }
        }
        camera?.takePicture(null, null, callback)
    }

ImageProcessor.kt的代码

class ImageProcessor {

    fun process(data: ByteArray): Bitmap? {
        val options = BitmapFactory.Options().apply {
            inMutable = true
        }

        val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, options)
        return fixImageRotation(data, bitmap)
    }
    private fun fixImageRotation(picture: ByteArray, bitmap: Bitmap): Bitmap? {
        return when (exifPostProcessor(picture)) {
            ExifInterface.ORIENTATION_ROTATE_90 ->
                rotateImage(bitmap, 90F)
            ExifInterface.ORIENTATION_ROTATE_180 ->
                rotateImage(bitmap, 180F)
            ExifInterface.ORIENTATION_ROTATE_270 ->
                rotateImage(
                    bitmap, 270F
                )
            ExifInterface.ORIENTATION_NORMAL -> bitmap
            else -> bitmap
        }
    }

    private fun rotateImage(source: Bitmap, angle: Float): Bitmap? {
        val matrix = Matrix()
        matrix.postRotate(angle)
        return Bitmap.createBitmap(
            source, 0, 0, source.width, source.height,
            matrix, true
        )
    }

    private fun exifPostProcessor(picture: ByteArray?): Int {
        try {
            return getExifOrientation(ByteArrayInputStream(picture))
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return -1
    }

    @Throws(IOException::class)
    private fun getExifOrientation(inputStream: InputStream): Int {
        val exif = ExifInterface(inputStream)
        return exif.getAttributeInt(
            ExifInterface.TAG_ORIENTATION,
            ExifInterface.ORIENTATION_NORMAL
        )
    }
}

FileSaver.kt的代码

internal class FileSaver(context: Context) {

    private val context: Context = context.applicationContext
    fun saveBitmap(bitmap: Bitmap, fileName: String): File {
        val file = File(mkdirsCacheFolder(), fileName)
        try {
            FileOutputStream(file).use { out ->
                bitmap.compress(Bitmap.CompressFormat.JPEG, ORIGINAL_QUALITY, out)
            }
            bitmap.recycle()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return file
    }


    private fun mkdirsCacheFolder(): File {
        return File(context.externalCacheDir, CACHE_DIRECTORY).apply {
            if (!exists()) {
                mkdirs()
            }
        }
    }

    companion object {
        private const val ORIGINAL_QUALITY = 100
        private const val CACHE_DIRECTORY = "/Lens"
    }
}

有什么建议吗?

我打印了Exif标签,结果是ORIENTATION_NORMAL,所以我真的不知道它是否旋转过.

I printed the Exif tag and it turns out to be ORIENTATION_NORMAL so I don't really know if it is rotated at all.

示例图片以人像模式拍摄,并从文件管理器中打开[! 并非如此,这些结果已在模拟器和真正的android手机上进行了测试,它们是相同的. 预览: 预览

Edit 2 : Sample pictures were taken in portrait mode and opened from file manager[! Not that, these results are tested on both emulator and real android phone and they are the same. Preview: Preview

从文件管理器捕获的图像: 从文件管理器捕获的图像

Captured image from file manager: Captured image from file manager

推荐答案

在这种情况下,很少遇到这种情况的重叠问题,因此花了我很长时间才能了解真正的情况.

Few issues with this situation got overlapped in this question, therefore it took me so long to understand what really was going on.

您所做的,您从相机收到了有效的Jpeg ByteArray,并且此流包含一些EXIF信息,但缺少方向标签.在许多设备上也会发生这种情况,在小米米上.

What you did, you received a valid Jpeg ByteArray from the camera, and this stream contained some EXIF information, but it was missing the orientation tag. This happens on many devices, also on Xiaomi Mi.

因此,您无法正确旋转位图.但是您完全知道活动的方向:preview.display.rotation.这应该告诉您在这种情况下应如何旋转位图,但是如果您的活动被锁定为纵向,则甚至无需检查.显示旋转范围可能在0…3范围内,它们表示 Surface.ROTATION_0 Surface.ROTATION_90Surface.ROTATION_180Surface.ROTATION_270.

So, you could not rotate the bitmap correctly. But you know exactly the orientation of your Activity: preview.display.rotation. This should tell you how the bitmap should be rotated in this case, but if your activity is locked into portrait, you don't even need to check. Display rotation may be in range 0…3 and these represent Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, or Surface.ROTATION_270.

要选择正确的旋转角度,您必须知道硬件的组装方式,即相机传感器与设备的对齐方式. 相机的方向可以是0、90、180或270.

To choose the correct rotation, you must know the way the hardware is assembled, i.e. how the camera sensor is aligned with the device. This orientation of the camera can be 0, 90, 180, or 270.

您可能已经在不同的来源中看到了这段代码:

You might have seen this piece of code in different sources:

var degrees = 0
when (preview.display.rotation) {
    Surface.ROTATION_0 -> degrees = 0
    Surface.ROTATION_90 -> degrees = 90
    Surface.ROTATION_180 -> degrees = 180
    Surface.ROTATION_270 -> degrees = 270
}
val ci = Camera.CameraInfo()
Camera.getCameraInfo(cameraId, ci)
if (ci.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
    degrees += ci.orientation
    degrees %= 360
    degrees = 360 - degrees
} else {
    degrees = 360 - degrees
    degrees += ci.orientation
}
camera!!.setDisplayOrientation(degrees % 360)

此代码可使摄像机预览与屏幕正确对齐;您可能也在应用程序中的某个位置.如果getExifOrientation()返回ExifInterface.ORIENTATION_UNKNOWN,则可以使用相同的代码在fixImageRotation()中选择正确的位图旋转.

This code allows the camera preview to be correctly aligned with your screen; you probably have this somewhere in your app, too. Same code can be used to choose the correct bitmap rotation in your fixImageRotation() if getExifOrientation() returns ExifInterface.ORIENTATION_UNKNOWN.

在某些情况下,您需要有关设备方向的更多详细信息,如此处所述.

In some cases, you need more detailed info about the device orientation, as explained here.

无论如何,我建议您切换到现代的 CameraX API,该API可以提供更好的支持大多数设备.它允许我调用 ImageCapture.setTargetRotation(),然后库将为我旋转生成的Jpeg.

Anyways, I would recommend you to switch to the modern CameraX API, which provides better support for most devices. It allows me to call ImageCapture.setTargetRotation() and the resulting Jpeg is is rotated for me by the library.

这篇关于Camera.takePicture返回旋转的byteArray的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆