Save about 400ms at capture by delegating the rotation to OpenCV
This commit is contained in:
@@ -57,7 +57,7 @@ class DocumentDetectionTest {
|
||||
if (quad != null) {
|
||||
val resizedQuad =
|
||||
quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
|
||||
outputBitmap = extractDocument(bitmap, resizedQuad)
|
||||
outputBitmap = extractDocument(bitmap, resizedQuad, 0)
|
||||
val file = File(context.getExternalFilesDir(null), imageFileName)
|
||||
FileOutputStream(file).use {
|
||||
outputBitmap.compress(Bitmap.CompressFormat.JPEG, 95, it)
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
package org.mydomain.myscan
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.core.graphics.createBitmap
|
||||
import org.opencv.android.Utils
|
||||
import org.opencv.core.Core
|
||||
import org.opencv.core.Mat
|
||||
import org.opencv.core.MatOfPoint
|
||||
import org.opencv.core.MatOfPoint2f
|
||||
@@ -24,7 +26,6 @@ import org.opencv.imgproc.Imgproc
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sqrt
|
||||
import androidx.core.graphics.createBitmap
|
||||
|
||||
fun detectDocumentQuad(mask: Bitmap): Quad? {
|
||||
val mat = Mat()
|
||||
@@ -65,7 +66,7 @@ fun detectDocumentQuad(mask: Bitmap): Quad? {
|
||||
return createQuad(vertices)
|
||||
}
|
||||
|
||||
fun extractDocument(originalBitmap: Bitmap, quad: Quad): Bitmap {
|
||||
fun extractDocument(originalBitmap: Bitmap, quad: Quad, rotationDegrees: Int): Bitmap {
|
||||
val widthTop = norm(quad.topLeft, quad.topRight)
|
||||
val widthBottom = norm(quad.bottomLeft, quad.bottomRight)
|
||||
val maxWidth = max(widthTop, widthBottom).toInt()
|
||||
@@ -96,11 +97,23 @@ fun extractDocument(originalBitmap: Bitmap, quad: Quad): Bitmap {
|
||||
|
||||
val enhanced = enhanceCapturedImage(outputMat)
|
||||
|
||||
return toBitmap(enhanced, maxWidth, maxHeight)
|
||||
return toBitmap(rotate(enhanced, rotationDegrees))
|
||||
}
|
||||
|
||||
private fun toBitmap(mat: Mat, width: Int, height: Int): Bitmap {
|
||||
val outputBitmap = createBitmap(width, height)
|
||||
fun rotate(input: Mat, degrees: Int): Mat {
|
||||
val output = Mat()
|
||||
when ((degrees % 360 + 360) % 360) {
|
||||
0 -> input.copyTo(output)
|
||||
90 -> Core.rotate(input, output, Core.ROTATE_90_CLOCKWISE)
|
||||
180 -> Core.rotate(input, output, Core.ROTATE_180)
|
||||
270 -> Core.rotate(input, output, Core.ROTATE_90_COUNTERCLOCKWISE)
|
||||
else -> throw IllegalArgumentException("Only 0, 90, 180, 270 degrees are supported")
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
private fun toBitmap(mat: Mat): Bitmap {
|
||||
val outputBitmap = createBitmap(mat.cols(), mat.rows())
|
||||
Utils.matToBitmap(mat, outputBitmap)
|
||||
return outputBitmap
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.mydomain.myscan
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
@@ -109,25 +108,19 @@ class MainViewModel(
|
||||
|
||||
private suspend fun processCapturedImage(imageProxy: ImageProxy): Bitmap? = withContext(Dispatchers.IO) {
|
||||
var corrected: Bitmap? = null
|
||||
val bitmap = imageProxy.toBitmap().rotate(imageProxy.imageInfo.rotationDegrees)
|
||||
var bitmap = imageProxy.toBitmap()
|
||||
val segmentation = imageSegmentationService.runSegmentationAndReturn(bitmap, 0)
|
||||
if (segmentation != null) {
|
||||
val mask = segmentation.segmentation.toBinaryMask()
|
||||
val quad = detectDocumentQuad(mask)
|
||||
if (quad != null) {
|
||||
val resizedQuad = quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
|
||||
corrected = extractDocument(bitmap, resizedQuad)
|
||||
corrected = extractDocument(bitmap, resizedQuad, imageProxy.imageInfo.rotationDegrees)
|
||||
}
|
||||
}
|
||||
return@withContext corrected
|
||||
}
|
||||
|
||||
fun Bitmap.rotate(degrees: Int): Bitmap {
|
||||
if (degrees == 0) return this
|
||||
val matrix = Matrix().apply { postRotate(degrees.toFloat()) }
|
||||
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
|
||||
}
|
||||
|
||||
fun addPage(bitmap: Bitmap, quality: Int = 75) {
|
||||
val resized = resizeImage(bitmap)
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
|
||||
@@ -131,6 +131,7 @@ fun CameraScreen(
|
||||
pageCount = viewModel.pageCount(),
|
||||
liveAnalysisState,
|
||||
onCapture = {
|
||||
Log.i("MyScan", "Pressed <Capture>")
|
||||
viewModel.liveAnalysisEnabled = false
|
||||
showPageDialog.value = true
|
||||
isProcessing.value = true
|
||||
@@ -140,6 +141,7 @@ fun CameraScreen(
|
||||
viewModel.processCapturedImageThen(imageProxy) {
|
||||
isProcessing.value = false
|
||||
viewModel.liveAnalysisEnabled = true
|
||||
Log.i("MyScan", "Capture process finished")
|
||||
}
|
||||
} else {
|
||||
Log.e("MyScan", "Error during image capture")
|
||||
|
||||
Reference in New Issue
Block a user