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

使用Google Vision实现图片LiveText时点击坐标与文本行不匹配问题求助

问题:LiveText点击位置与文本行匹配错位

我已在图片上实现了LiveText功能,但遇到点击位置与实际文本行不匹配的问题:点击第一行可正常高亮,但点击第三行会高亮第二行,点击第七行会高亮第三行等。

使用的文本识别库依赖:

implementation 'com.google.android.gms:play-services-vision:20.1.3'

现有代码

public class ImageViewActivity2 extends AppCompatActivity {

    private ImageView imageView;
    private Bitmap sampleImage;
    private SparseArray<TextBlock> textBlocks;

    // Variables to track text selection
    private float startX;
    private float startY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_view);

        imageView = findViewById(R.id.img);

        Button ocrButton = findViewById(R.id.ocrButton);

        sampleImage = BitmapFactory.decodeResource(getResources(), R.drawable.he);
        imageView.setImageBitmap(sampleImage);

        ocrButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startTextRecognition();
            }
        });

        // Initialize textBlocks
        textBlocks = new SparseArray<>();

        // Set a touch listener on the ImageView

        imageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                startX = motionEvent.getX();
                startY = motionEvent.getY() ;

                Log.d("Coordinates", "X "+ startX + " Y " + startY+" height="+view.getHeight());
                highlightLineContainingTouchPoint(startX, startY);
                return true;
            }
        });

    }

    private void startTextRecognition() {
        // Initialize the TextRecognizer
        TextRecognizer textRecognizer = new TextRecognizer.Builder(getApplicationContext()).build();

        if (!textRecognizer.isOperational()) {
            // Handle cases where the text recognizer is not operational
            // This may require downloading additional data or checking device settings
            // You can display an error message or prompt the user to enable it
            return;
        }

        // Create a Frame from the loaded image
        Frame frame = new Frame.Builder().setBitmap(sampleImage).build();

        // Perform text recognition
        textBlocks = textRecognizer.detect(frame);
    }

    private void highlightLineContainingTouchPoint(float x, float y) {
        // Initialize a new Bitmap for highlighting
        Bitmap highlightedImage = sampleImage.copy(sampleImage.getConfig(), true);
        Canvas canvas = new Canvas(highlightedImage);
        Paint paint = new Paint();
        paint.setColor(Color.YELLOW);
        paint.setAlpha(128);

        // Iterate over textBlocks to find the line containing the touch point
        for (int i = 0; i < textBlocks.size(); i++) {
            TextBlock textBlock = textBlocks.valueAt(i);
            for (Text line : textBlock.getComponents()) {
                // Check if the touch point is within the bounding box of the line
                if (x >= line.getBoundingBox().left &&
                        x <= line.getBoundingBox().right &&
                        y >= line.getBoundingBox().top &&
                        y <= line.getBoundingBox().bottom) {
                    float left = line.getBoundingBox().left;
                    float top = line.getBoundingBox().top;
                    float bottom = line.getBoundingBox().bottom;
                    float right = line.getBoundingBox().right;
                    Log.e("Coordinates", "Line top"+top+"Bottom"+bottom);

                    // Highlight the line containing the touch point
                    canvas.drawRect(left, top, right, bottom, paint);
                }
            }
        }

        // Update the ImageView with the highlighted image
        imageView.setImageBitmap(highlightedImage);
    }
}

解决方案

核心原因

错位的本质是ImageView显示尺寸与原始Bitmap尺寸不匹配:OCR识别的文本坐标基于原始Bitmap,但触摸事件获取的坐标是ImageView的屏幕显示坐标,两者比例不同,直接对比必然出现偏移。

修复步骤

1. 转换触摸坐标到原始Bitmap坐标系

在触摸事件中,计算ImageView与原始Bitmap的缩放比例,将屏幕触摸坐标转换为Bitmap上的对应坐标:

imageView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        // 获取显示与原始尺寸
        float viewWidth = view.getWidth();
        float viewHeight = view.getHeight();
        float bitmapWidth = sampleImage.getWidth();
        float bitmapHeight = sampleImage.getHeight();
        
        // 计算缩放比例
        float scaleX = bitmapWidth / viewWidth;
        float scaleY = bitmapHeight / viewHeight;
        
        // 转换触摸坐标到Bitmap坐标系
        float bitmapX = motionEvent.getX() * scaleX;
        float bitmapY = motionEvent.getY() * scaleY;

        highlightLineContainingTouchPoint(bitmapX, bitmapY);
        return true;
    }
});

2. 优化高亮逻辑(可选)

找到匹配行后直接跳出循环,提升效率;同时避免重复创建Bitmap副本(若图片较大):

private void highlightLineContainingTouchPoint(float x, float y) {
    Bitmap highlightedImage = sampleImage.copy(sampleImage.getConfig(), true);
    Canvas canvas = new Canvas(highlightedImage);
    Paint paint = new Paint();
    paint.setColor(Color.YELLOW);
    paint.setAlpha(128);

    for (int i = 0; i < textBlocks.size(); i++) {
        TextBlock textBlock = textBlocks.valueAt(i);
        for (Text line : textBlock.getComponents()) {
            Rect boundingBox = line.getBoundingBox();
            if (x >= boundingBox.left && x <= boundingBox.right 
                    && y >= boundingBox.top && y <= boundingBox.bottom) {
                canvas.drawRect(boundingBox, paint);
                // 找到匹配行后直接退出循环
                return;
            }
        }
    }

    imageView.setImageBitmap(highlightedImage);
}

3. 高性能优化方案(可选)

如果图片较大,每次触摸创建Bitmap副本会卡顿,可改用自定义ImageView绘制高亮层,避免重复生成Bitmap:

public class HighlightImageView extends ImageView {
    private Rect highlightRect;
    private Paint highlightPaint;
    private Bitmap sourceBitmap;

    public HighlightImageView(Context context) {
        super(context);
        init();
    }

    public HighlightImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        highlightPaint = new Paint();
        highlightPaint.setColor(Color.YELLOW);
        highlightPaint.setAlpha(128);
    }

    public void setSourceBitmap(Bitmap bitmap) {
        this.sourceBitmap = bitmap;
        setImageBitmap(bitmap);
    }

    public void setHighlightRect(Rect rect) {
        this.highlightRect = rect;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (highlightRect != null && sourceBitmap != null) {
            // 转换矩形到显示坐标系
            float scaleX = (float) getWidth() / sourceBitmap.getWidth();
            float scaleY = (float) getHeight() / sourceBitmap.getHeight();
            canvas.drawRect(
                    highlightRect.left * scaleX,
                    highlightRect.top * scaleY,
                    highlightRect.right * scaleX,
                    highlightRect.bottom * scaleY,
                    highlightPaint
            );
        }
    }
}

在Activity中使用该自定义View,触摸时仅更新高亮矩形即可:

// 替换原有ImageView为自定义View
private HighlightImageView imageView;

// 触摸事件中
imageView.setHighlightRect(line.getBoundingBox());

额外注意

  • 若ImageView使用CENTER_CROP缩放模式,需额外计算偏移量,确保坐标转换准确;
  • 可在OCR识别完成后缓存所有文本行的坐标,避免每次触摸都遍历textBlocks。

内容的提问来源于stack exchange,提问作者user18001087

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 08:29:59