如何在Camera Source中用ML Kit TextRecognizer替换GMS TextRecognizer
问题
我的应用通过SurfaceView相机持续检测文本并显示到TextView,原代码使用GMS TextRecognizer实现。现在想替换为ML Kit TextRecognizer,保持相同的连续识别逻辑,但两者API存在差异:原GMS通过setProcessor处理检测结果,而ML Kit的TextRecognizer需调用process(image)异步处理图像,不清楚如何协调两者,请求最简单的替换方案。
原代码:
cameraView = findViewById(R.id.surface_view); textView = findViewById(R.id.text_view); // 1 final TextRecognizer textRecognizer = new TextRecognizer.Builder(getApplicationContext()).build(); if (!textRecognizer.isOperational()) { Log.w("MainActivity", "Detected dependence are not found "); } else { cameraSource = new CameraSource.Builder(getApplicationContext(), textRecognizer) .setFacing(CameraSource.CAMERA_FACING_BACK) .setRequestedPreviewSize(1280, 1024) .setRequestedFps(2.0f) .setAutoFocusEnabled(true) .build(); // 2 cameraView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { try { if (ActivityCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA}, RequestCameraPermission); } cameraSource.start(cameraView.getHolder()); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { cameraSource.stop(); } }); // 4 textRecognizer.setProcessor(new Detector.Processor<TextBlock>() { @Override public void release() { } @Override public void receiveDetections(Detector.Detections<TextBlock> detections) { final SparseArray<TextBlock> items = detections.getDetectedItems(); if (items.size() != 0 ){ textView.post(new Runnable() { @Override public void run() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0 ;i < items.size();i++){ TextBlock item = items.valueAt(i); stringBuilder.append(item.getValue()); stringBuilder.append("\n"); } textView.setText(stringBuilder.toString()); Log.d("Text",stringBuilder.toString()); } }); } } }); }
最简单替换方案
核心思路是放弃原GMS的Detector绑定逻辑,改用CameraSource.PreviewCallback获取相机帧,再传给ML Kit异步识别。以下是完整修改后的代码:
import com.google.mlkit.vision.text.Text; import com.google.mlkit.vision.text.TextRecognition; import com.google.mlkit.vision.text.TextRecognizer; import com.google.mlkit.vision.text.latin.TextRecognizerOptions; // ... 其他原有导入 public class MainActivity extends AppCompatActivity { private SurfaceView cameraView; private TextView textView; private CameraSource cameraSource; private static final int RequestCameraPermission = 1001; // ML Kit 文本识别器 private TextRecognizer mlKitTextRecognizer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cameraView = findViewById(R.id.surface_view); textView = findViewById(R.id.text_view); // 初始化ML Kit文本识别器 mlKitTextRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS); // 创建CameraSource,不再传入GMS的TextRecognizer cameraSource = new CameraSource.Builder(getApplicationContext(), null) .setFacing(CameraSource.CAMERA_FACING_BACK) .setRequestedPreviewSize(1280, 1024) .setRequestedFps(2.0f) .setAutoFocusEnabled(true) .build(); // 设置预览回调,获取相机每一帧图像 cameraSource.setPreviewCallback(new CameraSource.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { // 将相机帧转换为ML Kit的InputImage Camera.Size size = camera.getParameters().getPreviewSize(); int rotation = getWindowManager().getDefaultDisplay().getRotation(); InputImage image = InputImage.fromByteArray(data, size.width, size.height, rotation, InputImage.IMAGE_FORMAT_NV21); // 调用ML Kit异步识别文本 mlKitTextRecognizer.process(image) .addOnSuccessListener(text -> { // 处理识别结果并更新TextView StringBuilder stringBuilder = new StringBuilder(); for (Text.TextBlock block : text.getTextBlocks()) { stringBuilder.append(block.getText()); stringBuilder.append("\n"); } // 在主线程更新UI textView.post(() -> textView.setText(stringBuilder.toString())); Log.d("Text", stringBuilder.toString()); }) .addOnFailureListener(e -> Log.w("MainActivity", "文本识别失败", e)); } }); // SurfaceHolder回调逻辑保持不变 cameraView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { try { if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, RequestCameraPermission); return; } cameraSource.start(cameraView.getHolder()); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(SurfaceHolder holder) { cameraSource.stop(); } }); } @Override protected void onDestroy() { super.onDestroy(); // 释放ML Kit识别器资源 if (mlKitTextRecognizer != null) { mlKitTextRecognizer.close(); } if (cameraSource != null) { cameraSource.release(); } } // 处理相机权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == RequestCameraPermission) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { try { cameraSource.start(cameraView.getHolder()); } catch (IOException e) { e.printStackTrace(); } } } } }
关键修改点说明
- 移除原GMS的
TextRecognizer及setProcessor逻辑,改用ML Kit的TextRecognition.getClient()创建识别器 - 创建
CameraSource时不再传入Detector,改为传入null - 添加
PreviewCallback获取相机预览帧,将帧数据转换为ML Kit要求的InputImage(注意处理屏幕旋转角度,确保识别方向正确) - 在ML Kit的
onSuccess回调中遍历TextBlock拼接文本,主线程更新TextView - 在
onDestroy中释放ML Kit识别器和CameraSource资源
内容的提问来源于stack exchange,提问作者Clint William Theron
相关产品推荐
相关产品推荐

