Skip to content

Commit 7056393

Browse files
committed
Save about 400ms at capture by delegating the rotation to OpenCV
1 parent 453a8b3 commit 7056393

File tree

4 files changed

+23
-15
lines changed

4 files changed

+23
-15
lines changed

app/src/androidTest/java/org/mydomain/myscan/DocumentDetectionTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class DocumentDetectionTest {
5757
if (quad != null) {
5858
val resizedQuad =
5959
quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
60-
outputBitmap = extractDocument(bitmap, resizedQuad)
60+
outputBitmap = extractDocument(bitmap, resizedQuad, 0)
6161
val file = File(context.getExternalFilesDir(null), imageFileName)
6262
FileOutputStream(file).use {
6363
outputBitmap.compress(Bitmap.CompressFormat.JPEG, 95, it)

app/src/main/java/org/mydomain/myscan/DocumentDetection.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
package org.mydomain.myscan
1616

1717
import android.graphics.Bitmap
18+
import androidx.core.graphics.createBitmap
1819
import org.opencv.android.Utils
20+
import org.opencv.core.Core
1921
import org.opencv.core.Mat
2022
import org.opencv.core.MatOfPoint
2123
import org.opencv.core.MatOfPoint2f
@@ -24,7 +26,6 @@ import org.opencv.imgproc.Imgproc
2426
import kotlin.math.abs
2527
import kotlin.math.max
2628
import kotlin.math.sqrt
27-
import androidx.core.graphics.createBitmap
2829

2930
fun detectDocumentQuad(mask: Bitmap): Quad? {
3031
val mat = Mat()
@@ -65,7 +66,7 @@ fun detectDocumentQuad(mask: Bitmap): Quad? {
6566
return createQuad(vertices)
6667
}
6768

68-
fun extractDocument(originalBitmap: Bitmap, quad: Quad): Bitmap {
69+
fun extractDocument(originalBitmap: Bitmap, quad: Quad, rotationDegrees: Int): Bitmap {
6970
val widthTop = norm(quad.topLeft, quad.topRight)
7071
val widthBottom = norm(quad.bottomLeft, quad.bottomRight)
7172
val maxWidth = max(widthTop, widthBottom).toInt()
@@ -96,11 +97,23 @@ fun extractDocument(originalBitmap: Bitmap, quad: Quad): Bitmap {
9697

9798
val enhanced = enhanceCapturedImage(outputMat)
9899

99-
return toBitmap(enhanced, maxWidth, maxHeight)
100+
return toBitmap(rotate(enhanced, rotationDegrees))
101+
}
102+
103+
fun rotate(input: Mat, degrees: Int): Mat {
104+
val output = Mat()
105+
when ((degrees % 360 + 360) % 360) {
106+
0 -> input.copyTo(output)
107+
90 -> Core.rotate(input, output, Core.ROTATE_90_CLOCKWISE)
108+
180 -> Core.rotate(input, output, Core.ROTATE_180)
109+
270 -> Core.rotate(input, output, Core.ROTATE_90_COUNTERCLOCKWISE)
110+
else -> throw IllegalArgumentException("Only 0, 90, 180, 270 degrees are supported")
111+
}
112+
return output
100113
}
101114

102-
private fun toBitmap(mat: Mat, width: Int, height: Int): Bitmap {
103-
val outputBitmap = createBitmap(width, height)
115+
private fun toBitmap(mat: Mat): Bitmap {
116+
val outputBitmap = createBitmap(mat.cols(), mat.rows())
104117
Utils.matToBitmap(mat, outputBitmap)
105118
return outputBitmap
106119
}

app/src/main/java/org/mydomain/myscan/MainViewModel.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package org.mydomain.myscan
1717
import android.content.Context
1818
import android.graphics.Bitmap
1919
import android.graphics.BitmapFactory
20-
import android.graphics.Matrix
2120
import androidx.camera.core.ImageProxy
2221
import androidx.lifecycle.ViewModel
2322
import androidx.lifecycle.ViewModelProvider
@@ -109,25 +108,19 @@ class MainViewModel(
109108

110109
private suspend fun processCapturedImage(imageProxy: ImageProxy): Bitmap? = withContext(Dispatchers.IO) {
111110
var corrected: Bitmap? = null
112-
val bitmap = imageProxy.toBitmap().rotate(imageProxy.imageInfo.rotationDegrees)
111+
var bitmap = imageProxy.toBitmap()
113112
val segmentation = imageSegmentationService.runSegmentationAndReturn(bitmap, 0)
114113
if (segmentation != null) {
115114
val mask = segmentation.segmentation.toBinaryMask()
116115
val quad = detectDocumentQuad(mask)
117116
if (quad != null) {
118117
val resizedQuad = quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
119-
corrected = extractDocument(bitmap, resizedQuad)
118+
corrected = extractDocument(bitmap, resizedQuad, imageProxy.imageInfo.rotationDegrees)
120119
}
121120
}
122121
return@withContext corrected
123122
}
124123

125-
fun Bitmap.rotate(degrees: Int): Bitmap {
126-
if (degrees == 0) return this
127-
val matrix = Matrix().apply { postRotate(degrees.toFloat()) }
128-
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
129-
}
130-
131124
fun addPage(bitmap: Bitmap, quality: Int = 75) {
132125
val resized = resizeImage(bitmap)
133126
val outputStream = ByteArrayOutputStream()

app/src/main/java/org/mydomain/myscan/view/Camera.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ fun CameraScreen(
131131
pageCount = viewModel.pageCount(),
132132
liveAnalysisState,
133133
onCapture = {
134+
Log.i("MyScan", "Pressed <Capture>")
134135
viewModel.liveAnalysisEnabled = false
135136
showPageDialog.value = true
136137
isProcessing.value = true
@@ -140,6 +141,7 @@ fun CameraScreen(
140141
viewModel.processCapturedImageThen(imageProxy) {
141142
isProcessing.value = false
142143
viewModel.liveAnalysisEnabled = true
144+
Log.i("MyScan", "Capture process finished")
143145
}
144146
} else {
145147
Log.e("MyScan", "Error during image capture")

0 commit comments

Comments
 (0)