Skip to content

Commit 949ca3c

Browse files
authored
Capture: fallback to last successful live analysis for quad detection (#12)
1 parent d8697b5 commit 949ca3c

File tree

5 files changed

+77
-5
lines changed

5 files changed

+77
-5
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fun detectDocumentQuad(mask: Bitmap, minQuadAreaRatio: Double = 0.02): Quad? {
6868
}
6969

7070
val vertices = biggest?.toList()?.map { Point(it.x.toInt(), it.y.toInt()) }
71-
return createQuad(vertices)
71+
return if (vertices?.size == 4) createQuad(vertices) else null
7272
}
7373

7474
/**

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,28 @@ data class Quad(
4444
Line(bottomRight, bottomLeft),
4545
Line(bottomLeft, topLeft))
4646
}
47+
48+
fun rotate90(iterations: Int, imageWidth: Int, imageHeight: Int): Quad {
49+
val rotatedPoints = listOf(
50+
rotate90(topLeft, imageWidth, imageHeight, iterations),
51+
rotate90(topRight, imageWidth, imageHeight, iterations),
52+
rotate90(bottomRight, imageWidth, imageHeight, iterations),
53+
rotate90(bottomLeft, imageWidth, imageHeight, iterations)
54+
)
55+
return createQuad(rotatedPoints)
56+
}
57+
private fun rotate90(p: Point, width: Int, height: Int, iterations: Int): Point {
58+
return when (iterations % 4) {
59+
1 -> Point(height - p.y, p.x) // 90°
60+
2 -> Point(width - p.x, height - p.y) // 180°
61+
3 -> Point(p.y, width - p.x) // 270°
62+
else -> p //
63+
}
64+
}
4765
}
4866

49-
fun createQuad(vertices: List<Point>?): Quad? {
50-
if (vertices == null || vertices.size != 4) return null
67+
fun createQuad(vertices: List<Point>): Quad {
68+
require(vertices.size == 4)
5169

5270
// Centroid of the points
5371
val cx = vertices.map { it.x }.average()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ data class LiveAnalysisState(
2222
val inferenceTime: Long = 0L,
2323
val binaryMask: Bitmap? = null,
2424
val documentQuad: Quad? = null,
25+
val timestamp: Long = System.currentTimeMillis(),
2526
)

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class MainViewModel(
6565

6666
private var _liveAnalysisState = MutableStateFlow(LiveAnalysisState())
6767
val liveAnalysisState: StateFlow<LiveAnalysisState> = _liveAnalysisState.asStateFlow()
68+
private var lastSuccessfulLiveAnalysisState: LiveAnalysisState? = null
6869

6970
private val _screenStack = MutableStateFlow<List<Screen>>(listOf(Screen.Camera))
7071
val currentScreen: StateFlow<Screen> = _screenStack.map { it.last() }
@@ -86,11 +87,15 @@ class MainViewModel(
8687
LiveAnalysisState(
8788
inferenceTime = it.inferenceTime,
8889
binaryMask = binaryMask,
89-
documentQuad = detectDocumentQuad(binaryMask)
90+
documentQuad = detectDocumentQuad(binaryMask),
91+
timestamp = System.currentTimeMillis(),
9092
)
9193
}
9294
.collect {
9395
_liveAnalysisState.value = it
96+
if (it.documentQuad != null) {
97+
lastSuccessfulLiveAnalysisState = it
98+
}
9499
}
95100
}
96101
}
@@ -164,7 +169,22 @@ class MainViewModel(
164169
val segmentation = imageSegmentationService.runSegmentationAndReturn(bitmap, 0)
165170
if (segmentation != null) {
166171
val mask = segmentation.segmentation.toBinaryMask()
167-
val quad = detectDocumentQuad(mask)
172+
var quad = detectDocumentQuad(mask)
173+
if (quad == null) {
174+
val now = System.currentTimeMillis()
175+
lastSuccessfulLiveAnalysisState?.timestamp?.let {
176+
val offset = now - it
177+
Log.i("Quad", "Last successful live analysis was $offset ms ago")
178+
}
179+
val recentLive = lastSuccessfulLiveAnalysisState?.takeIf {
180+
now - it.timestamp <= 1500
181+
}
182+
val rotations = (-imageProxy.imageInfo.rotationDegrees / 90) + 4
183+
quad = recentLive?.documentQuad?.rotate90(rotations, mask.width, mask.height)
184+
if (quad != null) {
185+
Log.i("Quad", "Using quad taken in live analysis; rotations=$rotations")
186+
}
187+
}
168188
if (quad != null) {
169189
val resizedQuad = quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
170190
corrected = extractDocument(bitmap, resizedQuad, imageProxy.imageInfo.rotationDegrees)

app/src/test/java/org/mydomain/myscan/GeometryTest.kt

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

1717
import org.assertj.core.api.Assertions.assertThat
18+
import org.assertj.core.api.Assertions.assertThatThrownBy
1819
import org.junit.Test
1920

2021
class GeometryTest {
@@ -24,4 +25,36 @@ class GeometryTest {
2425
assertThat(Line(Point(0, 0), Point(10, 0)).norm()).isEqualTo(10.0)
2526
assertThat(Line(Point(1, 2), Point(4, 6)).norm()).isEqualTo(5.0)
2627
}
28+
29+
@Test
30+
fun createQuad() {
31+
val quad = createQuad(listOf(
32+
Point(3, 9), Point(1,2), Point(11,12), Point(10, 3)))
33+
assertThat(quad).isEqualTo(
34+
Quad(Point(1,2), Point(10, 3), Point(11,12), Point(3, 9)))
35+
assertThatThrownBy { createQuad(listOf()) }
36+
.isInstanceOf(IllegalArgumentException::class.java)
37+
}
38+
39+
@Test
40+
fun rotateQuad() {
41+
val quad = createQuad(listOf(
42+
Point(1,2), Point(10, 3), Point(11,12), Point(3, 9)))
43+
assertThat(quad.rotate90(1, 100, 50)).isEqualTo(
44+
createQuad(listOf(
45+
Point(48,1), Point(47, 10), Point(38,11), Point(41, 3)
46+
)))
47+
assertThat(quad.rotate90(2, 100, 50)).isEqualTo(
48+
createQuad(listOf(
49+
Point(99,48), Point(90, 47), Point(89,38), Point(97, 41)
50+
)))
51+
assertThat(quad.rotate90(3, 100, 50)).isEqualTo(
52+
createQuad(listOf(
53+
Point(2,99), Point(3, 90), Point(12,89), Point(9, 97)
54+
)))
55+
assertThat(quad.rotate90(4, 100, 50)).isEqualTo(quad)
56+
assertThat(quad.rotate90(5, 100, 50)).isEqualTo(
57+
quad.rotate90(1, 100, 50)
58+
)
59+
}
2760
}

0 commit comments

Comments
 (0)