Skip to content

Commit 86f4268

Browse files
committed
Barcode Optimization
1.Added new processing for rotation 2.Image recognition support
1 parent 27bacdd commit 86f4268

File tree

4 files changed

+154
-23
lines changed

4 files changed

+154
-23
lines changed

play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDecodeHelper.kt

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,16 @@ import android.media.Image
1111
import android.os.Build.VERSION.SDK_INT
1212
import android.util.Log
1313
import androidx.annotation.RequiresApi
14-
import com.google.zxing.*
14+
import com.google.zxing.BarcodeFormat
15+
import com.google.zxing.BinaryBitmap
16+
import com.google.zxing.ChecksumException
17+
import com.google.zxing.DecodeHintType
18+
import com.google.zxing.FormatException
19+
import com.google.zxing.LuminanceSource
20+
import com.google.zxing.NotFoundException
21+
import com.google.zxing.PlanarYUVLuminanceSource
22+
import com.google.zxing.RGBLuminanceSource
23+
import com.google.zxing.Result
1524
import com.google.zxing.common.HybridBinarizer
1625
import java.nio.ByteBuffer
1726
import java.nio.IntBuffer
@@ -21,6 +30,7 @@ private const val TAG = "BarcodeDecodeHelper"
2130
class BarcodeDecodeHelper(formats: List<BarcodeFormat>, multi: Boolean = true) {
2231
private val reader = MultiBarcodeReader(
2332
mapOf(
33+
DecodeHintType.TRY_HARDER to true,
2434
DecodeHintType.ALSO_INVERTED to true,
2535
DecodeHintType.POSSIBLE_FORMATS to formats
2636
)
@@ -43,22 +53,56 @@ class BarcodeDecodeHelper(formats: List<BarcodeFormat>, multi: Boolean = true) {
4353
}
4454
}
4555

46-
fun decodeFromLuminanceBytes(bytes: ByteArray, width: Int, height: Int, rowStride: Int = width): List<Result> {
47-
return decodeFromSource(PlanarYUVLuminanceSource(bytes, rowStride, height, 0, 0, width, height, false))
56+
fun decodeFromLuminanceBytes(rawBarcodeData: RawBarcodeData, rotate: Int): List<Result> {
57+
Log.d(TAG, "decodeFromLuminanceBytes rotate:")
58+
rawBarcodeData.rotateDetail(rotate)
59+
return decodeFromSource(
60+
PlanarYUVLuminanceSource(
61+
rawBarcodeData.bytes, rawBarcodeData.width, rawBarcodeData.height,
62+
0, 0, rawBarcodeData.width, rawBarcodeData.height, false
63+
)
64+
)
4865
}
4966

50-
fun decodeFromLuminanceBytes(buffer: ByteBuffer, width: Int, height: Int, rowStride: Int = width): List<Result> {
67+
fun decodeFromLuminanceBytes(buffer: ByteBuffer, width: Int, height: Int, rotate: Int = 0): List<Result> {
5168
val bytes = ByteArray(buffer.remaining())
5269
buffer.get(bytes)
5370
buffer.rewind()
54-
return decodeFromLuminanceBytes(bytes, width, height, rowStride)
71+
val rawBarcodeData = RawBarcodeData(bytes, width, height)
72+
return decodeFromLuminanceBytes(rawBarcodeData, rotate)
5573
}
5674

5775
@RequiresApi(19)
58-
fun decodeFromImage(image: Image): List<Result> {
76+
fun decodeFromImage(image: Image, rotate: Int = 0): List<Result> {
5977
if (image.format !in SUPPORTED_IMAGE_FORMATS) return emptyList()
60-
val yPlane = image.planes[0]
61-
return decodeFromLuminanceBytes(yPlane.buffer, image.width, image.height, yPlane.rowStride)
78+
val rawBarcodeData =RawBarcodeData(getYUVBytesFromImage(image), image.width, image.height)
79+
return decodeFromLuminanceBytes(rawBarcodeData, rotate)
80+
}
81+
82+
private fun getYUVBytesFromImage(image: Image): ByteArray {
83+
val planes = image.planes
84+
val width = image.width
85+
val height = image.height
86+
val yuvBytes = ByteArray(width * height * 3 / 2)
87+
var offset = 0
88+
89+
for (i in planes.indices) {
90+
val buffer = planes[i].buffer
91+
val rowStride = planes[i].rowStride
92+
val pixelStride = planes[i].pixelStride
93+
val planeWidth = if ((i == 0)) width else width / 2
94+
val planeHeight = if ((i == 0)) height else height / 2
95+
96+
val planeBytes = ByteArray(buffer.capacity())
97+
buffer[planeBytes]
98+
99+
for (row in 0 until planeHeight) {
100+
for (col in 0 until planeWidth) {
101+
yuvBytes[offset++] = planeBytes[row * rowStride + col * pixelStride]
102+
}
103+
}
104+
}
105+
return yuvBytes
62106
}
63107

64108
fun decodeFromBitmap(bitmap: Bitmap): List<Result> {

play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDetector.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class BarcodeDetector(val context: Context, val options: BarcodeDetectorOptions)
3838
override fun detectBytes(wrappedByteBuffer: IObjectWrapper, metadata: FrameMetadataParcel): Array<Barcode> {
3939
if (!loggedOnce) Log.d(TAG, "detectBytes(${ObjectWrapper.unwrap(wrappedByteBuffer)}, $metadata)").also { loggedOnce = true }
4040
val bytes = wrappedByteBuffer.unwrap<ByteBuffer>() ?: return emptyArray()
41-
return helper.decodeFromLuminanceBytes(bytes, metadata.width, metadata.height)
41+
return helper.decodeFromLuminanceBytes(bytes, metadata.width, metadata.height, metadata.rotation)
4242
.mapNotNull { runCatching { it.toGms(metadata) }.getOrNull() }.toTypedArray()
4343
}
4444

@@ -224,12 +224,7 @@ private fun Result.toGms(metadata: FrameMetadataParcel): Barcode {
224224
barcode.rawBytes = rawBytes
225225
barcode.rawValue = text
226226
barcode.cornerPoints = resultPoints.map {
227-
when (metadata.rotation) {
228-
1 -> Point(metadata.height - it.y.toInt(), it.x.toInt())
229-
2 -> Point(metadata.width - it.x.toInt(), metadata.height - it.y.toInt())
230-
3 -> Point(it.y.toInt(), metadata.width - it.x.toInt())
231-
else -> Point(it.x.toInt(), it.y.toInt())
232-
}
227+
Point(it.x.toInt(), it.y.toInt())
233228
}.toTypedArray()
234229

235230
val parsed = ResultParser.parseResult(this)

play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeScanner.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.microg.gms.vision.barcode
77

88
import android.content.Context
9+
import android.graphics.Bitmap
910
import android.graphics.ImageFormat
1011
import android.graphics.Point
1112
import android.media.Image
@@ -42,8 +43,9 @@ class BarcodeScanner(val context: Context, val options: BarcodeScannerOptions) :
4243
override fun detect(wrappedImage: IObjectWrapper, metadata: ImageMetadata): List<Barcode> {
4344
if (!loggedOnce) Log.d(TAG, "detect(${ObjectWrapper.unwrap(wrappedImage)}, $metadata)").also { loggedOnce = true }
4445
return when (metadata.format) {
45-
ImageFormat.NV21 -> wrappedImage.unwrap<ByteBuffer>()?.let { helper.decodeFromLuminanceBytes(it, metadata.width, metadata.height) }
46-
ImageFormat.YUV_420_888 -> if (SDK_INT >= 19) wrappedImage.unwrap<Image>()?.let { image -> helper.decodeFromImage(image) } else null
46+
-1 -> wrappedImage.unwrap<Bitmap>()?.let { helper.decodeFromBitmap(it) }
47+
ImageFormat.NV21 -> wrappedImage.unwrap<ByteBuffer>()?.let { helper.decodeFromLuminanceBytes(it, metadata.width, metadata.height, metadata.rotation) }
48+
ImageFormat.YUV_420_888 -> if (SDK_INT >= 19) wrappedImage.unwrap<Image>()?.let { image -> helper.decodeFromImage(image, metadata.rotation) } else null
4749

4850
else -> null
4951
}?.map { it.toMlKit(metadata) } ?: emptyList()
@@ -230,12 +232,7 @@ private fun Result.toMlKit(metadata: ImageMetadata): Barcode {
230232
barcode.rawBytes = rawBytes
231233
barcode.rawValue = text
232234
barcode.cornerPoints = resultPoints.map {
233-
when (metadata.rotation) {
234-
1 -> Point(metadata.height - it.y.toInt(), it.x.toInt())
235-
2 -> Point(metadata.width - it.x.toInt(), metadata.height - it.y.toInt())
236-
3 -> Point(it.y.toInt(), metadata.width - it.x.toInt())
237-
else -> Point(it.x.toInt(), it.y.toInt())
238-
}
235+
Point(it.x.toInt(), it.y.toInt())
239236
}.toTypedArray()
240237

241238
val parsed = ResultParser.parseResult(this)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package org.microg.gms.vision.barcode
2+
3+
import android.util.Log
4+
import android.view.Surface
5+
6+
class RawBarcodeData(var bytes: ByteArray, var width: Int, var height: Int) {
7+
8+
fun rotateDetail(rotate: Int){
9+
when (rotate) {
10+
Surface.ROTATION_90 -> rotateDegree90()
11+
Surface.ROTATION_180 -> rotateDegree180()
12+
Surface.ROTATION_270 -> rotateDegree270()
13+
else -> this
14+
}
15+
}
16+
17+
private fun rotateDegree90(){
18+
val rotatedData = ByteArray(bytes.size)
19+
var index = 0
20+
21+
// Rotate Y plane
22+
for (col in 0 until width) {
23+
for (row in height - 1 downTo 0) {
24+
rotatedData[index++] = bytes[row * width + col]
25+
}
26+
}
27+
28+
// Rotate UV planes (UV interleaved)
29+
val uvHeight = height / 2
30+
for (col in 0 until width step 2) {
31+
for (row in uvHeight - 1 downTo 0) {
32+
rotatedData[index++] = bytes[width * height + row * width + col]
33+
rotatedData[index++] = bytes[width * height + row * width + col + 1]
34+
}
35+
}
36+
bytes = rotatedData
37+
val temp = width
38+
width = height
39+
height = temp
40+
}
41+
42+
private fun rotateDegree180() {
43+
val rotatedData = ByteArray(bytes.size)
44+
var index = 0
45+
46+
// Rotate Y plane
47+
for (row in height - 1 downTo 0) {
48+
for (col in width - 1 downTo 0) {
49+
rotatedData[index++] = bytes[row * width + col]
50+
}
51+
}
52+
53+
// Rotate UV planes (UV interleaved)
54+
val uvHeight = height / 2
55+
val uvWidth = width / 2
56+
for (row in uvHeight - 1 downTo 0) {
57+
for (col in uvWidth - 1 downTo 0) {
58+
val offset = width * height + row * width + col * 2
59+
rotatedData[index++] = bytes[offset]
60+
rotatedData[index++] = bytes[offset + 1]
61+
}
62+
}
63+
bytes = rotatedData
64+
}
65+
66+
67+
private fun rotateDegree270(){
68+
val rotatedData = ByteArray(bytes.size)
69+
var index = 0
70+
71+
// Rotate Y plane
72+
for (col in width - 1 downTo 0) {
73+
for (row in 0 until height) {
74+
rotatedData[index++] = bytes[row * width + col]
75+
}
76+
}
77+
78+
// Rotate UV planes (UV interleaved)
79+
val uvHeight = height / 2
80+
for (col in width - 1 downTo 0 step 2) {
81+
for (row in 0 until uvHeight) {
82+
rotatedData[index++] = bytes[width * height + row * width + col - 1]
83+
rotatedData[index++] = bytes[width * height + row * width + col]
84+
}
85+
}
86+
bytes = rotatedData
87+
val temp = width
88+
width = height
89+
height = temp
90+
}
91+
92+
override fun toString(): String {
93+
return "RawBarcodeData(bytes=${bytes.size}, width=$width, height=$height)"
94+
}
95+
}

0 commit comments

Comments
 (0)