Я работаю над приложением для Android, в котором реализую распознавание текста в реальном времени с помощью CameraX и ML Kit. Распознанный текст отображается с ограничивающими рамками при предварительном просмотре камеры, но я столкнулся с проблемой, заключающейся в том, что эти ограничивающие рамки неправильно совмещаются с текстом в прямой трансляции. Описание проблемы При запуске приложения:
Предварительный просмотр камеры правильно отображает прямую трансляцию.
Распознавание текста ML Kit обрабатывает изображение и идентифицирует текстовые блоки.
Ограничительные рамки рисуются вокруг обнаруженных текстовых элементов.
Однако эти ограничивающие рамки не совместить с фактическим текстом в предварительном просмотре камеры. Они выглядят смещенными или неправильно масштабированы.
package com.aviskaarlab.booksnap.ui.views.home
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
class TextOverlay @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val paint = Paint().apply {
color = Color.RED
style = Paint.Style.STROKE
strokeWidth = 2.0f
}
private val boundingBoxes = mutableListOf()
private var clickListener: ((String) -> Unit)? = null
fun setOnRectangleClickListener(listener: (String) -> Unit) {
clickListener = listener
}
fun updateBoundingBoxes(newBoundingBoxes: List) {
boundingBoxes.clear()
boundingBoxes.addAll(newBoundingBoxes)
invalidate() // Redraw the view
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
for (box in boundingBoxes) {
canvas.drawRect(box.rect, paint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP) {
val x = event.x
val y = event.y
for (box in boundingBoxes) {
if (box.rect.contains(x, y)) {
clickListener?.invoke("Clicked on rectangle ${box.text}")
return true
}
}
}
return true // Event handled
}
}
Проблема
Прямоугольники, нарисованные вокруг распознанного текста, не совпадают с текстом в предварительном просмотре камеры. Как настроить координаты ограничительной рамки, чтобы они правильно накладывались на текст в прямом эфире с камеры? (см. прикрепленное изображение)
Я работаю над приложением для Android, в котором реализую распознавание текста в реальном времени с помощью CameraX и ML Kit. Распознанный текст отображается с ограничивающими рамками при предварительном просмотре камеры, но я столкнулся с проблемой, заключающейся в том, что эти ограничивающие рамки неправильно совмещаются с текстом в прямой трансляции. [b] Описание проблемы При запуске приложения:[/b] [list] [*]Предварительный просмотр камеры правильно отображает прямую трансляцию. [*] Распознавание текста ML Kit обрабатывает изображение и идентифицирует текстовые блоки. [*]Ограничительные рамки рисуются вокруг обнаруженных текстовых элементов. [*]Однако эти ограничивающие рамки не совместить с фактическим текстом в предварительном просмотре камеры. Они выглядят смещенными или неправильно масштабированы. [/list] Фрагменты основных действий [code]package com.aviskaarlab.booksnap.ui.views.home
class MainActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding private lateinit var cameraExecutor: ExecutorService private lateinit var textOverlay: TextOverlay private lateinit var viewFinder: PreviewView
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root)
internal class BookSnapWordAnalyzer( private val overlay: TextOverlay, private val previewView: PreviewView, ) : ImageAnalysis.Analyzer {
companion object { private const val TAG = "BookSnapWordAnalyzer" private const val WORD_LENGTH = 4 }
private val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) private lateinit var visionText: Text private lateinit var matrix: Matrix private var rotationDegrees: Int = 0
@OptIn(ExperimentalGetImage::class) override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image ?: return rotationDegrees = imageProxy.imageInfo.rotationDegrees matrix = getCorrectionMatrix(imageProxy, previewView)
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
recognizer.process(image) .addOnSuccessListener { visionText -> this.visionText = visionText val boxes = mutableListOf() for (block in visionText.textBlocks) { for (line in block.lines) { for (element in line.elements) { val elementText = element.text val boundingBox = element.boundingBox if (elementText.length >= WORD_LENGTH && boundingBox != null) { boxes.add( CustomRect( adjustBoundingBox(boundingBox, imageProxy, previewView), elementText ) ) } } } } overlay.updateBoundingBoxes(boxes) } .addOnFailureListener { e -> Log.e(TAG, "Text recognition failed", e) }.addOnCompleteListener { imageProxy.close() } }
private fun adjustBoundingBox( rect: Rect, imageProxy: ImageProxy, previewView: PreviewView ): RectF { val cropRect = imageProxy.cropRect val imageWidth = cropRect.width() val imageHeight = cropRect.height()
val previewWidth = previewView.width val previewHeight = previewView.height
val scaleX = previewWidth.toFloat() / imageWidth val scaleY = previewHeight.toFloat() / imageHeight
val verticalOffset = (previewHeight - imageHeight * scaleY) / 2 val horizontalOffset = (previewWidth - imageWidth * scaleX) / 2
val left = rect.left * scaleX + horizontalOffset val top = rect.top * scaleY + verticalOffset val right = rect.right * scaleX + horizontalOffset val bottom = rect.bottom * scaleY + verticalOffset
return RectF(left, top, right, bottom) }
private fun getCorrectionMatrix( imageProxy: ImageProxy, previewView: PreviewView, ): Matrix { val cropRect = imageProxy.cropRect val matrix = Matrix()
class TextOverlay @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) {
private val paint = Paint().apply { color = Color.RED style = Paint.Style.STROKE strokeWidth = 2.0f }
private val boundingBoxes = mutableListOf() private var clickListener: ((String) -> Unit)? = null
fun setOnRectangleClickListener(listener: (String) -> Unit) { clickListener = listener }
fun updateBoundingBoxes(newBoundingBoxes: List) { boundingBoxes.clear() boundingBoxes.addAll(newBoundingBoxes) invalidate() // Redraw the view }
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) for (box in boundingBoxes) { canvas.drawRect(box.rect, paint) } }
override fun onTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_UP) { val x = event.x val y = event.y for (box in boundingBoxes) { if (box.rect.contains(x, y)) { clickListener?.invoke("Clicked on rectangle ${box.text}") return true } } } return true // Event handled } } [/code] [b]Проблема[/b] Прямоугольники, нарисованные вокруг распознанного текста, не совпадают с текстом в предварительном просмотре камеры. Как настроить координаты ограничительной рамки, чтобы они правильно накладывались на текст в прямом эфире с камеры? (см. прикрепленное изображение) [img]https://i.sstatic.net/pnM6s7fg.jpg[/img]