In this post, we will deal with installing react native camera, hassle-free into an android app and will configure it to use Firebase MLKit for face detection
Before we start integrating we assume that you have a react native app with firebase integrated. If not then please go through creating firebase project and How to integrate it into react native android. Once you have set up a firebase project and linked it into react native android app follow this post for integrating react native camera and detecting faces.
Install React Native Camera
We will use the master branch of React Native Camera.
STEP1: Run the following command in the terminal within your project.
//npm if you use npm npm install --save react-native-camera@git+https://git@github.com/react-native-community/react-native-camera.git // yarn if you use yarn . use only of of the two yarn add react-native-camera@git+https://git@github.com/react-native-community/react-native-camera.git
STEP2: If you have used npm then run
react-native link react-native-camera
STEP3: In android -> app -> build.gradle add following line
defaultConfig { ... missingDimensionStrategy 'react-native-camera', 'mlkit' }
Step4: In android -> app -> src -> main -> AndroidManifest.xml add folloing line
... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="face" /> ... </application>
Notes for beginners -> default config and application tags will be there already you just need to add the content given above don’t recreate them.
Save your files and run react-native run-android
make sure device is connected
Your project should build without any problem.
Once done edit your project’s App.js
import { RNCamera } from 'react-native-camera'; //...in render() return the following jsx render() { return(<RNCamera style={{flex:1}} type='front' onFacesDetected={(faceArray)=>{ console.log(faceArray)}} >); }
Now when you launch your app front cam should be open and after detecting your face it will call onFacesDetected
.
React Native camera provides lots of props. Try their example code. The entire example project might not build so you can paste their code in your app.js to see the result
/* eslint-disable no-console */ import React from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Slider } from 'react-native'; // eslint-disable-next-line import/no-unresolved import { RNCamera } from 'react-native-camera'; const flashModeOrder = { off: 'on', on: 'auto', auto: 'torch', torch: 'off', }; const wbOrder = { auto: 'sunny', sunny: 'cloudy', cloudy: 'shadow', shadow: 'fluorescent', fluorescent: 'incandescent', incandescent: 'auto', }; const landmarkSize = 2; export default class CameraScreen extends React.Component { state = { flash: 'off', zoom: 0, autoFocus: 'on', depth: 0, type: 'back', whiteBalance: 'auto', ratio: '16:9', recordOptions: { mute: false, maxDuration: 5, quality: RNCamera.Constants.VideoQuality['288p'], }, isRecording: false, canDetectFaces: false, canDetectText: false, canDetectBarcode: false, faces: [], textBlocks: [], barcodes: [], }; toggleFacing() { this.setState({ type: this.state.type === 'back' ? 'front' : 'back', }); } toggleFlash() { this.setState({ flash: flashModeOrder[this.state.flash], }); } toggleWB() { this.setState({ whiteBalance: wbOrder[this.state.whiteBalance], }); } toggleFocus() { this.setState({ autoFocus: this.state.autoFocus === 'on' ? 'off' : 'on', }); } zoomOut() { this.setState({ zoom: this.state.zoom - 0.1 < 0 ? 0 : this.state.zoom - 0.1, }); } zoomIn() { this.setState({ zoom: this.state.zoom + 0.1 > 1 ? 1 : this.state.zoom + 0.1, }); } setFocusDepth(depth) { this.setState({ depth, }); } takePicture = async function() { if (this.camera) { const data = await this.camera.takePictureAsync(); console.warn('takePicture ', data); } }; takeVideo = async function() { if (this.camera) { try { const promise = this.camera.recordAsync(this.state.recordOptions); if (promise) { this.setState({ isRecording: true }); const data = await promise; this.setState({ isRecording: false }); console.warn('takeVideo', data); } } catch (e) { console.error(e); } } }; toggle = value => () => this.setState(prevState => ({ [value]: !prevState[value] })); facesDetected = ({ faces }) => this.setState({ faces }); renderFace = ({ bounds, faceID, rollAngle, yawAngle }) => ( <View key={faceID} transform={[ { perspective: 600 }, { rotateZ: `${rollAngle.toFixed(0)}deg` }, { rotateY: `${yawAngle.toFixed(0)}deg` }, ]} style={[ styles.face, { ...bounds.size, left: bounds.origin.x, top: bounds.origin.y, }, ]} > <Text style={styles.faceText}>ID: {faceID}</Text> <Text style={styles.faceText}>rollAngle: {rollAngle.toFixed(0)}</Text> <Text style={styles.faceText}>yawAngle: {yawAngle.toFixed(0)}</Text> </View> ); renderLandmarksOfFace(face) { const renderLandmark = position => position && ( <View style={[ styles.landmark, { left: position.x - landmarkSize / 2, top: position.y - landmarkSize / 2, }, ]} /> ); return ( <View key={`landmarks-${face.faceID}`}> {renderLandmark(face.leftEyePosition)} {renderLandmark(face.rightEyePosition)} {renderLandmark(face.leftEarPosition)} {renderLandmark(face.rightEarPosition)} {renderLandmark(face.leftCheekPosition)} {renderLandmark(face.rightCheekPosition)} {renderLandmark(face.leftMouthPosition)} {renderLandmark(face.mouthPosition)} {renderLandmark(face.rightMouthPosition)} {renderLandmark(face.noseBasePosition)} {renderLandmark(face.bottomMouthPosition)} </View> ); } renderFaces = () => ( <View style={styles.facesContainer} pointerEvents="none"> {this.state.faces.map(this.renderFace)} </View> ); renderLandmarks = () => ( <View style={styles.facesContainer} pointerEvents="none"> {this.state.faces.map(this.renderLandmarksOfFace)} </View> ); renderTextBlocks = () => ( <View style={styles.facesContainer} pointerEvents="none"> {this.state.textBlocks.map(this.renderTextBlock)} </View> ); renderTextBlock = ({ bounds, value }) => ( <React.Fragment key={value + bounds.origin.x}> <Text style={[styles.textBlock, { left: bounds.origin.x, top: bounds.origin.y }]}> {value} </Text> <View style={[ styles.text, { ...bounds.size, left: bounds.origin.x, top: bounds.origin.y, }, ]} /> </React.Fragment> ); textRecognized = object => { const { textBlocks } = object; this.setState({ textBlocks }); }; barcodeRecognized = ({ barcodes }) => this.setState({ barcodes }); renderBarcodes = () => ( <View style={styles.facesContainer} pointerEvents="none"> {this.state.barcodes.map(this.renderBarcode)} </View> ); renderBarcode = ({ bounds, data, type }) => ( <React.Fragment key={data + bounds.origin.x}> <View style={[ styles.text, { ...bounds.size, left: bounds.origin.x, top: bounds.origin.y, }, ]} > <Text style={[styles.textBlock]}>{`${data} ${type}`}</Text> </View> </React.Fragment> ); renderCamera() { const { canDetectFaces, canDetectText, canDetectBarcode } = this.state; return ( <RNCamera ref={ref => { this.camera = ref; }} style={{ flex: 1, }} type={this.state.type} flashMode={this.state.flash} autoFocus={this.state.autoFocus} zoom={this.state.zoom} whiteBalance={this.state.whiteBalance} ratio={this.state.ratio} focusDepth={this.state.depth} trackingEnabled androidCameraPermissionOptions={{ title: 'Permission to use camera', message: 'We need your permission to use your camera', buttonPositive: 'Ok', buttonNegative: 'Cancel', }} faceDetectionLandmarks={ RNCamera.Constants.FaceDetection.Landmarks ? RNCamera.Constants.FaceDetection.Landmarks.all : undefined } faceDetectionClassifications={ RNCamera.Constants.FaceDetection.Classifications ? RNCamera.Constants.FaceDetection.Classifications.all : undefined } onFacesDetected={canDetectFaces ? this.facesDetected : null} onTextRecognized={canDetectText ? this.textRecognized : null} onGoogleVisionBarcodesDetected={canDetectBarcode ? this.barcodeRecognized : null} googleVisionBarcodeType={RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeType.ALL} > <View style={{ flex: 0.5, }} > <View style={{ backgroundColor: 'transparent', flexDirection: 'row', justifyContent: 'space-around', }} > <TouchableOpacity style={styles.flipButton} onPress={this.toggleFacing.bind(this)}> <Text style={styles.flipText}> FLIP </Text> </TouchableOpacity> <TouchableOpacity style={styles.flipButton} onPress={this.toggleFlash.bind(this)}> <Text style={styles.flipText}> FLASH: {this.state.flash} </Text> </TouchableOpacity> <TouchableOpacity style={styles.flipButton} onPress={this.toggleWB.bind(this)}> <Text style={styles.flipText}> WB: {this.state.whiteBalance} </Text> </TouchableOpacity> </View> <View style={{ backgroundColor: 'transparent', flexDirection: 'row', justifyContent: 'space-around', }} > <TouchableOpacity onPress={this.toggle('canDetectFaces')} style={styles.flipButton}> <Text style={styles.flipText}> {!canDetectFaces ? 'Detect Faces' : 'Detecting Faces'} </Text> </TouchableOpacity> <TouchableOpacity onPress={this.toggle('canDetectText')} style={styles.flipButton}> <Text style={styles.flipText}> {!canDetectText ? 'Detect Text' : 'Detecting Text'} </Text> </TouchableOpacity> <TouchableOpacity onPress={this.toggle('canDetectBarcode')} style={styles.flipButton}> <Text style={styles.flipText}> {!canDetectBarcode ? 'Detect Barcode' : 'Detecting Barcode'} </Text> </TouchableOpacity> </View> </View> <View style={{ flex: 0.4, backgroundColor: 'transparent', flexDirection: 'row', alignSelf: 'flex-end', }} > <Slider style={{ width: 150, marginTop: 15, alignSelf: 'flex-end' }} onValueChange={this.setFocusDepth.bind(this)} step={0.1} disabled={this.state.autoFocus === 'on'} /> </View> <View style={{ flex: 0.1, backgroundColor: 'transparent', flexDirection: 'row', alignSelf: 'flex-end', }} > <TouchableOpacity style={[ styles.flipButton, { flex: 0.3, alignSelf: 'flex-end', backgroundColor: this.state.isRecording ? 'white' : 'darkred', }, ]} onPress={this.state.isRecording ? () => {} : this.takeVideo.bind(this)} > {this.state.isRecording ? ( <Text style={styles.flipText}> ☕ </Text> ) : ( <Text style={styles.flipText}> REC </Text> )} </TouchableOpacity> </View> {this.state.zoom !== 0 && ( <Text style={[styles.flipText, styles.zoomText]}>Zoom: {this.state.zoom}</Text> )} <View style={{ flex: 0.1, backgroundColor: 'transparent', flexDirection: 'row', alignSelf: 'flex-end', }} > <TouchableOpacity style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]} onPress={this.zoomIn.bind(this)} > <Text style={styles.flipText}> + </Text> </TouchableOpacity> <TouchableOpacity style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]} onPress={this.zoomOut.bind(this)} > <Text style={styles.flipText}> - </Text> </TouchableOpacity> <TouchableOpacity style={[styles.flipButton, { flex: 0.25, alignSelf: 'flex-end' }]} onPress={this.toggleFocus.bind(this)} > <Text style={styles.flipText}> AF : {this.state.autoFocus} </Text> </TouchableOpacity> <TouchableOpacity style={[styles.flipButton, styles.picButton, { flex: 0.3, alignSelf: 'flex-end' }]} onPress={this.takePicture.bind(this)} > <Text style={styles.flipText}> SNAP </Text> </TouchableOpacity> </View> {!!canDetectFaces && this.renderFaces()} {!!canDetectFaces && this.renderLandmarks()} {!!canDetectText && this.renderTextBlocks()} {!!canDetectBarcode && this.renderBarcodes()} </RNCamera> ); } render() { return <View style={styles.container}>{this.renderCamera()}</View>; } } const styles = StyleSheet.create({ container: { flex: 1, paddingTop: 10, backgroundColor: '#000', }, flipButton: { flex: 0.3, height: 40, marginHorizontal: 2, marginBottom: 10, marginTop: 10, borderRadius: 8, borderColor: 'white', borderWidth: 1, padding: 5, alignItems: 'center', justifyContent: 'center', }, flipText: { color: 'white', fontSize: 15, }, zoomText: { position: 'absolute', bottom: 70, zIndex: 2, left: 2, }, picButton: { backgroundColor: 'darkseagreen', }, facesContainer: { position: 'absolute', bottom: 0, right: 0, left: 0, top: 0, }, face: { padding: 10, borderWidth: 2, borderRadius: 2, position: 'absolute', borderColor: '#FFD700', justifyContent: 'center', backgroundColor: 'rgba(0, 0, 0, 0.5)', }, landmark: { width: landmarkSize, height: landmarkSize, position: 'absolute', backgroundColor: 'red', }, faceText: { color: '#FFD700', fontWeight: 'bold', textAlign: 'center', margin: 10, backgroundColor: 'transparent', }, text: { padding: 10, borderWidth: 2, borderRadius: 2, position: 'absolute', borderColor: '#F00', justifyContent: 'center', }, textBlock: { color: '#F00', position: 'absolute', textAlign: 'center', backgroundColor: 'transparent', }, });
Hope it helped you God Speed !!!