Move SSD OCR nearby text to typed Kotlin
ober
dbe9f642567cf4cdeae95bd9752b896049866553
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -1354,7 +1354,8 @@ (typed-kotlin-file "com/sfb/ssdreview/OcrGeometry.kt" (typed-library (com sfb ssdreview) - (export centerInside rectArea normalizeRect rectsOverlap distanceToRect) + (export floatArrayGetOrZero + centerInside rectArea normalizeRect rectsOverlap distanceToRect) (type Float32) (type FloatArray) (type Int32) @@ -1429,6 +1430,179 @@ (float32 0.0))))) (+ dx dy)))))) + (typed-kotlin-file "com/sfb/ssdreview/NearbyText.kt" + (typed-library (com sfb ssdreview) + (export nearbyText) + (type Float32) + (type FloatArray) + (type Int32) + (type OcrWord) + (record NearbyBest + ((mut firstWord : (Nullable OcrWord)) + (mut firstDistance : Float32) + (mut secondWord : (Nullable OcrWord)) + (mut secondDistance : Float32) + (mut thirdWord : (Nullable OcrWord)) + (mut thirdDistance : Float32) + (mut fourthWord : (Nullable OcrWord)) + (mut fourthDistance : Float32))) + (def (nearbyMaxFloat32 (a : Float32) (b : Float32)) : Float32 + (if (> a b) a b)) + (def (nearbyMinFloat32 (a : Float32) (b : Float32)) : Float32 + (if (< a b) a b)) + (def (nearbyAppend (text : String) (piece : String)) : String + (if (string-blank? text) + piece + (string-append text (string-append " " piece)))) + (def (nearbyWordMatches? (word : OcrWord) (rect : FloatArray)) : Bool + (or (rectsOverlap (OcrWord-bbox word) rect) + (centerInside (OcrWord-bbox word) rect))) + (def (nearbyDirectCount (words : (List OcrWord)) + (rect : FloatArray)) : Int32 + (for/fold ((count (int32 0))) + ((i (in-range (int32 0) (list-size words)))) + (if (nearbyWordMatches? (list-ref words i) rect) + (+ count (int32 1)) + count))) + (def (nearbyDirectText (words : (List OcrWord)) + (rect : FloatArray)) : String + (string-take + (for/fold ((text "")) + ((i (in-range (int32 0) (list-size words)))) + (let ((word (list-ref words i))) + (if (nearbyWordMatches? word rect) + (nearbyAppend text (OcrWord-text word)) + text))) + (int32 240))) + (def (nearbyEmptyBest) : NearbyBest + (make-NearbyBest + (nullable-none OcrWord) (float32 0.0) + (nullable-none OcrWord) (float32 0.0) + (nullable-none OcrWord) (float32 0.0) + (nullable-none OcrWord) (float32 0.0))) + (def (nearbySetFourth (best : NearbyBest) + (word : (Nullable OcrWord)) + (distance : Float32)) : Unit + (begin + (NearbyBest-fourthWord-set! best word) + (NearbyBest-fourthDistance-set! best distance))) + (def (nearbySetThird (best : NearbyBest) + (word : (Nullable OcrWord)) + (distance : Float32)) : Unit + (begin + (NearbyBest-thirdWord-set! best word) + (NearbyBest-thirdDistance-set! best distance))) + (def (nearbySetSecond (best : NearbyBest) + (word : (Nullable OcrWord)) + (distance : Float32)) : Unit + (begin + (NearbyBest-secondWord-set! best word) + (NearbyBest-secondDistance-set! best distance))) + (def (nearbySetFirst (best : NearbyBest) + (word : (Nullable OcrWord)) + (distance : Float32)) : Unit + (begin + (NearbyBest-firstWord-set! best word) + (NearbyBest-firstDistance-set! best distance))) + (def (nearbyInsertFirst (best : NearbyBest) + (word : OcrWord) + (distance : Float32)) : Unit + (begin + (nearbySetFourth best (NearbyBest-thirdWord best) (NearbyBest-thirdDistance best)) + (nearbySetThird best (NearbyBest-secondWord best) (NearbyBest-secondDistance best)) + (nearbySetSecond best (NearbyBest-firstWord best) (NearbyBest-firstDistance best)) + (nearbySetFirst best (nullable-some word) distance))) + (def (nearbyInsertSecond (best : NearbyBest) + (word : OcrWord) + (distance : Float32)) : Unit + (begin + (nearbySetFourth best (NearbyBest-thirdWord best) (NearbyBest-thirdDistance best)) + (nearbySetThird best (NearbyBest-secondWord best) (NearbyBest-secondDistance best)) + (nearbySetSecond best (nullable-some word) distance))) + (def (nearbyInsertThird (best : NearbyBest) + (word : OcrWord) + (distance : Float32)) : Unit + (begin + (nearbySetFourth best (NearbyBest-thirdWord best) (NearbyBest-thirdDistance best)) + (nearbySetThird best (nullable-some word) distance))) + (def (nearbyInsertFourth (best : NearbyBest) + (word : OcrWord) + (distance : Float32)) : Unit + (nearbySetFourth best (nullable-some word) distance)) + (def (nearbyMaybeInsert (best : NearbyBest) + (word : OcrWord) + (distance : Float32)) : Unit + (if (or (nullable-null? (NearbyBest-firstWord best)) + (< distance (NearbyBest-firstDistance best))) + (nearbyInsertFirst best word distance) + (if (or (nullable-null? (NearbyBest-secondWord best)) + (< distance (NearbyBest-secondDistance best))) + (nearbyInsertSecond best word distance) + (if (or (nullable-null? (NearbyBest-thirdWord best)) + (< distance (NearbyBest-thirdDistance best))) + (nearbyInsertThird best word distance) + (if (or (nullable-null? (NearbyBest-fourthWord best)) + (< distance (NearbyBest-fourthDistance best))) + (nearbyInsertFourth best word distance) + (begin)))))) + (def (nearbyCollectBest (words : (List OcrWord)) + (cx : Float32) + (cy : Float32) + (limit : Float32)) : NearbyBest + (let ((best (nearbyEmptyBest))) + (begin + (for/fold ((ignored (int32 0))) + ((i (in-range (int32 0) (list-size words)))) + (let ((word (list-ref words i))) + (let ((distance (distanceToRect cx cy (OcrWord-bbox word)))) + (begin + (if (<= distance limit) + (nearbyMaybeInsert best word distance) + (begin)) + ignored)))) + best))) + (def (nearbyAppendWord (text : String) + (word : (Nullable OcrWord))) : String + (if (nullable-null? word) + text + (nearbyAppend text (OcrWord-text (nullable-get word))))) + (def (nearbyBestText (best : NearbyBest)) : String + (string-take + (nearbyAppendWord + (nearbyAppendWord + (nearbyAppendWord + (nearbyAppendWord "" (NearbyBest-firstWord best)) + (NearbyBest-secondWord best)) + (NearbyBest-thirdWord best)) + (NearbyBest-fourthWord best)) + (int32 240))) + (def (nearbyText (words : (List OcrWord)) + (bbox : FloatArray) + (imageWidth : Int32) + (imageHeight : Int32)) : String + (let ((x1 (floatArrayGetOrZero bbox (int32 0))) + (y1 (floatArrayGetOrZero bbox (int32 1))) + (x2 (floatArrayGetOrZero bbox (int32 2))) + (y2 (floatArrayGetOrZero bbox (int32 3)))) + (let ((padX (nearbyMaxFloat32 (float32 90.0) (* (- x2 x1) (float32 3.0)))) + (padY (nearbyMaxFloat32 (float32 80.0) (* (- y2 y1) (float32 2.2))))) + (let ((rect + (float-array + (nearbyMaxFloat32 (float32 0.0) (- x1 padX)) + (nearbyMaxFloat32 (float32 0.0) (- y1 padY)) + (nearbyMinFloat32 (float32 imageWidth) (+ x2 padX)) + (nearbyMinFloat32 (float32 imageHeight) (+ y2 padY))))) + (if (> (nearbyDirectCount words rect) (int32 0)) + (nearbyDirectText words rect) + (let ((cx (/ (+ x1 x2) (float32 2.0))) + (cy (/ (+ y1 y2) (float32 2.0)))) + (nearbyBestText + (nearbyCollectBest + words + cx + cy + (* (nearbyMaxFloat32 padX padY) (float32 1.5)))))))))))) + (typed-kotlin-file "com/sfb/ssdreview/VisionPixels.kt" (typed-library (com sfb ssdreview) (export grayscale pixelsToRgba isColorMask looksLikeInk looksLikeBoxFill) @@ -4290,8 +4464,6 @@ "import com.google.mlkit.vision.common.InputImage" "import com.google.mlkit.vision.text.TextRecognition" "import com.google.mlkit.vision.text.latin.TextRecognizerOptions" - "import kotlin.math.max" - "import kotlin.math.min" "" "object SsdOcr {" " private val patterns = listOf(" @@ -4374,24 +4546,6 @@ " return applied" " }" "" - " private fun nearbyText(words: List<OcrWord>, bbox: FloatArray, imageWidth: Int, imageHeight: Int): String {" - " val x1 = bbox.getOrElse(0) { 0f }" - " val y1 = bbox.getOrElse(1) { 0f }" - " val x2 = bbox.getOrElse(2) { 0f }" - " val y2 = bbox.getOrElse(3) { 0f }" - " val padX = max(45f, (x2 - x1) * 1.8f)" - " val padY = max(35f, (y2 - y1) * 1.2f)" - " val rect = floatArrayOf(" - " max(0f, x1 - padX)," - " max(0f, y1 - padY)," - " min(imageWidth.toFloat(), x2 + padX)," - " min(imageHeight.toFloat(), y2 + padY)" - " )" - " return words.filter { centerInside(it.bbox, rect) }" - " .joinToString(\" \") { it.text }" - " .take(240)" - " }" - "" " private fun ocrCandidates(context: Context, text: String, race: String): List<OcrCandidate> {" " if (text.isBlank()) return emptyList()" " val seen = mutableSetOf<String>()"