Move SSD learned feature JSON to typed Kotlin
ober
dcf24991955881a0dbad86a42fa563edaca1ce40
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -1061,6 +1061,72 @@ "height" (int32 1)))))) + (typed-kotlin-file "com/sfb/ssdreview/LearnedFeatures.kt" + (kotlin-imports (org json JSONArray) (org json JSONObject)) + (typed-library (com sfb ssdreview) + (export groupFeaturesFromTruthGroup groupFeaturesFromGroup) + (type JSONArray) + (type JSONObject) + (type Float32) + (type FloatArray) + (type Int32) + (def (featureJsonArrayOrEmpty (raw : (Nullable JSONArray))) : JSONArray + (if (nullable-null? raw) + (json-array-empty) + (nullable-get raw))) + (def (featurePositiveFloat (value : Float32)) : Float32 + (if (> value (float32 1.0)) + value + (float32 1.0))) + (def (featureSafeImage (value : Int32)) : Float32 + (featurePositiveFloat (float32 value))) + (def (featureObject (count : Int32) + (x1 : Float32) + (y1 : Float32) + (x2 : Float32) + (y2 : Float32) + (imageWidth : Int32) + (imageHeight : Int32)) : JSONObject + (let ((w (featurePositiveFloat (- x2 x1))) + (h (featurePositiveFloat (- y2 y1))) + (safeWidth (featureSafeImage imageWidth)) + (safeHeight (featureSafeImage imageHeight))) + (let ((out (json-object-empty))) + (begin + (json-object-put-int32! out "count" count) + (json-object-put-float32! out "cx" + (/ (/ (+ x1 x2) (float32 2.0)) safeWidth)) + (json-object-put-float32! out "cy" + (/ (/ (+ y1 y2) (float32 2.0)) safeHeight)) + (json-object-put-float32! out "w" (/ w safeWidth)) + (json-object-put-float32! out "h" (/ h safeHeight)) + (json-object-put-float32! out "aspect" (/ w h)) + out)))) + (def (groupFeaturesFromTruthGroup (group : JSONObject) + (imageWidth : Int32) + (imageHeight : Int32)) : JSONObject + (let ((bbox (featureJsonArrayOrEmpty (json-object-opt-json-array group "bbox")))) + (featureObject + (json-object-opt-int32-default group "count" (int32 0)) + (json-array-opt-float32 bbox (int32 0) (float32 0.0)) + (json-array-opt-float32 bbox (int32 1) (float32 0.0)) + (json-array-opt-float32 bbox (int32 2) (float32 0.0)) + (json-array-opt-float32 bbox (int32 3) (float32 0.0)) + imageWidth + imageHeight))) + (def (groupFeaturesFromGroup (group : SsdGroup) + (imageWidth : Int32) + (imageHeight : Int32)) : JSONObject + (let ((bbox (SsdGroup-bbox group))) + (featureObject + (SsdGroup-count group) + (float-array-ref bbox (int32 0)) + (float-array-ref bbox (int32 1)) + (float-array-ref bbox (int32 2)) + (float-array-ref bbox (int32 3)) + imageWidth + imageHeight))))) + (typed-kotlin-file "com/sfb/ssdreview/Guess.kt" (typed-library (com sfb ssdreview) (export make-Guess Guess? Guess-label Guess-boxType Guess-reason) @@ -5341,7 +5407,7 @@ " var applied = 0" " session.groups.forEach { group ->" " if (group.label.isNotBlank() && !group.label.endsWith(\"?\")) return@forEach" - " val features = groupFeatures(group, session.imageWidth, session.imageHeight)" + " val features = groupFeaturesFromGroup(group, session.imageWidth, session.imageHeight)" " var best: JSONObject? = null" " var bestScore = Float.POSITIVE_INFINITY" " for (i in 0 until examples.length()) {" @@ -5595,7 +5661,7 @@ " .put(\"firing_arc\", group.optString(\"firing_arc\", group.optString(\"arc\")))" " .put(\"count\", group.optInt(\"count\"))" " .put(\"bbox\", group.optJSONArray(\"bbox\") ?: JSONArray())" - " .put(\"features\", groupFeatures(group, imageWidth(truth), imageHeight(truth))))" + " .put(\"features\", groupFeaturesFromTruthGroup(group, imageWidth(truth), imageHeight(truth))))" " }" " }" " val learned = JSONObject()" @@ -5819,39 +5885,6 @@ " return file.inputStream().use { readBoundedBytes(it, MAX_ZIP_ENTRY_BYTES).decodeToString() }" " }" "" - " private fun groupFeatures(group: JSONObject, imageWidth: Int, imageHeight: Int): JSONObject {" - " val bbox = group.optJSONArray(\"bbox\") ?: JSONArray()" - " val x1 = bbox.optDouble(0, 0.0)" - " val y1 = bbox.optDouble(1, 0.0)" - " val x2 = bbox.optDouble(2, 0.0)" - " val y2 = bbox.optDouble(3, 0.0)" - " val w = (x2 - x1).coerceAtLeast(1.0)" - " val h = (y2 - y1).coerceAtLeast(1.0)" - " return JSONObject()" - " .put(\"count\", group.optInt(\"count\", 0))" - " .put(\"cx\", ((x1 + x2) / 2.0) / imageWidth.coerceAtLeast(1))" - " .put(\"cy\", ((y1 + y2) / 2.0) / imageHeight.coerceAtLeast(1))" - " .put(\"w\", w / imageWidth.coerceAtLeast(1))" - " .put(\"h\", h / imageHeight.coerceAtLeast(1))" - " .put(\"aspect\", w / h)" - " }" - "" - " private fun groupFeatures(group: SsdGroup, imageWidth: Int, imageHeight: Int): JSONObject {" - " val x1 = group.bbox[0].toDouble()" - " val y1 = group.bbox[1].toDouble()" - " val x2 = group.bbox[2].toDouble()" - " val y2 = group.bbox[3].toDouble()" - " val w = (x2 - x1).coerceAtLeast(1.0)" - " val h = (y2 - y1).coerceAtLeast(1.0)" - " return JSONObject()" - " .put(\"count\", group.count)" - " .put(\"cx\", ((x1 + x2) / 2.0) / imageWidth.coerceAtLeast(1))" - " .put(\"cy\", ((y1 + y2) / 2.0) / imageHeight.coerceAtLeast(1))" - " .put(\"w\", w / imageWidth.coerceAtLeast(1))" - " .put(\"h\", h / imageHeight.coerceAtLeast(1))" - " .put(\"aspect\", w / h)" - " }" - "" " private fun featureDistance(a: JSONObject, b: JSONObject): Float {" " val countA = a.optDouble(\"count\", 0.0)" " val countB = b.optDouble(\"count\", 0.0)"