【实战教程】React Native项目集成Google ML Kit实现离线水表OCR识别

前言

在移动应用开发中,OCR(光学字符识别)技术广泛应用于各类场景。本文将详细介绍如何在React Native项目中集成Google ML Kit,实现离线水表数字识别功能。全程使用TypeScript,并针对React Native 0.74版本进行适配,解决版本兼容性问题。

技术栈

  • React Native: v0.74
  • TypeScript/TSX
  • Google ML Kit
  • React Native Image Picker
  • Native Base UI组件库

1. 安装必要依赖

首先,需要安装相关依赖包:

bash 复制代码
# 安装ML Kit文本识别包
yarn add @react-native-ml-kit/text-recognition

# 安装文件处理包(用于图像处理)
yarn add react-native-fs

2. Android项目配置

2.1 修改build.gradle

打开android/app/build.gradle,添加以下配置:

gradle 复制代码
android {
    defaultConfig {
        // 其他配置...
        
        // ML Kit配置
        missingDimensionStrategy 'react-native-camera', 'general'
    }
    
    packagingOptions {
        pickFirst '**/*.so'
    }
}

dependencies {
    // 其他依赖...
    
    // 添加离线文本识别模型
    implementation 'com.google.mlkit:text-recognition:16.0.0'
}

2.2 配置AndroidManifest.xml

确保在AndroidManifest.xml中添加相机权限:

xml 复制代码
<manifest ... >
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
    
    <!-- 其他配置... -->
</manifest>

3. 核心代码实现

3.1 导入相关模块

typescript 复制代码
// OcrDemo.tsx
import React, { useState, useEffect } from 'react';
import {
  VStack, Button, Image, Text, Box, Spinner, HStack, Icon, useToast
} from 'native-base';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
import { Platform, PermissionsAndroid } from 'react-native';
// 关键导入:ML Kit文本识别
import TextRecognition from '@react-native-ml-kit/text-recognition';

3.2 相机权限请求函数

typescript 复制代码
// 请求相机权限
const requestCameraPermission = async () => {
  if (Platform.OS === 'android') {
    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.CAMERA,
        {
          title: "需要相机权限",
          message: "应用需要使用您的相机以拍摄水表照片",
          buttonNeutral: "稍后询问",
          buttonNegative: "取消",
          buttonPositive: "确定"
        }
      );
      return granted === PermissionsAndroid.RESULTS.GRANTED;
    } catch (err) {
      console.warn(err);
      return false;
    }
  }
  return true;
};

3.3 OCR识别核心函数

typescript 复制代码
// 使用ML Kit进行OCR识别
const performOcrRecognition = async (imagePath: string) => {
  try {
    console.log('开始OCR识别,图像路径:', imagePath);
    
    // 处理图像路径,确保使用file:// URI格式
    let correctPath = imagePath;
    if (Platform.OS === 'android' && !imagePath.startsWith('file://')) {
      correctPath = `file://${imagePath}`;
    }
    
    console.log('使用的图像路径:', correctPath);
    
    // 调用TextRecognition,使用离线模式
    const result = await TextRecognition.recognize(correctPath);
    
    console.log('OCR结果:', result);
    
    // 处理OCR结果,提取数字
    let meterReading = extractMeterReading(result);
    return meterReading;
  } catch (error) {
    console.error('OCR处理错误:', error);
    throw error;
  }
};

3.4 提取水表读数函数

typescript 复制代码
// 提取水表读数
const extractMeterReading = (ocrResult: any) => {
  // 从OCR结果中提取所有数字序列
  let allDigitSequences: string[] = [];
  
  // 检查结果是否有效
  if (!ocrResult || !ocrResult.blocks || !Array.isArray(ocrResult.blocks)) {
    console.log('OCR结果无效或没有文本块');
    return '';
  }
  
  // 遍历识别到的所有文本块
  ocrResult.blocks.forEach((block: any) => {
    if (block.lines && Array.isArray(block.lines)) {
      // 遍历每个块中的每一行
      block.lines.forEach((line: any) => {
        if (line && line.text) {
          // 获取这一行的完整文本
          const lineText = line.text;
          
          // 使用正则表达式提取连续的数字序列(可能包含小数点)
          const digitRegex = /\d+(\.\d+)?/g;
          const matches = lineText.match(digitRegex);
          
          if (matches) {
            allDigitSequences = [...allDigitSequences, ...matches];
          }
        }
      });
    }
  });
  
  console.log('提取的数字序列:', allDigitSequences);
  
  // 如果找到了数字序列,尝试确定哪一个是水表读数
  if (allDigitSequences.length > 0) {
    // 筛选策略:选择符合水表读数特征的数字
    // 水表读数通常是5-8位数字,可能带小数点
    const potentialReadings = allDigitSequences.filter(seq => {
      // 检查是否符合水表读数的格式(例如:5-8位数字,可能有1位小数)
      return /^\d{5,8}(\.\d{1})?$/.test(seq);
    });
    
    // 如果有符合条件的读数,返回第一个
    if (potentialReadings.length > 0) {
      return potentialReadings[0];
    }
    
    // 如果没有符合特定条件的,就返回最长的数字序列
    return allDigitSequences.sort((a, b) => b.length - a.length)[0];
  }
  
  // 如果没有找到任何数字序列,返回空字符串
  return '';
};

3.5 处理识别结果

当识别出多个可能结果时,让用户选择正确读数:

tsx 复制代码
// 添加状态存储多个可能的读数
const [potentialReadings, setPotentialReadings] = useState<string[]>([]);
const [selectedReadingIndex, setSelectedReadingIndex] = useState<number>(-1);

// 处理识别结果
const handleRecognizeResult = async (result: any) => {
  // 提取所有可能的水表读数
  const allDigits = extractAllDigitSequences(result);
  
  // 根据特征筛选可能的水表读数(5-8位数字等)
  const candidates = filterPotentialReadings(allDigits);
  
  if (candidates.length === 0) {
    // 没有找到符合条件的读数
    toast.show({
      title: "未识别到读数",
      description: "请尝试重新拍摄清晰的水表照片",
      status: "warning"
    });
    return;
  } else if (candidates.length === 1) {
    // 只有一个结果,直接使用
    setRecognizedText(candidates[0]);
  } else {
    // 多个可能结果,展示给用户选择
    setPotentialReadings(candidates);
  }
};

3.6 用户选择UI组件

tsx 复制代码
// 用户选择UI
{potentialReadings.length > 0 && (
  <VStack space={3} width="90%" mt={4}>
    <Text fontSize="md" fontWeight="medium">
      检测到多个可能的读数,请选择正确的:
    </Text>
    
    {potentialReadings.map((reading, index) => (
      <Button
        key={index}
        variant={selectedReadingIndex === index ? "solid" : "outline"}
        colorScheme="orange"
        onPress={() => {
          setSelectedReadingIndex(index);
          setRecognizedText(reading);
        }}
      >
        {reading}
      </Button>
    ))}
  </VStack>
)}

4. 关键问题与解决方案

4.1 ML Kit模块无法识别问题

问题:导入后TextRecognition报undefined错误

解决方案

typescript 复制代码
// 正确的导入方式
import TextRecognition from '@react-native-ml-kit/text-recognition';
// 错误的导入方式
// import { TextRecognition } from '@react-native-ml-kit/text-recognition';

4.2 图片路径格式问题

问题:Android中图片路径需要以"file://"开头

解决方案

typescript 复制代码
// 处理图像路径,确保使用file:// URI格式
let correctPath = imagePath;
if (Platform.OS === 'android' && !imagePath.startsWith('file://')) {
  correctPath = `file://${imagePath}`;
}

4.3 多结果处理

问题:水表上存在多个数字区域导致识别混乱

解决方案

typescript 复制代码
// 筛选策略:选择符合水表读数特征的数字
const potentialReadings = allDigitSequences.filter(seq => {
  // 水表读数通常是5-8位数字,可能有1位小数
  return /^\d{5,8}(\.\d{1})?$/.test(seq);
});

5. 完整集成效果

通过以上步骤,我们成功实现了:

  1. Google ML Kit的离线OCR识别功能集成
  2. 相机拍照和相册选择功能
  3. 水表读数的智能提取与筛选
  4. 多结果的用户选择机制

此解决方案完全支持离线使用,无需网络连接即可工作。

总结

本文详细介绍了在React Native项目中集成Google ML Kit实现水表OCR识别的完整流程,包括环境配置、核心代码实现和关键问题解决方案。希望对大家有所帮助!

如有任何问题或建议,欢迎在评论区留言交流!

相关推荐
桥豆麻袋939310 小时前
vite 初始化react项目
前端·react.js·前端框架
HelloRevit11 小时前
React -> AI组件 -> 调用Ollama模型, qwen3:1.7B非常聪明
前端·react.js·前端框架
1nv1s1ble12 小时前
React 笔记[1] hello world
前端·笔记·react.js
程序猿阿伟12 小时前
《社交应用架构生存战:React Native与Flutter的部署容灾决胜法则》
flutter·react native·架构
Jenna的海糖12 小时前
ts axios中报 Property ‘code‘ does not exist on type ‘AxiosResponse<any, any>‘
前端·vue.js·react.js·typescript
小江-1 天前
基于计算机视觉的试卷答题区表格识别与提取技术
python·ocr
TextIn智能文档云平台1 天前
TextIn ParseX重磅功能更新:支持切换公式输出形式、表格解析优化、新增电子档PDF去印章
java·图像处理·人工智能·算法·自然语言处理·pdf·ocr
果冻kk1 天前
【实战教程】零基础搭建DeepSeek大模型聊天系统 - Spring Boot+React完整开发指南
spring boot·后端·react.js·deepseek
流星雨在线2 天前
react naive 网络框架源码解析
网络·react native