You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:40:29