Move SSD detector initial grouping to typed Kotlin
ober
15d309bf9f38311673bd10e85e844fb479d09b47
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -448,7 +448,8 @@ ssdSessionFindCellById ssdSessionRecomputeGroup ssdSessionSetSsdArea ssdSessionPruneGroupsOutsideSsdArea createManualCells - ssdSessionNextCellId ssdSessionNextGroupId) + ssdSessionNextCellId ssdSessionNextGroupId + bboxForMutableCells) (type Int32) (type FloatArray) (type SsdCell) @@ -702,6 +703,112 @@ (int32 4) #\0)))))) + (typed-kotlin-file "com/sfb/ssdreview/SsdDetectorGroups.kt" + (typed-library (com sfb ssdreview) + (export initialGroups) + (type Int32) + (type BooleanArray) + (def (detectorGroupId (index : Int32)) : String + (string-append "g" + (string-pad-start (int32->string (+ index (int32 1))) (int32 4) #\0))) + (def (markAdjacentCells! (cells : (MutableList SsdCell)) + (seen : BooleanArray) + (component : BooleanArray) + (queue : (MutableList Int32)) + (current : Int32)) : Unit + (begin + (for/fold ((ignored (int32 0))) + ((j (in-range (int32 0) (list-size cells)))) + (if (and (not (boolean-array-ref seen j)) + (adjacent (list-ref cells current) (list-ref cells j))) + (begin + (boolean-array-set! seen j #t) + (boolean-array-set! component j #t) + (mutable-list-add! queue j) + ignored) + ignored)) + (begin))) + (def (growInitialGroupComponent (cells : (MutableList SsdCell)) + (seen : BooleanArray) + (start : Int32)) : BooleanArray + (let ((component + (boolean-array-build + (list-size cells) + (index #f))) + (queue (mutable-list-empty Int32))) + (begin + (boolean-array-set! seen start #t) + (boolean-array-set! component start #t) + (mutable-list-add! queue start) + (for/fold ((cursor (int32 0))) + ((pass (in-range (int32 0) (list-size cells)))) + (if (< cursor (list-size queue)) + (begin + (markAdjacentCells! + cells + seen + component + queue + (list-ref queue cursor)) + (+ cursor (int32 1))) + cursor)) + component))) + (def (componentCellIds (cells : (MutableList SsdCell)) + (component : BooleanArray)) : (MutableList String) + (for/fold ((ids (mutable-list-empty String))) + ((i (in-range (int32 0) (list-size cells)))) + (if (boolean-array-ref component i) + (begin + (mutable-list-add! ids (SsdCell-id (list-ref cells i))) + ids) + ids))) + (def (componentCells (cells : (MutableList SsdCell)) + (component : BooleanArray)) : (MutableList SsdCell) + (for/fold ((out (mutable-list-empty SsdCell))) + ((i (in-range (int32 0) (list-size cells)))) + (if (boolean-array-ref component i) + (begin + (mutable-list-add! out (list-ref cells i)) + out) + out))) + (def (addInitialGroupFromComponent! (groups : (MutableList SsdGroup)) + (cells : (MutableList SsdCell)) + (component : BooleanArray)) : Unit + (let ((ids (componentCellIds cells component))) + (if (> (list-size ids) (int32 1)) + (let ((groupCells (componentCells cells component))) + (mutable-list-add! + groups + (make-SsdGroup + (detectorGroupId (list-size groups)) + "" + "" + "candidate" + (list-size groupCells) + (bboxForMutableCells groupCells) + ids + "" + ""))) + (begin)))) + (def (initialGroups (cells : (MutableList SsdCell))) : (MutableList SsdGroup) + (let ((groups (mutable-list-empty SsdGroup)) + (seen + (boolean-array-build + (list-size cells) + (index #f)))) + (begin + (for/fold ((ignored (int32 0))) + ((i (in-range (int32 0) (list-size cells)))) + (if (boolean-array-ref seen i) + ignored + (begin + (addInitialGroupFromComponent! + groups + cells + (growInitialGroupComponent cells seen i)) + ignored))) + groups))))) + (typed-kotlin-file "com/sfb/ssdreview/SourceKey.kt" (typed-library (com sfb ssdreview) (export sourceKey) @@ -3788,7 +3895,7 @@ " detectorSource = if (forceDetect && existingTruthGroupCount > 0) \"forced-android-plus-truth\" else \"android-fallback\"" " }" " session.cells.addAll(detectedCells.filter { ssdSessionCellInsideSsdArea(session, it) })" - " session.groups.addAll(SsdDetector.initialGroups(session.cells).take(120))" + " session.groups.addAll(initialGroups(session.cells).take(120))" " if (existingTruth != null && existingTruthGroupCount > 0) {" " truthStore.applyTruth(session)" " ssdSessionPruneGroupsOutsideSsdArea(session)" @@ -4794,61 +4901,6 @@ " return merged" " }" "" - " fun initialGroups(cells: List<SsdCell>): MutableList<SsdGroup> {" - " val edges = List(cells.size) { mutableListOf<Int>() }" - " for (i in cells.indices) {" - " for (j in i + 1 until cells.size) {" - " if (adjacent(cells[i], cells[j])) {" - " edges[i].add(j)" - " edges[j].add(i)" - " }" - " }" - " }" - " val seen = BooleanArray(cells.size)" - " val components = mutableListOf<List<Int>>()" - " for (i in cells.indices) {" - " if (seen[i]) continue" - " val stack = ArrayDeque<Int>()" - " val component = mutableListOf<Int>()" - " stack.add(i)" - " seen[i] = true" - " while (!stack.isEmpty()) {" - " val index = stack.removeLast()" - " component.add(index)" - " for (next in edges[index]) {" - " if (!seen[next]) {" - " seen[next] = true" - " stack.add(next)" - " }" - " }" - " }" - " if (component.size > 1) {" - " components.add(component)" - " }" - " }" - " return components" - " .sortedWith(compareBy<List<Int>> { component -> component.minOf { cells[it].y } }" - " .thenBy { component -> component.minOf { cells[it].x } })" - " .mapIndexed { index, component ->" - " val ids = component.sortedWith(compareBy<Int> { cells[it].y }.thenBy { cells[it].x })" - " .map { cells[it].id }" - " .toMutableList()" - " val groupCells = ids.mapNotNull { id -> cells.firstOrNull { it.id == id } }" - " SsdGroup(" - " id = \"g%04d\".format(index + 1)," - " label = \"\"," - " boxTypeId = \"\"," - " status = \"candidate\"," - " count = groupCells.size," - " bbox = bboxForCells(groupCells)," - " cellIds = ids," - " firingArc = \"\"," - " notes = \"\"" - " )" - " }" - " .toMutableList()" - " }" - "" " fun shrinkRectToInk(bitmap: Bitmap, rect: FloatArray): FloatArray? {" " return shrinkRectByMask(bitmap, rect) { pixel -> looksLikeInk(pixel) }" " }"