前言
在移動應用開發中,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. 安裝必要依賴
首先,需要安裝相關依賴包:
# 安裝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
,添加以下配置:
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
中添加相機權限:
<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 導入相關模塊
// 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 相機權限請求函數
// 請求相機權限
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識別核心函數
// 使用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 提取水表讀數函數
// 提取水表讀數
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 處理識別結果
當識別出多個可能結果時,讓用戶選擇正確讀數:
// 添加狀態存儲多個可能的讀數
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組件
// 用戶選擇UI
{potentialReadings.length > 0 && (<VStack space={3} width="90%" mt={4}><Text fontSize="md" fontWeight="medium">檢測到多個可能的讀數,請選擇正確的:</Text>{potentialReadings.map((reading, index) => (<Buttonkey={index}variant={selectedReadingIndex === index ? "solid" : "outline"}colorScheme="orange"onPress={() => {setSelectedReadingIndex(index);setRecognizedText(reading);}}>{reading}</Button>))}</VStack>
)}
4. 關鍵問題與解決方案
4.1 ML Kit模塊無法識別問題
問題:導入后TextRecognition報undefined錯誤
解決方案:
// 正確的導入方式
import TextRecognition from '@react-native-ml-kit/text-recognition';
// 錯誤的導入方式
// import { TextRecognition } from '@react-native-ml-kit/text-recognition';
4.2 圖片路徑格式問題
問題:Android中圖片路徑需要以"file://"開頭
解決方案:
// 處理圖像路徑,確保使用file:// URI格式
let correctPath = imagePath;
if (Platform.OS === 'android' && !imagePath.startsWith('file://')) {correctPath = `file://${imagePath}`;
}
4.3 多結果處理
問題:水表上存在多個數字區域導致識別混亂
解決方案:
// 篩選策略:選擇符合水表讀數特征的數字
const potentialReadings = allDigitSequences.filter(seq => {// 水表讀數通常是5-8位數字,可能有1位小數return /^\d{5,8}(\.\d{1})?$/.test(seq);
});
5. 完整集成效果
通過以上步驟,我們成功實現了:
- Google ML Kit的離線OCR識別功能集成
- 相機拍照和相冊選擇功能
- 水表讀數的智能提取與篩選
- 多結果的用戶選擇機制
此解決方案完全支持離線使用,無需網絡連接即可工作。
總結
本文詳細介紹了在React Native項目中集成Google ML Kit實現水表OCR識別的完整流程,包括環境配置、核心代碼實現和關鍵問題解決方案。希望對大家有所幫助!
如有任何問題或建議,歡迎在評論區留言交流!