【发布时间】:2020-12-02 00:44:04
【问题描述】:
我正在尝试使用 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 手机上都进行了测试,并且它们是相同的。 预习: Preview
从文件管理器捕获的图像: Captured image from file manager
【问题讨论】:
-
我认为大部分都包含在代码中。输入文件名,然后使用该名称创建一个文件,然后将其写入。 @blackapps
-
@blackapps 对不起 :( 输入文件名是我传递给此函数的硬编码字符串。我应该将其包含在描述中吗?
-
@blackapps 我添加了一个编辑。感谢您的反馈
-
嘿嘿...终于有我要的信息了。数据字节数组包含一个 jpg 文件,您正在将字节保存到文件中。请在您的帖子中输入您获得的 jpg 图像,并将其保存到文件中。并像我一样移除 cmets。
-
在写入文件时,标签被忽略——这很奇怪。您的代码写入所有 jpeg 字节,包括 EXIF 标头。也许您的意思是您的 viewer 忽略了这些标志?我建议尝试一些好的查看器(即不是 Windows 默认)来检查这一点。如果您发现 EXIF 标头损坏,您可以修复它(请参阅ExifInterface 库)。如果您希望图像与忽略标题的查看器兼容,您别无选择,只能自己执行旋转。