通过TFLite模型完成目标检测后,如何裁剪检测到的矩形区域?
问题根因
你裁剪失败的核心原因是坐标体系不匹配:
- 你调用
cropToFrameTransform.mapRect(location)之后,location的坐标已经从TFLite输入图(cropCopyBitmap,尺寸为模型输入尺寸,比如300x300/640x640)的相对坐标,转换为了原始预览帧(rgbFrameBitmap,尺寸为previewWidth x previewHeight)的绝对坐标 - 但你裁剪用的源图是
cropCopyBitmap,两者的尺寸完全不一致,要么裁剪坐标超出cropCopyBitmap的宽高范围,要么裁剪到空白区域,自然会出问题。
可行解决方案
你可以任选以下两种方案之一实现裁剪:
方案1:用未映射的检测坐标裁剪模型输入图
如果你的裁剪需求是从TFLite的输入图里抠出检测区域,直接在调用cropToFrameTransform.mapRect(location)之前做裁剪即可,首先编写通用裁剪方法:
private Bitmap cropBitmapFromRect(Bitmap source, RectF rect) { // 转换坐标为int值同时做边界校验,防止越界 int left = Math.max(0, (int) rect.left); int top = Math.max(0, (int) rect.top); int width = Math.min(source.getWidth() - left, (int) rect.width()); int height = Math.min(source.getHeight() - top, (int) rect.height()); // 宽高合法性校验,避免出现0值导致崩溃 if (width <= 0 || height <= 0) { return null; } return Bitmap.createBitmap(source, left, top, width, height); }
再修改你循环内的代码顺序:
for (final Detector.Recognition result : results) { RectF location = result.getLocation(); if (location != null && result.getConfidence() >= minimumConfidence) { canvas.drawRect(location, paint); // 先裁剪,再做坐标映射 Bitmap cropImage = cropBitmapFromRect(cropCopyBitmap, location); // 坐标映射用于后续追踪逻辑 cropToFrameTransform.mapRect(location); result.setLocation(location); mappedRecognitions.add(result); // 后续赋值逻辑 try { crop_result_list = new Detector.Recognition("0", "BusinessCard", result.getConfidence(), location); if (cropImage != null) { crop_result_list.setCrop(cropImage); } } catch (Exception err) { err.printStackTrace(); } } }
方案2:用映射后的坐标裁剪原始预览帧
如果你需要的是从原始高清预览帧里抠出检测区域,直接用映射后的location坐标裁剪rgbFrameBitmap即可,裁剪方法还是上述的cropBitmapFromRect,调用时替换源图参数即可:
cropToFrameTransform.mapRect(location); result.setLocation(location); mappedRecognitions.add(result); // 用原始预览帧作为裁剪源 Bitmap cropImage = cropBitmapFromRect(rgbFrameBitmap, location);
注意事项
- 不要直接用RectF的float值作为
createBitmap的入参,必须转int后做边界校验,否则很容易出现坐标越界、宽高为0的异常 - 如果你的预览帧有旋转角度,裁剪后需要根据
sensorOrientation对裁剪出来的Bitmap做旋转处理,保证方向正确
内容的提问来源于stack exchange,提问作者AlisonJ
相关产品推荐
相关产品推荐

