Camera.takePicture returns 一个旋转的字节数组
Camera.takePicture returns a rotated byteArray
我正在尝试使用 hardware.camera
制作自定义相机应用。
我实现了一个PictureCallback
,它会在拍摄照片时写入具有特定路径的文件。写入文件的data
是相机API.takePicture
返回的ByteArray
。
所以写入文件后,我注意到垂直拍摄的照片是水平保存的。问题不是因为 Exif
标记导致 byteArray 在写入文件之前和之后都有 ORIENTATION_NORMAL
。
写入文件的data
是相机API.takePicture
返回的ByteArray
。
这是 takePicture
在 Camera.Java
中的样子:
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback jpeg) {
takePicture(shutter, raw, null, jpeg);
}
这是 CameraPreview
的一部分,将用于拍摄照片:
相机预览代码
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
所以我真的不知道它是否旋转了。
编辑 2 :
示例照片是在纵向模式下拍摄的,并从文件管理器 [!
并非如此,这些结果在模拟器和真实 android phone 上都进行了测试,它们是相同的。
预习:
Preview
从文件管理器中捕获的图像:
Captured image from file manager
这个问题中几乎没有与这种情况重叠的问题,因此我花了很长时间才明白到底发生了什么。
你做了什么,你从相机收到了一个有效的 Jpeg ByteArray
,并且这个流包含一些 EXIF 信息,但它缺少方向标签。这发生在许多设备上,也在 Xiaomi Mi.
上
所以,您无法正确旋转位图。但是您确切地知道 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
或 Surface.ROTATION_270
。
要选择正确的旋转方式,您必须了解硬件的组装方式,即相机传感器如何与设备对齐。 orientation of the camera 可以是 0、90、180 或 270。
您可能在不同来源看到过这段代码:
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)
此代码允许相机预览与您的屏幕正确对齐;您的应用程序中可能也有这个。相同的代码可用于在 fixImageRotation()
if getExifOrientation()
returns ExifInterface.ORIENTATION_UNKNOWN
.
中选择正确的位图旋转
在某些情况下,您需要有关设备方向的更多详细信息,如 here 所述。
无论如何,我建议您切换到现代 CameraX API, which provides better support for most devices. It allows me to call ImageCapture.setTargetRotation(),生成的 Jpeg 由图书馆为我旋转。
我正在尝试使用 hardware.camera
制作自定义相机应用。
我实现了一个PictureCallback
,它会在拍摄照片时写入具有特定路径的文件。写入文件的data
是相机API.takePicture
返回的ByteArray
。
所以写入文件后,我注意到垂直拍摄的照片是水平保存的。问题不是因为 Exif
标记导致 byteArray 在写入文件之前和之后都有 ORIENTATION_NORMAL
。
写入文件的data
是相机API.takePicture
返回的ByteArray
。
这是 takePicture
在 Camera.Java
中的样子:
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback jpeg) {
takePicture(shutter, raw, null, jpeg);
}
这是 CameraPreview
的一部分,将用于拍摄照片:
相机预览代码
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
所以我真的不知道它是否旋转了。
编辑 2 : 示例照片是在纵向模式下拍摄的,并从文件管理器 [! 并非如此,这些结果在模拟器和真实 android phone 上都进行了测试,它们是相同的。 预习: Preview
从文件管理器中捕获的图像: Captured image from file manager
这个问题中几乎没有与这种情况重叠的问题,因此我花了很长时间才明白到底发生了什么。
你做了什么,你从相机收到了一个有效的 Jpeg ByteArray
,并且这个流包含一些 EXIF 信息,但它缺少方向标签。这发生在许多设备上,也在 Xiaomi Mi.
所以,您无法正确旋转位图。但是您确切地知道 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
或 Surface.ROTATION_270
。
要选择正确的旋转方式,您必须了解硬件的组装方式,即相机传感器如何与设备对齐。 orientation of the camera 可以是 0、90、180 或 270。
您可能在不同来源看到过这段代码:
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)
此代码允许相机预览与您的屏幕正确对齐;您的应用程序中可能也有这个。相同的代码可用于在 fixImageRotation()
if getExifOrientation()
returns ExifInterface.ORIENTATION_UNKNOWN
.
在某些情况下,您需要有关设备方向的更多详细信息,如 here 所述。
无论如何,我建议您切换到现代 CameraX API, which provides better support for most devices. It allows me to call ImageCapture.setTargetRotation(),生成的 Jpeg 由图书馆为我旋转。