OCR-сканер Card2Contact | Кодементор
Это демонстрационное учебное приложение для создания сканера OCR для визитных карточек и сохранения информации в списке контактов телефона. В этом случае я буду ориентироваться только на систему iOS, но это можно сделать и для Android. я звоню в это приложение Card2Контакты и это часть Аламеда Дев Исследования
Основные пакеты, которые нам понадобятся, это пакет для чтения изображений с камеры (react-native-camera).
Создание базового приложения React Native
react-native init Card2Contact
Установить зависимости
yarn add react-native-camera --save
Настройте пакет реактивной камеры
react-native link react-native-camera
Для iOS мы должны установить разрешения.
в Информация.plist файл добавить описание для NSCameraUsageОписание ключ. Это требуется Apple, и в нем говорится, почему вам нужен доступ к камере.
Вам также нужно NSPhotoLibraryUsageDescription клавиша для сохранения изображения в библиотеке телефона.
<key>NSCameraUsageDescription</key>
<string>This app needs permission to access your phone camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app store photos in your phone library.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app store photos in your phone library.</string>
Основной экран компонента камеры
import React, {Component} from 'react';
import {StyleSheet, Dimensions, Text, View} from 'react-native';
import Camera from 'react-native-camera';
export default class App extends Component<Props> {
takePicture() {
this.camera.capture()
.then((data) => console.log(data))
.catch(err => console.error(err));
}
render() {
return (
<View style={styles.container}>
<Camera
ref={cam => { this.camera = cam }}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}> [CAPTURE CARD]</Text>
</Camera>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
height: Dimensions.get('window').height,
width: Dimensions.get('window').width
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});
Добавить навигацию, чтобы показать экран фотографий для обработки
yarn add react-navigation --save
yarn add react-native-gesture-handler --save
react-native link react-native-gesture-handler
Реорганизуйте код, чтобы добавить навигацию
App.js
import React, {Component} from 'react';
import {StyleSheet, Dimensions, Text, View} from 'react-native';
import CameraScreen from './src/screens/CameraScreen'
import ContactScreen from './src/screens/ContactScreen'
import {createStackNavigator, createAppContainer} from 'react-navigation';
const AppNavigator = createStackNavigator({
CameraScreen: {
screen: CameraScreen
},
ContactScreen: {
screen: ContactScreen
}
});
const AppContainer = createAppContainer(AppNavigator);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
CameraScreen.js
import React, { Component } from 'react';
import { StyleSheet, Dimensions, Text, View } from 'react-native';
import { RNCamera } from 'react-native-camera';
export default class CameraScreen extends Component {
takePicture = async function() {
if (this.camera) {
const options = { quality: 0.5, base64: true };
const data = await this.camera.takePictureAsync(options)
console.log(data.uri);
}
};
render() {
return (
<View style={styles.container}>
<RNCamera
ref={cam => { this.camera = cam }}
style={styles.preview}
>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}> [CAPTURE CARD]</Text>
</RNCamera>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
height: Dimensions.get('window').height,
width: Dimensions.get('window').width
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});
ContactScreen.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class ContactScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>ContactScreen</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
У Google есть отличный API для обнаружения и извлечения текста (OCR), который мы можем использовать для этой цели. Для использования этого API вам необходимо создать Проект Google Cloud Platform (GCP)
Перейдите на веб-страницу и создайте новый проект
После этого перейдите в свой проект и создайте API учетных данных для использования в вашем проекте React Native.
Скопируйте свой ключ API и сохраните его в надежном месте.
Давайте добавим Vision API для этого проекта
Найдите Vision API и нажмите ВКЛЮЧИТЬ.
Не забудьте включить биллинг для проектов, так как это НЕОБХОДИМО.
Код для обнаружения текста
Давайте изменим код в CameraScreen.js файл и добавьте новый метод для подключения к Google Vision API.
Конечная точка API для Google Vision: И вам нужно передать ключ для аутентификации.
CameraScreen.js
takePicture = async function() {
if (this.camera) {
const options = { quality: 0.5, base64: true };
const data = await this.camera.takePictureAsync(options)
this.detectText(data.base64)
}
};
detectText(base64){
fetch("?key=" + "YOUR API KEY", {
method: 'POST',
body: JSON.stringify({
"requests": [{
"image": { "content": base64 },
"features": [
{ "type": "TEXT_DETECTION" }
]}]
})
})
.then(response => { return response.json()})
.then(jsonRes => {
let text = jsonRes.responses[0].fullTextAnnotation.text
this.props.navigation.navigate('ContactScreen', { text: text })
}).catch(err => {
console.log('Error', err)
})
}
ConatctScreen.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class ContactScreen extends Component {
constructor(props){
super(props)
const { navigation } = this.props
const text = navigation.getParam('text')
const lines = text.match(/[^\r\n]+/g)
this.state = {
text: text,
lines: lines
}
console.log('text', text)
console.log('lines', lines)
}
render() {
return (
<View style={styles.container}>
<Text>ContactScreen</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Давайте отобразим список текста, возвращаемый API Google, и вместе с ним форму для выбора типа текста, чтобы классифицировать его для контакта.
npm install react-native-picker-select
А также добавить внизу кнопку для сохранения информации.
import React, { Component } from 'react';
import { StyleSheet, Text, View, ScrollView, TouchableOpacity} from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
export default class ContactScreen extends Component {
static navigationOptions = {
title: 'New Contact'
}
constructor(props){
super(props)
const { navigation } = this.props
const text = navigation.getParam('text')
const lines = text.match(/[^\r\n]+/g)
this.state = {
text: text,
lines: lines,
contact: {
familyName: null,
givenName: null,
phoneNumbers: [],
emailAddresses: [],
company: null
},
lineTypes: [
{label: 'Last Name', value: 'familyName'},
{label: 'First Name', value: 'givenName'},
{label: 'Phone Number', value: 'phoneNumbers'},
{label: 'Email Address', value: 'emailAddresses'},
{label: 'Company', value: 'company'}
]
}
this.selectedType = this.selectedType.bind(this)
}
selectedType(type, value) {
var updatedContact = Object.assign({}, this.state.contact)
if (type == 'phoneNumbers') {
updatedContact[type].push({label: "mobile", number: value.replace(/ /g,'')})
} else if (type == 'emailAddresses'){
updatedContact[type].push({label: "work", email: value.replace(/ /g,'')})
} else {
updatedContact[type] = value
}
this.setState({contact:updatedContact});
}
render() {
console.log('state', this.state)
return (
<View style={{ flex:1 }}>
<ScrollView style={styles.container}>
{
this.state.lines.map((line, index) => {
return (
<View style={styles.lineContainer} key={index}>
<Text style={styles.lineText}>{line}</Text>
<View style={styles.lineType}>
<RNPickerSelect
placeholder={{
label: 'Select type or nothing to discard',
value: null
}}
onValueChange={(type) => this.selectedType(type, line)}
items={this.state.lineTypes}
style={styles.picker}
/>
</View>
</View>
)
})
}
</ScrollView>
<TouchableOpacity
style={styles.saveButton}
>
<Text>Save Contact</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
lineContainer: {
flex: 1,
borderColor: '#666666',
borderWidth: 1,
padding: 10
},
picker: {
fontSize: 16,
borderWidth: 1,
borderColor: 'gray',
borderRadius: 4,
backgroundColor: 'white',
color: 'black',
},
saveButton: {
alignContent: 'center',
alignSelf: 'center',
paddingVertical: 15
}
});
npm install react-native-contacts --save
react-native link
Добавьте специальные ключи «разрешения» набора в свой Xcode. Информация.plist файл, чтобы заставить requestPermission работать. В противном случае ваше приложение аварийно завершает работу при запросе определенного разрешения.
Откройте Xcode> откройте ios/yourApp.xcodeproj> Info.plist> Добавить ключ Конфиденциальность — Описание использования контактов с вашим специальным разрешением. Значение ключа является необязательным при разработке. Если вы отправляете в App Store, значение должно объяснять, почему вам нужно это разрешение.
saveContact() {
let contact = this.state.contact
if (contact.givenName == null || contact.phoneNumbers.length == 0) {
alert('Select at least an option for Name and Phone')
} else {
Contacts.openContactForm(this.state.contact, (err) => {
if (err) {
alert('Select an option')
}
this.props.navigation.navigate('ContactScreen', { text: this.state.lines })
})
}
}
И мы закончили!!