Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 54
Fix/adfa 2731 adding dictionary to handle ocr errors experimental#921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
9ae6fa601c995c9eb52e01ab72ac8ca6217b351af5941a51c3291265796a2fec062dd2caa128506a8795c51f05cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| package org.appdevforall.codeonthego.computervision.domain | ||
| import android.util.Log | ||
| import org.appdevforall.codeonthego.computervision.domain.model.DetectionResult | ||
| import kotlin.math.min | ||
| object MarginAnnotationParser { | ||
| private const val TAG = "MarginAnnotationParser" | ||
| // --- Dictionaries for Widgets and Attributes --- | ||
| private val widgetTypeMap = mapOf( | ||
| 'B' to "Button", | ||
| 'I' to "ImageView", | ||
| 'P' to "ImageView" // P is also an ImageView as seen in logs | ||
| ) | ||
| private val widgetAttributes = mapOf( | ||
| "Button" to setOf("layout_width", "layout_height", "id", "text", "background"), | ||
| "ImageView" to setOf("layout_width", "layout_height", "id", "src", "layout_gravity", "contentDescription") | ||
| ) | ||
| // --- OCR Correction and Value Keywords --- | ||
| private val attributeCorrectionMap = mapOf( | ||
| "ld" to "id", | ||
| "scr" to "src", | ||
| "background" to "background", | ||
| "layout width" to "layout_width", | ||
| "layout height" to "layout_height", | ||
| "lavout height" to "layout_height", | ||
| "yekow" to "yellow" | ||
| ) | ||
| private val valueCorrectionMap = mapOf( | ||
| "red" to "#FFFF0000", | ||
| "blue" to "#FF0000FF", | ||
| "green" to "#FF00FF00", | ||
| "yellow" to "#FFFFFF00", | ||
| "black" to "#FF000000", | ||
| "white" to "#FFFFFFFF", | ||
| "gray" to "#FF888888", | ||
| "purple" to "#FF800080", | ||
| "orange" to "#FFFFA500" | ||
| ) | ||
| private fun isTag(text: String): Boolean { | ||
| return text.matches(Regex("^(B-|P-|I-)\\d+$")) | ||
| } | ||
| /** | ||
| * Parses a dimension string, correcting common OCR errors and ensuring a valid unit. | ||
| */ | ||
| private fun parseDimension(value: String): String { | ||
| val numericPart = value.filter { it.isDigit() } | ||
| if (numericPart.isEmpty()) return value // Return original if no numbers found | ||
| // Check for presence of a unit, assuming 'dp' if absent or malformed | ||
| val unitPart = value.filter { it.isLetter() } | ||
| return if (unitPart.contains("dp", ignoreCase = true) || unitPart.contains("sp", ignoreCase = true) || unitPart.contains("px", ignoreCase = true)) { | ||
| "$numericPart${unitPart.lowercase()}" | ||
| } else { | ||
| "$numericPart" + "dp" // Default to 'dp' | ||
| } | ||
| } | ||
| /** | ||
| * Parses a raw annotation string into a clean map of XML attributes. | ||
| */ | ||
| private fun parseAnnotationString(rawText: String, widgetType: String): Map<String, String> { | ||
| val attributesForWidget = widgetAttributes[widgetType] ?: return emptyMap() | ||
| // Pre-process the text to normalize attribute keywords | ||
| var correctedText = rawText.lowercase() | ||
| attributeCorrectionMap.forEach { (ocrError, correctAttr) -> | ||
| correctedText = correctedText.replace(ocrError, correctAttr) | ||
| } | ||
| // Specific regex for handling variations of layout attributes | ||
| correctedText = correctedText.replace(Regex("layout\\s*[-_]?\\s*width"), "layout_width") | ||
| correctedText = correctedText.replace(Regex("layout\\s*[-_]?\\s*height"), "layout_height") | ||
| correctedText = correctedText.replace(Regex("layout\\s*[-_]?\\s*gravity"), "layout_gravity") | ||
| val parsedAttributes = mutableMapOf<String, String>() | ||
| val attributeRegex = attributesForWidget.joinToString("|") { Regex.escape(it) }.toRegex() | ||
| val matches = attributeRegex.findAll(correctedText).toList() | ||
| matches.forEachIndexed { i, match -> | ||
| val key = match.value | ||
| val valueStartIndex = match.range.last + 1 | ||
| val valueEndIndex = if (i + 1 < matches.size) matches[i + 1].range.first else correctedText.length | ||
| var value = correctedText.substring(valueStartIndex, valueEndIndex).replace(":", "").trim() | ||
| // Clean and format the extracted value | ||
| value = when (key) { | ||
| "id" -> value.replace(Regex("\\s+"), "_").replace(Regex("[^a-zA-Z0-9_]"), "") | ||
| "background" -> valueCorrectionMap[value] ?: value | ||
| "src" -> valueCorrectionMap[value] ?: "@drawable/${value.replace(' ', '_')}" | ||
| "layout_width", "layout_height" -> parseDimension(value) | ||
| else -> value | ||
avestaadfa marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| parsedAttributes[key] = value | ||
| } | ||
| return parsedAttributes | ||
| } | ||
| fun parse( | ||
| detections: List<DetectionResult>, | ||
| imageWidth: Int, | ||
| leftGuidePct: Float, | ||
| rightGuidePct: Float | ||
| ): Pair<List<DetectionResult>, Map<String, String>> { | ||
| val leftMarginPx = imageWidth * leftGuidePct | ||
| val rightMarginPx = imageWidth * rightGuidePct | ||
| val (canvasDetections, marginDetections) = detections.partition { | ||
| it.boundingBox.centerX().toInt() in (leftMarginPx.toInt() + 1)..rightMarginPx.toInt() | ||
| } | ||
| val groupedMarginOcr = mutableMapOf<String, MutableList<String>>() | ||
| var currentMarginTag: String? = null | ||
| marginDetections.sortedBy { it.boundingBox.top }.forEach { detection -> | ||
| val text = detection.text.trim() | ||
| if (isTag(text)) { | ||
| currentMarginTag = text | ||
| if (!groupedMarginOcr.containsKey(currentMarginTag)) { | ||
| groupedMarginOcr[currentMarginTag!!] = mutableListOf() | ||
| } | ||
| } else if (currentMarginTag != null) { | ||
| groupedMarginOcr[currentMarginTag!!]?.add(text) | ||
| } | ||
| } | ||
| val finalAnnotationMap = mutableMapOf<String, String>() | ||
| groupedMarginOcr.forEach { (tag, texts) -> | ||
| val widgetPrefix = tag.first() | ||
| val widgetType = widgetTypeMap[widgetPrefix] | ||
| if (widgetType != null) { | ||
| val fullAnnotationText = texts.joinToString(" ") | ||
| val parsedAttrs = parseAnnotationString(fullAnnotationText, widgetType) | ||
| if (parsedAttrs.isNotEmpty()) { | ||
| val attrString = parsedAttrs.map { (k, v) -> | ||
| val key: String | ||
| val value: String | ||
| when (k) { | ||
| "id" -> { | ||
| key = "android:id" | ||
| value = "@+id/$v" | ||
| } | ||
| "background" -> { | ||
| key = "app:backgroundTint" | ||
| value = v | ||
| } | ||
| else -> { | ||
| key = "android:$k" | ||
| value = v | ||
| } | ||
| } | ||
| "$key=\"$value\"" | ||
| }.joinToString("\n") | ||
| finalAnnotationMap[tag] = attrString | ||
| } | ||
| } | ||
| } | ||
| // --- Logging for Verification --- | ||
| Log.d(TAG, "--- Raw OCR Content by Region ---") | ||
| Log.d(TAG, "Canvas OCR: [${canvasDetections.joinToString(", ") { "'${it.text}'" }}]") | ||
| Log.d(TAG, "--- Grouped Margin OCR ---") | ||
| groupedMarginOcr.forEach { (tag, texts) -> | ||
| Log.d(TAG, "Tag: '$tag', Content: [${texts.joinToString(", ") { "'$it'" }}]") | ||
| } | ||
| Log.d(TAG, "--- Parsed Annotations ---") | ||
| finalAnnotationMap.forEach { (tag, attrs) -> | ||
| Log.d(TAG, "Tag: '$tag', Attributes:\n$attrs") | ||
| } | ||
| Log.d(TAG, "--------------------------") | ||
| val canvasWidgetTags = canvasDetections.filter { isTag(it.text.trim()) } | ||
| Log.d(TAG, "Canvas Widget Tags Found: ${canvasWidgetTags.joinToString(", ") { "'${it.text.trim()}'" }}") | ||
avestaadfa marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| return Pair(canvasDetections, finalAnnotationMap) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.