无法从 EXIF 获取图像方向
Can't get image orientation from EXIF
目前,我正在用 Kotlin 为我们公司的微型图书馆编写一个函数,该函数读取图像的字节和 returns 以度为单位的方向
我知道,在 API 24 中我们有 ExifInterface
和从 InputStream
实例化它的能力,但问题是我们需要支持 API 21,这不支持有这样的构造函数。
传递给 getOrientation
函数的字节数组总是如下所示:
-1, -40, -1, -32, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 72, 0, 72, 0, 0, -1, -31, 8, 82, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42, 0, 0, 0, 8, 0, 12, 1, 15, 0, 2, 0, 0, 0, 6, 0, 0, 0, -98, 1, 16, 0, 2, 0, 0, 0, 9, 0, 0, 0, -92, 1, 18, 0, 3, 0, 0, 0, 1, 0, 6, 0, 0, 1, 26, 0, 5, 0, 0, 0, 1, 0, 0, 0, -82, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0, -74, 1, 40, 0 and so on
貌似移位了,所以我在第一行右移了256
这是我现在坚持使用的代码:
object Exif {
fun getOrientation(_bytes: ByteArray): Int {
val bytes = _bytes.map { b -> b.toInt() + 256 }
if (bytes[0] != 0xff && bytes[1] != 0xd8) {
return 0
}
val length = bytes.size
var offset = 2
while (offset < length) {
// TODO: extract all operations like the following
// into separate function
val marker = (bytes[offset] shl 8) or bytes[offset + 1]
offset += 2
if (marker == 0xffe1) {
offset += 2
val exifStr = (bytes[offset] shl 24) or (bytes[offset + 1] shl 16) or (bytes[offset + 2] shl 8) or bytes[offset + 3]
if (exifStr != 0x45786966) {
return 0
}
offset += 6
val little = (bytes[offset] shl 8) or bytes[offset + 1] == 0x4949
offset += 4
val inc = (bytes[offset] shl 24) or (bytes[offset + 1] shl 16) or (bytes[offset + 2] shl 8) or bytes[offset + 3]
offset += if (little) inc.reverseBytes() else inc
val tagsWOEndian = (bytes[offset] shl 8) or bytes[offset + 1]
val tags = if (little) tagsWOEndian.reverseBytes() else tagsWOEndian
offset += 2
for (idx in 0..tags) {
val off = offset + idx * 12
val orientWOEndian = (bytes[off] shl 8) or bytes[off + 1]
val orient = if (little) orientWOEndian.reverseBytes() else orientWOEndian
if (orient == 0x0112) {
when ((bytes[off + 8] shl 8) or bytes[off + 8 + 1]) {
1 -> return 0
3 -> return 180
6 -> return 90
8 -> return 270
}
}
}
} else if (marker and 0xff00 != 0xff00) {
break
} else {
offset += (bytes[offset] shl 8) or bytes[offset + 1]
}
}
return 0
}
}
fun Int.reverseBytes(): Int {
val v0 = ((this ushr 0) and 0xFF)
val v1 = ((this ushr 8) and 0xFF)
val v2 = ((this ushr 16) and 0xFF)
val v3 = ((this ushr 24) and 0xFF)
return (v0 shl 24) or (v1 shl 16) or (v2 shl 8) or (v3 shl 0)
}
已解决!
我最终得到了以下解决方案:
fun getUint16(bytes: List<Int>, offset: Int, littleEndian: Boolean): Int {
val value = (bytes[offset] shl 8) or bytes[offset + 1]
return if (littleEndian) value.reverseBytes() else value
}
fun getUint32(bytes: List<Int>, offset: Int, littleEndian: Boolean): Int {
val value = (bytes[offset] shl 24) or (bytes[offset + 1] shl 16) or (bytes[offset + 2] shl 8) or bytes[offset + 3]
return if (littleEndian) value.reverseBytes() else value
}
fun Int.reverseBytes(): Int {
val v0 = ((this ushr 0) and 0xFF)
val v1 = ((this ushr 8) and 0xFF)
val v2 = ((this ushr 16) and 0xFF)
val v3 = ((this ushr 24) and 0xFF)
return (v0 shl 24) or (v1 shl 16) or (v2 shl 8) or (v3 shl 0)
}
object Exif {
fun getRotationDegrees(_bytes: ByteArray): Int {
val bytes = _bytes.take(64 * 1024).map { b -> b.toInt() and 0xff }
if (getUint16(bytes, 0, false) != 0xffd8) {
return 0
}
val length = bytes.size
var offset = 2
while (offset < length) {
val marker = getUint16(bytes, offset, false)
offset += 2
if (marker == 0xffe1) {
if (getUint32(bytes, offset + 2, false) != 0x45786966) {
return 0
}
offset += 2
val little = getUint16(bytes, offset + 6, false) == 0x4949
offset += 6
offset += getUint32(bytes, offset + 4, little)
val tags = getUint16(bytes, offset, little)
offset += 2
for (idx in 0..tags) {
if (getUint16(bytes, offset + (idx * 12), little) == 0x0112) {
when (getUint16(bytes, offset + (idx * 12) + 8, little)) {
1 -> return 0
3 -> return 180
6 -> return 90
8 -> return 270
}
}
}
} else if (marker and 0xff00 != 0xff00) {
break
} else {
offset += getUint16(bytes, offset, false)
}
}
return 0
}
}
与问题中发布的代码相比的变化:
- 有符号
_bytes
和无符号bytes
之间的转换执行错误。现在我首先获取前 65536 个字节,然后通过对每个字节应用按位 and
而不是简单地添加 256 将它们转换为无符号
- 从字节数组中获取 UInt16 和 UInt32 值的所有操作都已移至单独的函数
- 修复了
while
循环中错误的 offset
增量
目前,我正在用 Kotlin 为我们公司的微型图书馆编写一个函数,该函数读取图像的字节和 returns 以度为单位的方向
我知道,在 API 24 中我们有 ExifInterface
和从 InputStream
实例化它的能力,但问题是我们需要支持 API 21,这不支持有这样的构造函数。
传递给 getOrientation
函数的字节数组总是如下所示:
-1, -40, -1, -32, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 72, 0, 72, 0, 0, -1, -31, 8, 82, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42, 0, 0, 0, 8, 0, 12, 1, 15, 0, 2, 0, 0, 0, 6, 0, 0, 0, -98, 1, 16, 0, 2, 0, 0, 0, 9, 0, 0, 0, -92, 1, 18, 0, 3, 0, 0, 0, 1, 0, 6, 0, 0, 1, 26, 0, 5, 0, 0, 0, 1, 0, 0, 0, -82, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0, -74, 1, 40, 0 and so on
貌似移位了,所以我在第一行右移了256
这是我现在坚持使用的代码:
object Exif {
fun getOrientation(_bytes: ByteArray): Int {
val bytes = _bytes.map { b -> b.toInt() + 256 }
if (bytes[0] != 0xff && bytes[1] != 0xd8) {
return 0
}
val length = bytes.size
var offset = 2
while (offset < length) {
// TODO: extract all operations like the following
// into separate function
val marker = (bytes[offset] shl 8) or bytes[offset + 1]
offset += 2
if (marker == 0xffe1) {
offset += 2
val exifStr = (bytes[offset] shl 24) or (bytes[offset + 1] shl 16) or (bytes[offset + 2] shl 8) or bytes[offset + 3]
if (exifStr != 0x45786966) {
return 0
}
offset += 6
val little = (bytes[offset] shl 8) or bytes[offset + 1] == 0x4949
offset += 4
val inc = (bytes[offset] shl 24) or (bytes[offset + 1] shl 16) or (bytes[offset + 2] shl 8) or bytes[offset + 3]
offset += if (little) inc.reverseBytes() else inc
val tagsWOEndian = (bytes[offset] shl 8) or bytes[offset + 1]
val tags = if (little) tagsWOEndian.reverseBytes() else tagsWOEndian
offset += 2
for (idx in 0..tags) {
val off = offset + idx * 12
val orientWOEndian = (bytes[off] shl 8) or bytes[off + 1]
val orient = if (little) orientWOEndian.reverseBytes() else orientWOEndian
if (orient == 0x0112) {
when ((bytes[off + 8] shl 8) or bytes[off + 8 + 1]) {
1 -> return 0
3 -> return 180
6 -> return 90
8 -> return 270
}
}
}
} else if (marker and 0xff00 != 0xff00) {
break
} else {
offset += (bytes[offset] shl 8) or bytes[offset + 1]
}
}
return 0
}
}
fun Int.reverseBytes(): Int {
val v0 = ((this ushr 0) and 0xFF)
val v1 = ((this ushr 8) and 0xFF)
val v2 = ((this ushr 16) and 0xFF)
val v3 = ((this ushr 24) and 0xFF)
return (v0 shl 24) or (v1 shl 16) or (v2 shl 8) or (v3 shl 0)
}
已解决! 我最终得到了以下解决方案:
fun getUint16(bytes: List<Int>, offset: Int, littleEndian: Boolean): Int {
val value = (bytes[offset] shl 8) or bytes[offset + 1]
return if (littleEndian) value.reverseBytes() else value
}
fun getUint32(bytes: List<Int>, offset: Int, littleEndian: Boolean): Int {
val value = (bytes[offset] shl 24) or (bytes[offset + 1] shl 16) or (bytes[offset + 2] shl 8) or bytes[offset + 3]
return if (littleEndian) value.reverseBytes() else value
}
fun Int.reverseBytes(): Int {
val v0 = ((this ushr 0) and 0xFF)
val v1 = ((this ushr 8) and 0xFF)
val v2 = ((this ushr 16) and 0xFF)
val v3 = ((this ushr 24) and 0xFF)
return (v0 shl 24) or (v1 shl 16) or (v2 shl 8) or (v3 shl 0)
}
object Exif {
fun getRotationDegrees(_bytes: ByteArray): Int {
val bytes = _bytes.take(64 * 1024).map { b -> b.toInt() and 0xff }
if (getUint16(bytes, 0, false) != 0xffd8) {
return 0
}
val length = bytes.size
var offset = 2
while (offset < length) {
val marker = getUint16(bytes, offset, false)
offset += 2
if (marker == 0xffe1) {
if (getUint32(bytes, offset + 2, false) != 0x45786966) {
return 0
}
offset += 2
val little = getUint16(bytes, offset + 6, false) == 0x4949
offset += 6
offset += getUint32(bytes, offset + 4, little)
val tags = getUint16(bytes, offset, little)
offset += 2
for (idx in 0..tags) {
if (getUint16(bytes, offset + (idx * 12), little) == 0x0112) {
when (getUint16(bytes, offset + (idx * 12) + 8, little)) {
1 -> return 0
3 -> return 180
6 -> return 90
8 -> return 270
}
}
}
} else if (marker and 0xff00 != 0xff00) {
break
} else {
offset += getUint16(bytes, offset, false)
}
}
return 0
}
}
与问题中发布的代码相比的变化:
- 有符号
_bytes
和无符号bytes
之间的转换执行错误。现在我首先获取前 65536 个字节,然后通过对每个字节应用按位and
而不是简单地添加 256 将它们转换为无符号
- 从字节数组中获取 UInt16 和 UInt32 值的所有操作都已移至单独的函数
- 修复了
while
循环中错误的offset
增量