Treat SSD area truth as detector boundary
ober
96a4b7772389cf87f2caeb286f2bc9fb2ee66b17
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -298,6 +298,7 @@ "import kotlin.system.exitProcess" "import kotlin.math.ceil" "import kotlin.math.max" + "import kotlin.math.min" "import kotlin.math.sqrt" "" "class MainActivity : Activity() {" @@ -774,19 +775,36 @@ " setStatus(\"SSD area ignored: selection was too small\")" " return" " }" - " val removed = session.setSsdArea(area)" + " session.ssdArea = floatArrayOf(" + " min(area[0], area[2])," + " min(area[1], area[3])," + " max(area[0], area[2])," + " max(area[1], area[3])" + " )" + " val beforeGroups = session.groups.size" + " val outsideCount = session.groups.count { !session.groupInsideSsdArea(it) }" + " val removed = if (beforeGroups > 0 && outsideCount < beforeGroups) {" + " session.pruneGroupsOutsideSsdArea()" + " } else {" + " 0" + " }" " selectedGroupId = session.groups.firstOrNull { it.id == selectedGroupId }?.id" " ?: session.groups.firstOrNull()?.id" " reviewView.ssdArea = session.ssdArea" " reviewView.selectedGroupId = selectedGroupId" - " truthStore.appendEvent(\"ssd_area_set\", session, JSONObject().put(\"removed_groups\", removed))" + " truthStore.appendEvent(\"ssd_area_set\", session, JSONObject()" + " .put(\"removed_groups\", removed)" + " .put(\"before_groups\", beforeGroups)" + " .put(\"outside_groups\", outsideCount))" " reviewView.performHapticFeedback(HapticFeedbackConstants.CONFIRM)" " reviewView.invalidate()" " saveSessionAsync(" " session," - " \"SSD area set; removed $removed outside tags; saving...\"," - " \"SSD area saved; removed $removed outside tags\"" - " )" + " \"SSD area set; saving boundary...\"," + " \"SSD area saved; detecting inside boundary...\"" + " ) {" + " detectCurrent(forceDetect = true)" + " }" " }" "" " private fun selectGroup(groupId: String?) {" @@ -929,44 +947,42 @@ " }" "" " val remoteMatches = 0" - " val existingTruth = truthStore.loadTruth(session)" + " val existingTruth = localTruth ?: truthStore.loadTruth(session)" + " val existingTruthGroupCount = existingTruth?.optJSONArray(\"groups\")?.length() ?: 0" " var ocrGuesses = 0" " var detectorNote = \"\"" " var detectorSource = \"unknown\"" "" - " if (existingTruth != null) {" + " if (existingTruth != null && existingTruthGroupCount > 0 && !forceDetect) {" " detectorSource = \"local-truth\"" " session.ssdArea = jsonRect(existingTruth.optJSONArray(\"ssd_area\"))" - " if (forceDetect) {" - " val forcedDetection = SsdVisionBridge.detect(bitmap)" - " if (forcedDetection != null) {" - " detectorNote = \", rust detector\"" - " detectorSource = \"forced-rust-plus-truth\"" - " } else {" - " detectorSource = \"forced-android-plus-truth\"" - " }" - " val forcedCells = forcedDetection?.cells ?: SsdDetector.detectCells(bitmap)" - " session.cells.addAll(forcedCells.filter { session.cellInsideSsdArea(it) })" - " }" " truthStore.applyTruth(session)" " session.pruneGroupsOutsideSsdArea()" " } else {" + " if (existingTruth != null) {" + " session.ssdArea = jsonRect(existingTruth.optJSONArray(\"ssd_area\"))" + " }" " val nativeDetection = SsdVisionBridge.detect(bitmap)" + " val detectedCells: List<SsdCell>" " if (nativeDetection != null) {" - " session.cells.addAll(nativeDetection.cells)" - " session.groups.addAll(nativeDetection.groups.take(80))" + " detectedCells = nativeDetection.cells" " detectorNote = \", rust detector\"" - " detectorSource = \"rust-detector\"" + " detectorSource = if (forceDetect && existingTruthGroupCount > 0) \"forced-rust-plus-truth\" else \"rust-detector\"" " } else {" - " session.cells.addAll(SsdDetector.detectCells(bitmap))" - " session.groups.addAll(SsdDetector.initialGroups(session.cells).take(80))" - " detectorSource = \"android-fallback\"" + " detectedCells = SsdDetector.detectCells(bitmap)" + " detectorSource = if (forceDetect && existingTruthGroupCount > 0) \"forced-android-plus-truth\" else \"android-fallback\"" + " }" + " session.cells.addAll(detectedCells.filter { session.cellInsideSsdArea(it) })" + " session.groups.addAll(SsdDetector.initialGroups(session.cells).take(120))" + " if (existingTruth != null && existingTruthGroupCount > 0) {" + " truthStore.applyTruth(session)" + " session.pruneGroupsOutsideSsdArea()" " }" " Guessing.applyInitialGuesses(this, session)" " session.ocrWords.addAll(SsdOcr.recognizeWords(bitmap))" " ocrGuesses = SsdOcr.applyOcrGuesses(this, session)" " }" - " val learnedGuesses = if (existingTruth == null) truthStore.applyLearnedGuesses(session) else 0" + " val learnedGuesses = if (existingTruth == null || existingTruthGroupCount == 0) truthStore.applyLearnedGuesses(session) else 0" " runOnUiThread {" " if (!isCurrentLoad(expectedGeneration, uri, expectedPageIndex)) return@runOnUiThread" " currentSession = session" @@ -1138,7 +1154,12 @@ " count.requestFocus()" " }" "" - " private fun saveSessionAsync(session: SsdSession, savingStatus: String, savedStatus: String) {" + " private fun saveSessionAsync(" + " session: SsdSession," + " savingStatus: String," + " savedStatus: String," + " afterSaved: (() -> Unit)? = null" + " ) {" " val truth = session.toTruthJson()" " val viewport = reviewView.captureViewport()" " finishSelectInteraction()" @@ -1150,6 +1171,7 @@ " runOnUiThread {" " restoreViewportAfterLayout(viewport)" " setStatus(savedStatus)" + " afterSaved?.invoke()" " }" " } catch (error: Exception) {" " runOnUiThread {" @@ -2115,6 +2137,7 @@ " fun pruneGroupsOutsideSsdArea(): Int {" " val area = ssdArea ?: return 0" " val outside = groups.filter { group -> !rectCenterInside(group.bbox, area) }.toList()" + " if (outside.isNotEmpty() && outside.size >= groups.size) return 0" " outside.forEach { group ->" " groups.remove(group)" " group.status = \"suppressed\""