使用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
相关产品推荐
相关产品推荐

