Move SSD detector component scan to typed Kotlin
ober
a31117b5befa7379611914202b91661327315e26
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -2757,6 +2757,7 @@ (typed-kotlin-file "com/sfb/ssdreview/VisionArrays.kt" (typed-library (com sfb ssdreview) (export grayAt edgeAt edgeMask sideCoverage darkSideCoverage contourCell toneCell + detectColorComponents detectToneComponents detectEdgeComponents detectorComponentCells detectorToneCells detectorContourCells shrinkMaskBounds) (type Int32) @@ -2852,6 +2853,161 @@ (>= y height)))) #f (boolean-array-ref edge (+ (* y width) x)))) + (def (toneMaskAt (gray : IntArray) (index : Int32)) : Bool + (let ((value (int-array-ref gray index))) + (and (>= value (int32 70)) + (<= value (int32 245))))) + (def (componentMaskAt (kind : Int32) + (pixels : IntArray) + (gray : IntArray) + (edge : BooleanArray) + (index : Int32)) : Bool + (if (= kind (int32 0)) + (isColorMask (int-array-ref pixels index)) + (if (= kind (int32 1)) + (toneMaskAt gray index) + (boolean-array-ref edge index)))) + (def (markComponentNeighbor! (visited : BooleanArray) + (queue : (MutableList Int32)) + (kind : Int32) + (pixels : IntArray) + (gray : IntArray) + (edge : BooleanArray) + (width : Int32) + (height : Int32) + (nx : Int32) + (ny : Int32)) : Unit + (if (or (or (< nx (int32 0)) + (< ny (int32 0))) + (or (>= nx width) + (>= ny height))) + (begin) + (let ((nidx (+ (* ny width) nx))) + (if (and (not (boolean-array-ref visited nidx)) + (componentMaskAt kind pixels gray edge nidx)) + (begin + (boolean-array-set! visited nidx #t) + (mutable-list-add! queue nidx)) + (begin))))) + (def (scanComponentNeighbors! (visited : BooleanArray) + (queue : (MutableList Int32)) + (kind : Int32) + (pixels : IntArray) + (gray : IntArray) + (edge : BooleanArray) + (width : Int32) + (height : Int32) + (idx : Int32)) : Unit + (let ((cx (mod idx width)) + (cy (/ idx width))) + (begin + (markComponentNeighbor! visited queue kind pixels gray edge width height (- cx (int32 1)) (- cy (int32 1))) + (markComponentNeighbor! visited queue kind pixels gray edge width height cx (- cy (int32 1))) + (markComponentNeighbor! visited queue kind pixels gray edge width height (+ cx (int32 1)) (- cy (int32 1))) + (markComponentNeighbor! visited queue kind pixels gray edge width height (- cx (int32 1)) cy) + (markComponentNeighbor! visited queue kind pixels gray edge width height cx cy) + (markComponentNeighbor! visited queue kind pixels gray edge width height (+ cx (int32 1)) cy) + (markComponentNeighbor! visited queue kind pixels gray edge width height (- cx (int32 1)) (+ cy (int32 1))) + (markComponentNeighbor! visited queue kind pixels gray edge width height cx (+ cy (int32 1))) + (markComponentNeighbor! visited queue kind pixels gray edge width height (+ cx (int32 1)) (+ cy (int32 1)))))) + (def (scanComponentPixel! (component : Component) + (visited : BooleanArray) + (queue : (MutableList Int32)) + (kind : Int32) + (pixels : IntArray) + (gray : IntArray) + (edge : BooleanArray) + (width : Int32) + (height : Int32) + (idx : Int32)) : Unit + (let ((cx (mod idx width)) + (cy (/ idx width))) + (begin + (componentAdd component cx cy) + (scanComponentNeighbors! visited queue kind pixels gray edge width height idx)))) + (def (detectMaskedComponents (width : Int32) + (height : Int32) + (pixels : IntArray) + (gray : IntArray) + (edge : BooleanArray) + (detector : String) + (kind : Int32)) : (MutableList Component) + (let ((visited + (boolean-array-build + (* width height) + (index #f))) + (components (mutable-list-empty Component))) + (begin + (for/fold ((ignored (int32 0))) + ((start (in-range (int32 0) (* width height)))) + (if (or (boolean-array-ref visited start) + (not (componentMaskAt kind pixels gray edge start))) + (begin + (boolean-array-set! visited start #t) + ignored) + (let ((queue (mutable-list-empty Int32)) + (x (mod start width)) + (y (/ start width))) + (let ((component (make-Component x y x y (int32 0) detector))) + (begin + (boolean-array-set! visited start #t) + (mutable-list-add! queue start) + (for/fold ((cursor (int32 0))) + ((pass (in-range (int32 0) (* width height)))) + (if (< cursor (list-size queue)) + (begin + (scanComponentPixel! + component + visited + queue + kind + pixels + gray + edge + width + height + (list-ref queue cursor)) + (+ cursor (int32 1))) + cursor)) + (mutable-list-add! components component) + ignored))))) + components))) + (def (detectColorComponents (width : Int32) + (height : Int32) + (pixels : IntArray) + (detector : String)) : (MutableList Component) + (detectMaskedComponents + width + height + pixels + pixels + (boolean-array-build (int32 0) (index #f)) + detector + (int32 0))) + (def (detectToneComponents (width : Int32) + (height : Int32) + (gray : IntArray) + (detector : String)) : (MutableList Component) + (detectMaskedComponents + width + height + gray + gray + (boolean-array-build (int32 0) (index #f)) + detector + (int32 1))) + (def (detectEdgeComponents (width : Int32) + (height : Int32) + (edge : BooleanArray) + (detector : String)) : (MutableList Component) + (detectMaskedComponents + width + height + (int-array-build (int32 0) (index (int32 0))) + (int-array-build (int32 0) (index (int32 0))) + edge + detector + (int32 2))) (def (sobelPixel (gray : IntArray) (width : Int32) (x : Int32) @@ -5839,10 +5995,6 @@ "package com.sfb.ssdreview" "" "import android.graphics.Bitmap" - "import java.util.ArrayDeque" - "import kotlin.math.max" - "import kotlin.math.min" - "" "object SsdDetector {" " fun detectCells(bitmap: Bitmap): MutableList<SsdCell> {" " val width = bitmap.width" @@ -5853,16 +6005,12 @@ " val cells = mutableListOf<SsdCell>()" "" " cells += detectorComponentCells(" - " detectMask(width, height, pixels, \"android-color\") { index ->" - " isColorMask(pixels[index])" - " }," + " detectColorComponents(width, height, pixels, \"android-color\")," " \"android-color\"" " )" "" " cells += detectorToneCells(" - " detectMask(width, height, pixels, \"android-tone\") { index ->" - " gray[index] in 70..245" - " }," + " detectToneComponents(width, height, gray, \"android-tone\")," " gray," " width," " height" @@ -5911,52 +6059,10 @@ " return shrinkMaskBounds(rows, cols, x1, y1, bitmap.width, bitmap.height)" " }" "" - " private fun detectMask(" - " width: Int," - " height: Int," - " pixels: IntArray," - " detector: String," - " masked: (Int) -> Boolean" - " ): List<Component> {" - " val visited = BooleanArray(width * height)" - " val components = mutableListOf<Component>()" - " val queue = ArrayDeque<Int>()" - " for (y in 0 until height) {" - " for (x in 0 until width) {" - " val start = y * width + x" - " if (visited[start] || !masked(start)) {" - " visited[start] = true" - " continue" - " }" - " queue.clear()" - " queue.add(start)" - " visited[start] = true" - " val acc = Component(x, y, x, y, 0, detector)" - " while (!queue.isEmpty()) {" - " val idx = queue.removeFirst()" - " val cx = idx % width" - " val cy = idx / width" - " componentAdd(acc, cx, cy)" - " for (ny in max(0, cy - 1)..min(height - 1, cy + 1)) {" - " for (nx in max(0, cx - 1)..min(width - 1, cx + 1)) {" - " val nidx = ny * width + nx" - " if (!visited[nidx] && masked(nidx)) {" - " visited[nidx] = true" - " queue.add(nidx)" - " }" - " }" - " }" - " }" - " components.add(acc)" - " }" - " }" - " return components" - " }" - "" " private fun contourCells(width: Int, height: Int, gray: IntArray): List<SsdCell> {" " val edge = edgeMask(gray, width, height)" " return detectorContourCells(" - " detectMask(width, height, IntArray(width * height), \"android-contour\") { edge[it] }," + " detectEdgeComponents(width, height, edge, \"android-contour\")," " edge," " width," " height"