如何获取ML Kit TextRecognizer识别文本的屏幕位置?
解决ML Kit实时文本识别的屏幕坐标映射问题
要实现类似Google Translate的「在原文本位置显示转换后文本」的效果,核心是把ML Kit识别出的文本坐标(基于输入图像)转换成屏幕预览View上的坐标,具体步骤如下:
1. 获取ML Kit识别文本的图像坐标
ML Kit返回的Text对象中,每个文本块(TextBlock)、文本行(Line)或字符元素(Element)都可以通过getBoundingBox()获取其在输入图像坐标系中的矩形边界。
在你的onSuccess回调中遍历识别结果:
@Override public void onSuccess(@NonNull Text visionText) { // 遍历所有文本块 for (Text.TextBlock block : visionText.getTextBlocks()) { Rect imageBoundingBox = block.getBoundingBox(); if (imageBoundingBox == null) continue; // 后续需要将这个imageBoundingBox转换成屏幕坐标 } }
2. 图像坐标转屏幕预览View坐标
由于相机预览图像存在旋转、缩放、适配预览View的情况,需要将图像坐标映射到屏幕上的PreviewView坐标:
步骤细节:
首先在Activity/Fragment中持有PreviewView和自定义OverlayView的引用:
private PreviewView previewView; private OverlayView overlayView; // 初始化时赋值 previewView = findViewById(R.id.previewView); overlayView = findViewById(R.id.overlayView);
然后在onSuccess中完成坐标转换:
@Override public void onSuccess(@NonNull Text visionText) { // 获取输入图像的原始尺寸和旋转角度 int imageWidth = imageProxy.getWidth(); int imageHeight = imageProxy.getHeight(); int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees(); // 旋转后图像的宽高(90/270度旋转时宽高互换) int rotatedImageWidth = (rotationDegrees == 90 || rotationDegrees == 270) ? imageHeight : imageWidth; int rotatedImageHeight = (rotationDegrees == 90 || rotationDegrees == 270) ? imageWidth : imageHeight; // 获取PreviewView的屏幕尺寸 int viewWidth = previewView.getWidth(); int viewHeight = previewView.getHeight(); // 计算适配预览View的缩放比例(保持图像比例,居中显示) float scaleX = (float) viewWidth / rotatedImageWidth; float scaleY = (float) viewHeight / rotatedImageHeight; float scale = Math.min(scaleX, scaleY); // 计算图像在预览View中的偏移量(居中对齐) float offsetX = (viewWidth - rotatedImageWidth * scale) / 2f; float offsetY = (viewHeight - rotatedImageHeight * scale) / 2f; List<OverlayView.TextDrawInfo> drawInfos = new ArrayList<>(); for (Text.TextBlock block : visionText.getTextBlocks()) { Rect imageBox = block.getBoundingBox(); if (imageBox == null) continue; // 将图像坐标转换为屏幕坐标 int screenLeft = (int) (imageBox.left * scale + offsetX); int screenTop = (int) (imageBox.top * scale + offsetY); int screenRight = (int) (imageBox.right * scale + offsetX); int screenBottom = (int) (imageBox.bottom * scale + offsetY); // 替换成你的文本转换逻辑(比如翻译) String translatedText = translateOriginalText(block.getText()); drawInfos.add(new OverlayView.TextDrawInfo(screenLeft, screenTop, screenRight, screenBottom, translatedText)); } // 更新OverlayView绘制内容 overlayView.setTextDrawInfos(drawInfos); }
3. 实现OverlayView绘制转换后文本
自定义一个悬浮在PreviewView上层的View,用于绘制转换后的文本:
public class OverlayView extends View { private final Paint textPaint = new Paint(); private final Paint borderPaint = new Paint(); private List<TextDrawInfo> textDrawInfos = new ArrayList<>(); public OverlayView(Context context) { super(context); initPaints(); } public OverlayView(Context context, AttributeSet attrs) { super(context, attrs); initPaints(); } private void initPaints() { // 文本画笔 textPaint.setColor(Color.WHITE); textPaint.setTextSize(40); textPaint.setAntiAlias(true); textPaint.setShadowLayer(5, 0, 0, Color.BLACK); // 增加阴影提升可读性 // 边界框画笔 borderPaint.setColor(Color.RED); borderPaint.setStyle(Paint.Style.STROKE); borderPaint.setStrokeWidth(2); borderPaint.setAntiAlias(true); } public void setTextDrawInfos(List<TextDrawInfo> infos) { this.textDrawInfos = infos; invalidate(); // 触发重绘 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (TextDrawInfo info : textDrawInfos) { // 绘制边界框(可选) canvas.drawRect(info.left, info.top, info.right, info.bottom, borderPaint); // 绘制转换后的文本(对齐原文本底部) canvas.drawText(info.translatedText, info.left, info.bottom, textPaint); } } // 存储文本坐标和转换后内容的实体类 public static class TextDrawInfo { int left, top, right, bottom; String translatedText; public TextDrawInfo(int left, int top, int right, int bottom, String translatedText) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; this.translatedText = translatedText; } } }
4. 布局配置
将OverlayView叠加在PreviewView上层:
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.camera.view.PreviewView android:id="@+id/previewView" android:layout_width="match_parent" android:layout_height="match_parent"/> <com.yourpackage.OverlayView android:id="@+id/overlayView" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>
注意事项
- 文本转换(如翻译)要放在子线程执行,避免阻塞相机分析线程,可通过
AsyncTask或协程实现。 - 如果
PreviewView使用非居中的缩放模式(如FIT_START),需要调整偏移量的计算逻辑。 - 若需要更精细的文本位置,可遍历
TextBlock.getLines()或Line.getElements()获取更小粒度的边界框。
内容的提问来源于stack exchange,提问作者Darcia14
相关产品推荐
相关产品推荐

