{"id":1016,"date":"2020-07-09T15:30:27","date_gmt":"2020-07-09T15:30:27","guid":{"rendered":"https:\/\/www.confianzit.com\/cit-blog\/?p=1016"},"modified":"2022-10-31T17:53:28","modified_gmt":"2022-10-31T17:53:28","slug":"rectangle-detection-and-perspective-correction-in-react-native","status":"publish","type":"post","link":"https:\/\/www.confianzit.com\/cit-blog\/rectangle-detection-and-perspective-correction-in-react-native\/","title":{"rendered":"Rectangle Detection and Perspective Correction in React Native"},"content":{"rendered":"\n\n[et_pb_section admin_label=&#8221;section&#8221;]\r\n\t\t\t[et_pb_row admin_label=&#8221;row&#8221;]\r\n\t\t\t\t[et_pb_column type=&#8221;4_4&#8243;][et_pb_text admin_label=&#8221;Text&#8221;]<!-- divi:paragraph -->\n<p>Since the age of artificial intelligence and machine learning, one of the most important applications to which they have been applied is for text detection and image processing, creating the field of computer vision. While computer vision was almost arcane in the old days and developing applications related to the same required much effort and deeper understanding of many mathematical concepts, nowadays it is used almost everywhere, thanks to the development of the famous OpenCV library by Intel.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>Most of the applications for image processing can be handled well with the OpenCV library, which is commonly used with Python to handle complex cases, and the source for the library is also available in other platforms. In the context of mobile application development, OpenCV has provided libraries for both Android and iOS.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>One of the widely used applications of OpenCV is to detect a document and possibly apply perspective correction to get a clear view of the document. This is quite easy in OpenCV and we could use built-in functions such as getPerspectiveTransform(src, dst). While this works out great when developing native applications, if the app is developed in a cross-platform ecosystem using frameworks such as React Native, this won\u2019t be ideal. This is because we will have to integrate OpenCV in the native source files of the React Native project and write native code to implement the same which is used in React Native. This defeats the entire purpose of cross platform development and requires specialized knowledge in native development which brings us back to square one. Thankfully due to the massive adoption of React Native over the recent years and the open source community we now have alternatives available as plugins to use like react-native-rectangle-scanner. So, let\u2019s see how to use this plugin to implement a simple app in React Native.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p><strong>Getting Started<\/strong><\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:image {\"align\":\"center\"} -->\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/cOB4MLBnUZtmflXeZ38yvgX5EFsP-HEO4FAeIBhb4EKQaieV4elg_G0PaAeBNxSQGY1d8rhgkL9d7CuTpWynusaux1tosLjc8YDfyUMb9jDxXu6zP_MLcGJA5OPqXezefrIWgRIv03zOKXUfzA\" alt=\"\" \/><\/figure><\/div>\n<!-- \/divi:image -->\n\n<!-- divi:paragraph -->\n<p>Let\u2019s create a sample application to detect a rectangle document and show it to the user with the perspective correction applied as shown above.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>Using the react-native-cli tool let\u2019s initialize an empty project. This can also be done using expo and really is up to personal preference. The npx command allows you to use the latest version of the React Native.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>npx react-native init RectangleScanner<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>In the <em>package.json<\/em> file add the plugin along with two more dependencies which will be used to render the UI icons later for our app as shown below.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>..\n\"dependencies\": {\n   \"react\": \"16.11.0\",\n   \"react-native\": \"0.62.2\",\n   \"react-native-rectangle-scanner\": \"^1.0.10\",\n   \"react-native-svg\": \"^12.1.0\",\n   \"react-native-vector-icons\": \"^6.6.0\"\n\t..<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>Run yarn install to pull in the dependencies added above.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p><strong>Configuring Android Settings<\/strong><\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>Now there are some slight additions to be made in the Android source directory to get the icons working properly.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>In<strong> <\/strong><em>android\/app\/src\/main\/AndroidManifest.xml<\/em><\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>Add camera permissions request:<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>&lt;uses-permission android:name=\"android.permission.CAMERA\" \/&gt;<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>Update settings.gradle file as the following to link openCV and Vector Icons.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>..\ninclude ':app'\n \ninclude ':react-native-vector-icons'\nproject(':react-native-vector-icons').projectDir = new File(rootProject.projectDir,\n '..\/node_modules\/react-native-vector-icons\/android')\n \ninclude ':openCVLibrary310'\nproject(':openCVLibrary310').projectDir = new File(rootProject.projectDir,'..\/node_modules\/react-native-rectangle-scanne\nr\/android\/openCVLibrary310')\n\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p><strong>Diving into Code<\/strong><\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>We will begin by initializing a stateful React Component and define the <em>propTypes<\/em> for the props that we will be using later and initialize the state variables. We then utilize the createRef provided by React to initialize a reference and assign it to the camera variable, which will be later used for camera actions.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>import { PropTypes } from 'prop-types';\n \nexport default class DocumentScanner extends React.Component {\n static propTypes = {\n   cameraIsOn: PropTypes.bool,\n   onLayout: PropTypes.func,\n   onPictureTaken: PropTypes.func,\n   onPictureProcessed: PropTypes.func\n }\n \n static defaultProps = {\n   cameraIsOn: undefined, \/\/ Whether camera is on or off\n   onLayout: () =&gt; { }, \/\/ Invokes when the camera layout is initialized\n   onPictureTaken: () =&gt; { }, \/\/ Invokes when the picture is taken\n   onPictureProcessed: () =&gt; { } \/\/ Invokes when the picture is taken and cached.\n }\n \n constructor(props) {\n   super(props);\n   this.state = {\n     flashEnabled: false,\n     showScannerView: false,\n     didLoadInitialLayout: false,\n     detectedRectangle: false,\n     isMultiTasking: false,\n     loadingCamera: true,\n     processingImage: false,\n     takingPicture: false,\n     overlayFlashOpacity: new Animated.Value(0),\n     device: {\n       initialized: false,\n       hasCamera: false,\n       permissionToUseCamera: false,\n       flashIsAvailable: false,\n       previewHeightPercent: 1,\n       previewWidthPercent: 1,\n     },\n   };\n \n   this.camera = React.createRef();\n   this.imageProcessorTimeout = null;\n }\n..\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>We define a function called onDeviceSetup() which basically retrieves information from the platform such as whether the device permissions for the camera are set, the aspect ratio when the preview is generated, etc.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code> onDeviceSetup = (deviceDetails) =&gt; {\n   const {\n     hasCamera, permissionToUseCamera, flashIsAvailable, previewHeightPercent, previewWidthPercent,\n   } = deviceDetails;\n   this.setState({\n     loadingCamera: false,\n     device: {\n       initialized: true,\n       hasCamera,\n       permissionToUseCamera,\n       flashIsAvailable,\n       previewHeightPercent: previewHeightPercent || 1,\n       previewWidthPercent: previewWidthPercent || 1,\n     },\n   });\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>To notify various errors that may arise when accessing the camera&nbsp; getCameraDisabledMessage() is used.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>getCameraDisabledMessage() {\n   if (this.state.isMultiTasking) {\n     return 'Camera is not allowed in multi tasking mode.';\n   }\n \n   const { device } = this.state;\n   if (device.initialized) {\n     if (!device.hasCamera) {\n       return 'Could not find a camera on the device.';\n     }\n     if (!device.permissionToUseCamera) {\n       return 'Permission to use camera has not been granted.';\n     }\n   }\n   return 'Failed to set up the camera.';\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>Create a function turnOnCamera() which will open the scanner view. The turnOffCamera() function similarly hides the camera view, and also if the camera view is on but no camera was found in the device after calling the onDeviceSetup() method, it can optionally uninitialize the camera. The turnOnCamera() will be called each time immediately after a view update occurs.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>turnOnCamera() {\n   if (!this.state.showScannerView) {\n     this.setState({\n       showScannerView: true,\n       loadingCamera: true,\n     });\n   }\n }\n \n turnOffCamera(shouldUninitializeCamera = false) {\n   if (shouldUninitializeCamera &amp;&amp; this.state.device.initialized) {\n     this.setState(({ device }) =&gt; ({\n       showScannerView: false,\n       device: { ...device, initialized: false },\n     }));\n   } else if (this.state.showScannerView) {\n     this.setState({ showScannerView: false });\n   }\n }\n \n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>The turnOnCamera() and turnOffCamera() methods are invoked using lifecycle methods.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>The camera will be turned on inside componentDidMount() only after loading the initial layout when multi-tasking mode is off on iOS devices. Otherwise, the turnOffCamera() function will get invoked from componentDidUpdate(). Also, the imageProcessorTimeout timer which will be set at the time of capture failure should be cleared inside the componentWillUnmount() function.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>componentDidMount() {\n   if (this.state.didLoadInitialLayout &amp;&amp; !this.state.isMultiTasking) {\n     this.turnOnCamera();\n   }\n }\n \n componentDidUpdate() {\n   if (this.state.didLoadInitialLayout) {\n     if (this.state.isMultiTasking) return this.turnOffCamera(true);\n     if (this.state.device.initialized) {\n       if (!this.state.device.hasCamera) return this.turnOffCamera();\n       if (!this.state.device.permissionToUseCamera) return this.turnOffCamera();\n     }\n     if (this.props.cameraIsOn === true &amp;&amp; !this.state.showScannerView) {\n       return this.turnOnCamera();\n     }\n     if (this.props.cameraIsOn === false &amp;&amp; this.state.showScannerView) {\n       return this.turnOffCamera(true);\n     }\n     if (this.props.cameraIsOn === undefined) {\n       return this.turnOnCamera();\n     }\n   }\n   return null;\n }\n \n componentWillUnmount() {\n   clearTimeout(this.imageProcessorTimeout);\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>In some Android devices, the aspect ratio of the preview is different than the screen size which may lead to a distorted camera preview. To deal with this issue write a utility function getPreviewSize()which will take the device height and width into account and return an appropriate preview size for the same.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>getPreviewSize() {\n   const dimensions = Dimensions.get('window');\n   \/\/ We use set margin amounts because for some reasons the percentage values don't align the camera preview in the center correctly.\n   const heightMargin = (1 - this.state.device.previewHeightPercent) * dimensions.height \/ 2;\n   const widthMargin = (1 - this.state.device.previewWidthPercent) * dimensions.width \/ 2;\n   if (dimensions.height &gt; dimensions.width) {\n     \/\/ Portrait\n     return {\n       height: this.state.device.previewHeightPercent,\n       width: this.state.device.previewWidthPercent,\n       marginTop: heightMargin,\n       marginLeft: widthMargin,\n     };\n   }\n   \/\/ Landscape\n   return {\n     width: this.state.device.previewHeightPercent,\n     height: this.state.device.previewWidthPercent,\n     marginTop: widthMargin,\n     marginLeft: heightMargin,\n   };\n }\n \n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>The function triggerSnapAnimation() is used to show the flash animation when the user captures an image.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>triggerSnapAnimation() {\n   Animated.sequence(&#091;\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0.2, duration: 100, useNativeDriver: true }),\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0, duration: 50, useNativeDriver: true }),\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0.6, delay: 100, duration: 120, useNativeDriver: true }),\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0, duration: 90, useNativeDriver: true }),\n   ]).start();\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>The capture() function is used to capture the current frame or the identified rectangle region. The loading or processing state will be set at the time of capturing to prevent any further capture triggers.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>capture = () =&gt; {\n   if (this.state.takingPicture) return;\n   if (this.state.processingImage) return;\n   this.setState({ takingPicture: true, processingImage: true });\n   this.camera.current.capture();\n   this.triggerSnapAnimation();\n \n   \/\/ If capture failed, allow for additional captures\n   this.imageProcessorTimeout = setTimeout(() =&gt; {\n     if (this.state.takingPicture) {\n       this.setState({ takingPicture: false });\n     }\n   }, 100);\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>We will be using the props provided by the plugin to process and cache the image as shown below. Here the image state will be set with the cached image url to be used in the preview.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>\/\/ The picture was captured but still needs to be processed.\n onPictureTaken = (event) =&gt; {\n   this.setState({ takingPicture: false });\n   this.props.onPictureTaken(event);\n }\n \n \/\/ The picture was taken and cached. You can now go on to using it.\n onPictureProcessed = (event) =&gt; {\n   this.props.onPictureProcessed(event);\n   this.setState({\n     image: event,\n     takingPicture: false,\n     processingImage: false,\n     showScannerView: this.props.cameraIsOn || false,\n   });\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>Depending on whether the device has a flashlight or not, the renderFlashControl() function will return a flash icon. To use the icons for the UI we will have to import vector icons as follows.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>import Icon from 'react-native-vector-icons\/Ionicons';<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>renderFlashControl() {\n   const { flashEnabled, device } = this.state;\n   if (!device.flashIsAvailable) return null;\n   return (\n     &lt;TouchableOpacity\n       style={&#091;styles.flashControl, { backgroundColor: flashEnabled ? '#FFFFFF80' : '#00000080' }]}\n       activeOpacity={0.8}\n       onPress={() =&gt; this.setState({ flashEnabled: !flashEnabled })}\n     &gt;\n       &lt;Icon name=\"ios-flashlight\" style={&#091;styles.buttonIcon, { fontSize: 28, color: flashEnabled ? '#333' : '#FFF' }]} \/&gt;\n     &lt;\/TouchableOpacity&gt;\n   );\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>renderCameraControls() returns the camera capture button along with the flash button only when the loading or processing states are off.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>renderCameraControls() {\n   const cameraIsDisabled = this.state.takingPicture || this.state.processingImage;\n   const disabledStyle = { opacity: cameraIsDisabled ? 0.8 : 1 };\n \n   return (\n     &lt;&gt;\n       &lt;View style={styles.buttonBottomContainer}&gt;\n         &lt;View style={styles.cameracontainer}&gt;\n           &lt;View style={&#091;styles.cameraOutline, disabledStyle]}&gt;\n             &lt;TouchableOpacity\n               activeOpacity={0.8}\n               style={styles.cameraButton}\n               onPress={this.capture}\n             \/&gt;\n           &lt;\/View&gt;\n         &lt;\/View&gt;\n         &lt;View&gt;\n           {this.renderFlashControl()}\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     &lt;\/&gt;\n   );\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>The function renderCameraOverlay() is used to conditionally display a loading screen or processing screen and the camera controls.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>renderCameraOverlay() {\n   let loadingState = null;\n   if (this.state.loadingCamera) {\n     loadingState = (\n       &lt;View style={styles.overlay}&gt;\n         &lt;View style={styles.loadingContainer}&gt;\n           &lt;ActivityIndicator color=\"white\" \/&gt;\n           &lt;Text style={styles.loadingCameraMessage}&gt;Loading Camera&lt;\/Text&gt;\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     );\n   } else if (this.state.processingImage) {\n     loadingState = (\n       &lt;View style={styles.overlay}&gt;\n         &lt;View style={styles.loadingContainer}&gt;\n           &lt;View style={styles.processingContainer}&gt;\n             &lt;ActivityIndicator color=\"#333333\" size=\"large\" \/&gt;\n             &lt;Text style={{ color: '#333333', fontSize: 30, marginTop: 10 }}&gt;Processing&lt;\/Text&gt;\n           &lt;\/View&gt;\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     );\n   }\n \n   return (\n     &lt;&gt;\n       {loadingState}\n       &lt;SafeAreaView style={&#091;styles.overlay]}&gt;\n         {this.renderCameraControls()}\n       &lt;\/SafeAreaView&gt;\n     &lt;\/&gt;\n   );\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>The renderCameraView() function&nbsp; is rendering either the camera view or a loading state, depending or an error message. Here the allowDetection prop is set true which allows automatic detection of an identifiable rectangular region and then triggers the onDetectedCapture prop where we extract and process the detected document.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>You have to import the Scanner and RectangleOverlay from the react-native-rectangle-scanner package.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>import Scanner, { RectangleOverlay } from 'react-native-rectangle-scanner';\n\nrenderCameraView() {\n   if (this.state.showScannerView) {\n     const previewSize = this.getPreviewSize();\n     let rectangleOverlay = null;\n     if (!this.state.loadingCamera &amp;&amp; !this.state.processingImage) {\n       rectangleOverlay = (\n         &lt;RectangleOverlay\n           detectedRectangle={this.state.detectedRectangle}\n           backgroundColor=\"rgba(255,181,6, 0.2)\"\n           borderColor=\"rgb(255,181,6)\"\n           borderWidth={4}\n           detectedBackgroundColor=\"rgba(255,181,6, 0.3)\"\n           detectedBorderWidth={6}\n           detectedBorderColor=\"rgb(255,218,124)\"\n           onDetectedCapture={this.capture}\n           allowDetection\n         \/&gt;\n       );\n     }\n     return (\n       &lt;View style={{ backgroundColor: 'rgba(0, 0, 0, 0)', position: 'relative', marginTop: previewSize.marginTop, marginLeft: previewSize.marginLeft, height: `${previewSize.height * 100}%`, width: `${previewSize.width * 100}%` }}&gt;\n         &lt;Scanner\n           onPictureTaken={this.onPictureTaken}\n           onPictureProcessed={this.onPictureProcessed}\n           enableTorch={this.state.flashEnabled}\n           ref={this.camera}\n           capturedQuality={0.6}\n           onRectangleDetected={({ detectedRectangle }) =&gt; this.setState({ detectedRectangle })}\n           onDeviceSetup={this.onDeviceSetup}\n           onTorchChanged={({ enabled }) =&gt; this.setState({ flashEnabled: enabled })}\n           style={styles.scanner}\n           onErrorProcessingImage={(err) =&gt; console.log('error', err)}\n         \/&gt;\n         {rectangleOverlay}\n         &lt;Animated.View style={{ ...styles.overlay, backgroundColor: 'white', opacity: this.state.overlayFlashOpacity }} \/&gt;\n         {this.renderCameraOverlay()}\n       &lt;\/View&gt;\n     );\n   }\n \n   let message = null;\n   if (this.state.loadingCamera) {\n     message = (\n       &lt;View style={styles.overlay}&gt;\n         &lt;View style={styles.loadingContainer}&gt;\n           &lt;ActivityIndicator color=\"white\" \/&gt;\n           &lt;Text style={styles.loadingCameraMessage}&gt;Loading Camera&lt;\/Text&gt;\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     );\n   } else {\n     message = (\n       &lt;Text style={styles.cameraNotAvailableText}&gt;\n         {this.getCameraDisabledMessage()}\n       &lt;\/Text&gt;\n     );\n   }\n   return (\n     &lt;View style={styles.cameraNotAvailableContainer}&gt;\n       {message}\n     &lt;\/View&gt;\n \n   );\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>Now we can piece this all together to render the final UI. Here, if the image state is set, we will be redirected to the preview page and if not, the camera view will be rendered from which we can capture the image.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>render() {\n   if (this.state.image) {\n     return (\n       &lt;View style={styles.previewContainer}&gt;\n         &lt;View style={styles.previewBox}&gt;\n           &lt;Image source={{ uri: this.state.image.croppedImage }} style={styles.preview} \/&gt;\n         &lt;\/View&gt;\n         &lt;TouchableOpacity style={styles.buttonContainer} onPress={this.retryCapture}&gt;\n           &lt;Text style={styles.buttonText}&gt;Retry&lt;\/Text&gt;\n         &lt;\/TouchableOpacity&gt;\n       &lt;\/View&gt;\n     )\n   } else {\n     return (\n       &lt;View\n         style={styles.container}\n         onLayout={(event) =&gt; {\n           \/\/ This is used to detect multi tasking mode on iOS\/iPad\n           \/\/ Camera use is not allowed\n           this.props.onLayout(event);\n           if (this.state.didLoadInitialLayout &amp;&amp; Platform.OS === 'ios') {\n             const screenWidth = Dimensions.get('screen').width;\n             const isMultiTasking = (\n               Math.round(event.nativeEvent.layout.width) &lt; Math.round(screenWidth)\n             );\n             if (isMultiTasking) {\n               this.setState({ isMultiTasking: true, loadingCamera: false });\n             } else {\n               this.setState({ isMultiTasking: false });\n             }\n           } else {\n             this.setState({ didLoadInitialLayout: true });\n           }\n         }}\n       &gt;\n         &lt;StatusBar backgroundColor=\"black\" barStyle=\"light-content\" hidden={Platform.OS !== 'android'} \/&gt;\n         {this.renderCameraView()}\n       &lt;\/View&gt;\n     );\n   }\n }\n \n retryCapture = () =&gt; {\n   this.setState({\n     image: null\n   });\n }\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p>You might have noticed that there are a lot of custom style references which scaffolds out the views properly which is defined below.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:code -->\n<pre class=\"wp-block-code\"><code>const styles = StyleSheet.create({\n preview: {\n   flex: 1, width: null, height: null, resizeMode: 'contain'\n },\n previewBox: {\n   width: 350, height: 350\n },\n previewContainer: {\n   justifyContent: 'center', alignItems: 'center', flex:1\n },\n buttonBottomContainer: {\n   display:'flex', bottom:40, flexDirection:'row', position:'absolute',\n },\n buttonContainer: {\n   position: 'relative', backgroundColor: '#000000', alignSelf: 'center', alignItems: 'center', borderRadius: 10, marginTop: 40, padding: 10, width: 100\n },\n buttonGroup: {\n   backgroundColor: '#00000080', borderRadius: 17,\n },\n buttonIcon: {\n   color: 'white', fontSize: 22, marginBottom: 3, textAlign: 'center',\n },\n buttonText: {\n   color: 'white', fontSize: 13,\n },\n cameraButton: {\n   backgroundColor: 'white', borderRadius: 50, flex: 1, margin: 3\n },\n cameraNotAvailableContainer: {\n   alignItems: 'center', flex: 1, justifyContent: 'center',  marginHorizontal: 15,\n },\n cameraNotAvailableText: {\n   color: 'white', fontSize: 25, textAlign: 'center',\n },\n cameracontainer: {\n   flex: 1, display: 'flex', justifyContent: 'center',\n },\n cameraOutline: {\n   alignSelf: \"center\", left: 30, borderColor: 'white', borderRadius: 50,\n   borderWidth: 3, height: 70, width: 70,\n },\n container: {\n   backgroundColor: 'black', flex: 1,\n },\n flashControl: {\n   alignItems: 'center', borderRadius: 30, height: 50, justifyContent: 'center', margin: 8, paddingTop: 7, width: 50\n },\n loadingCameraMessage: {\n   color: 'white', fontSize: 18, marginTop: 10, textAlign: 'center'\n },\n loadingContainer: {\n   alignItems: 'center', flex: 1, justifyContent: 'center'\n },\n overlay: {\n   bottom: 0, flex: 1, left: 0, position: 'absolute', right: 0, top: 0,\n },\n processingContainer: {\n   alignItems: 'center', backgroundColor: 'rgba(220, 220, 220, 0.7)', borderRadius: 16,height: 140,justifyContent: 'center',width: 200,\n },\n scanner: {\n   flex: 1,\n },\n});\n<\/code><\/pre>\n<!-- \/divi:code -->\n\n<!-- divi:paragraph -->\n<p><strong>Conclusion<\/strong><\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>The field of software engineering is vast and diverse but gets simplified with the passage of time and advancing technology. Every day we see even simpler solutions to problems that were impossible or difficult previously. Writing such an <a href=\"https:\/\/www.confianzit.com\/android-application-development\" class=\"rank-math-link\">application for Android<\/a> or <a href=\"https:\/\/www.confianzit.com\/ios-application-development\" class=\"rank-math-link\">iOS<\/a> earlier would have involved using the native libraries and wiring up much more code for even working out a basic implementation which also requires a fundamental understanding of OpenCV library. This would be much more tedious to maintain across the two platforms.<\/p>\n<!-- \/divi:paragraph -->\n\n<!-- divi:paragraph -->\n<p>With the intelligent use of open source libraries and React Native we can scaffold out a relatively robust rectangle detection app without much effort. The is just a simple demonstration using a commonly available plugin for rectangle detection. Keep in mind this is just a barebones implementation of the plugin and while it does the job quite well, diving into the plugin documentation and experimenting should allow you to build up a better version.<\/p>\n<!-- \/divi:paragraph -->[\/et_pb_text][\/et_pb_column]\r\n\t\t\t[\/et_pb_row]\r\n\t\t[\/et_pb_section]\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":16,"featured_media":1125,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"<!-- wp:paragraph -->\n<p>Since the age of artificial intelligence and machine learning, one of the most important applications to which they have been applied is for text detection and image processing, creating the field of computer vision. While computer vision was almost arcane in the old days and developing applications related to the same required much effort and deeper understanding of many mathematical concepts, nowadays it is used almost everywhere, thanks to the development of the famous OpenCV library by Intel.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Most of the applications for image processing can be handled well with the OpenCV library, which is commonly used with Python to handle complex cases, and the source for the library is also available in other platforms. In the context of mobile application development, OpenCV has provided libraries for both Android and iOS.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>One of the widely used applications of OpenCV is to detect a document and possibly apply perspective correction to get a clear view of the document. This is quite easy in OpenCV and we could use built-in functions such as getPerspectiveTransform(src, dst). While this works out great when developing native applications, if the app is developed in a cross-platform ecosystem using frameworks such as React Native, this won\u2019t be ideal. This is because we will have to integrate OpenCV in the native source files of the React Native project and write native code to implement the same which is used in React Native. This defeats the entire purpose of cross platform development and requires specialized knowledge in native development which brings us back to square one. Thankfully due to the massive adoption of React Native over the recent years and the open source community we now have alternatives available as plugins to use like react-native-rectangle-scanner. So, let\u2019s see how to use this plugin to implement a simple app in React Native.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><strong>Getting Started<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"align\":\"center\"} -->\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img src=\"https:\/\/lh3.googleusercontent.com\/cOB4MLBnUZtmflXeZ38yvgX5EFsP-HEO4FAeIBhb4EKQaieV4elg_G0PaAeBNxSQGY1d8rhgkL9d7CuTpWynusaux1tosLjc8YDfyUMb9jDxXu6zP_MLcGJA5OPqXezefrIWgRIv03zOKXUfzA\" alt=\"\" \/><\/figure><\/div>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Let\u2019s create a sample application to detect a rectangle document and show it to the user with the perspective correction applied as shown above.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Using the react-native-cli tool let\u2019s initialize an empty project. This can also be done using expo and really is up to personal preference. The npx command allows you to use the latest version of the React Native.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>npx react-native init RectangleScanner<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>In the <em>package.json<\/em> file add the plugin along with two more dependencies which will be used to render the UI icons later for our app as shown below.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>..\n\"dependencies\": {\n   \"react\": \"16.11.0\",\n   \"react-native\": \"0.62.2\",\n   \"react-native-rectangle-scanner\": \"^1.0.10\",\n   \"react-native-svg\": \"^12.1.0\",\n   \"react-native-vector-icons\": \"^6.6.0\"\n\t..<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Run yarn install to pull in the dependencies added above.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><strong>Configuring Android Settings<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now there are some slight additions to be made in the Android source directory to get the icons working properly.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In<strong> <\/strong><em>android\/app\/src\/main\/AndroidManifest.xml<\/em><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Add camera permissions request:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>&lt;uses-permission android:name=\"android.permission.CAMERA\" \/&gt;<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Update settings.gradle file as the following to link openCV and Vector Icons.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>..\ninclude ':app'\n \ninclude ':react-native-vector-icons'\nproject(':react-native-vector-icons').projectDir = new File(rootProject.projectDir,\n '..\/node_modules\/react-native-vector-icons\/android')\n \ninclude ':openCVLibrary310'\nproject(':openCVLibrary310').projectDir = new File(rootProject.projectDir,'..\/node_modules\/react-native-rectangle-scanne\nr\/android\/openCVLibrary310')\n\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p><strong>Diving into Code<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We will begin by initializing a stateful React Component and define the <em>propTypes<\/em> for the props that we will be using later and initialize the state variables. We then utilize the createRef provided by React to initialize a reference and assign it to the camera variable, which will be later used for camera actions.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>import { PropTypes } from 'prop-types';\n \nexport default class DocumentScanner extends React.Component {\n static propTypes = {\n   cameraIsOn: PropTypes.bool,\n   onLayout: PropTypes.func,\n   onPictureTaken: PropTypes.func,\n   onPictureProcessed: PropTypes.func\n }\n \n static defaultProps = {\n   cameraIsOn: undefined, \/\/ Whether camera is on or off\n   onLayout: () =&gt; { }, \/\/ Invokes when the camera layout is initialized\n   onPictureTaken: () =&gt; { }, \/\/ Invokes when the picture is taken\n   onPictureProcessed: () =&gt; { } \/\/ Invokes when the picture is taken and cached.\n }\n \n constructor(props) {\n   super(props);\n   this.state = {\n     flashEnabled: false,\n     showScannerView: false,\n     didLoadInitialLayout: false,\n     detectedRectangle: false,\n     isMultiTasking: false,\n     loadingCamera: true,\n     processingImage: false,\n     takingPicture: false,\n     overlayFlashOpacity: new Animated.Value(0),\n     device: {\n       initialized: false,\n       hasCamera: false,\n       permissionToUseCamera: false,\n       flashIsAvailable: false,\n       previewHeightPercent: 1,\n       previewWidthPercent: 1,\n     },\n   };\n \n   this.camera = React.createRef();\n   this.imageProcessorTimeout = null;\n }\n..\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>We define a function called onDeviceSetup() which basically retrieves information from the platform such as whether the device permissions for the camera are set, the aspect ratio when the preview is generated, etc.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code> onDeviceSetup = (deviceDetails) =&gt; {\n   const {\n     hasCamera, permissionToUseCamera, flashIsAvailable, previewHeightPercent, previewWidthPercent,\n   } = deviceDetails;\n   this.setState({\n     loadingCamera: false,\n     device: {\n       initialized: true,\n       hasCamera,\n       permissionToUseCamera,\n       flashIsAvailable,\n       previewHeightPercent: previewHeightPercent || 1,\n       previewWidthPercent: previewWidthPercent || 1,\n     },\n   });\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>To notify various errors that may arise when accessing the camera&nbsp; getCameraDisabledMessage() is used.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>getCameraDisabledMessage() {\n   if (this.state.isMultiTasking) {\n     return 'Camera is not allowed in multi tasking mode.';\n   }\n \n   const { device } = this.state;\n   if (device.initialized) {\n     if (!device.hasCamera) {\n       return 'Could not find a camera on the device.';\n     }\n     if (!device.permissionToUseCamera) {\n       return 'Permission to use camera has not been granted.';\n     }\n   }\n   return 'Failed to set up the camera.';\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Create a function turnOnCamera() which will open the scanner view. The turnOffCamera() function similarly hides the camera view, and also if the camera view is on but no camera was found in the device after calling the onDeviceSetup() method, it can optionally uninitialize the camera. The turnOnCamera() will be called each time immediately after a view update occurs.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>turnOnCamera() {\n   if (!this.state.showScannerView) {\n     this.setState({\n       showScannerView: true,\n       loadingCamera: true,\n     });\n   }\n }\n \n turnOffCamera(shouldUninitializeCamera = false) {\n   if (shouldUninitializeCamera &amp;&amp; this.state.device.initialized) {\n     this.setState(({ device }) =&gt; ({\n       showScannerView: false,\n       device: { ...device, initialized: false },\n     }));\n   } else if (this.state.showScannerView) {\n     this.setState({ showScannerView: false });\n   }\n }\n \n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>The turnOnCamera() and turnOffCamera() methods are invoked using lifecycle methods.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The camera will be turned on inside componentDidMount() only after loading the initial layout when multi-tasking mode is off on iOS devices. Otherwise, the turnOffCamera() function will get invoked from componentDidUpdate(). Also, the imageProcessorTimeout timer which will be set at the time of capture failure should be cleared inside the componentWillUnmount() function.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>componentDidMount() {\n   if (this.state.didLoadInitialLayout &amp;&amp; !this.state.isMultiTasking) {\n     this.turnOnCamera();\n   }\n }\n \n componentDidUpdate() {\n   if (this.state.didLoadInitialLayout) {\n     if (this.state.isMultiTasking) return this.turnOffCamera(true);\n     if (this.state.device.initialized) {\n       if (!this.state.device.hasCamera) return this.turnOffCamera();\n       if (!this.state.device.permissionToUseCamera) return this.turnOffCamera();\n     }\n     if (this.props.cameraIsOn === true &amp;&amp; !this.state.showScannerView) {\n       return this.turnOnCamera();\n     }\n     if (this.props.cameraIsOn === false &amp;&amp; this.state.showScannerView) {\n       return this.turnOffCamera(true);\n     }\n     if (this.props.cameraIsOn === undefined) {\n       return this.turnOnCamera();\n     }\n   }\n   return null;\n }\n \n componentWillUnmount() {\n   clearTimeout(this.imageProcessorTimeout);\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>In some Android devices, the aspect ratio of the preview is different than the screen size which may lead to a distorted camera preview. To deal with this issue write a utility function getPreviewSize()which will take the device height and width into account and return an appropriate preview size for the same.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>getPreviewSize() {\n   const dimensions = Dimensions.get('window');\n   \/\/ We use set margin amounts because for some reasons the percentage values don't align the camera preview in the center correctly.\n   const heightMargin = (1 - this.state.device.previewHeightPercent) * dimensions.height \/ 2;\n   const widthMargin = (1 - this.state.device.previewWidthPercent) * dimensions.width \/ 2;\n   if (dimensions.height &gt; dimensions.width) {\n     \/\/ Portrait\n     return {\n       height: this.state.device.previewHeightPercent,\n       width: this.state.device.previewWidthPercent,\n       marginTop: heightMargin,\n       marginLeft: widthMargin,\n     };\n   }\n   \/\/ Landscape\n   return {\n     width: this.state.device.previewHeightPercent,\n     height: this.state.device.previewWidthPercent,\n     marginTop: widthMargin,\n     marginLeft: heightMargin,\n   };\n }\n \n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>The function triggerSnapAnimation() is used to show the flash animation when the user captures an image.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>triggerSnapAnimation() {\n   Animated.sequence(&#091;\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0.2, duration: 100, useNativeDriver: true }),\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0, duration: 50, useNativeDriver: true }),\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0.6, delay: 100, duration: 120, useNativeDriver: true }),\n     Animated.timing(this.state.overlayFlashOpacity, { toValue: 0, duration: 90, useNativeDriver: true }),\n   ]).start();\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>The capture() function is used to capture the current frame or the identified rectangle region. The loading or processing state will be set at the time of capturing to prevent any further capture triggers.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>capture = () =&gt; {\n   if (this.state.takingPicture) return;\n   if (this.state.processingImage) return;\n   this.setState({ takingPicture: true, processingImage: true });\n   this.camera.current.capture();\n   this.triggerSnapAnimation();\n \n   \/\/ If capture failed, allow for additional captures\n   this.imageProcessorTimeout = setTimeout(() =&gt; {\n     if (this.state.takingPicture) {\n       this.setState({ takingPicture: false });\n     }\n   }, 100);\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>We will be using the props provided by the plugin to process and cache the image as shown below. Here the image state will be set with the cached image url to be used in the preview.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>\/\/ The picture was captured but still needs to be processed.\n onPictureTaken = (event) =&gt; {\n   this.setState({ takingPicture: false });\n   this.props.onPictureTaken(event);\n }\n \n \/\/ The picture was taken and cached. You can now go on to using it.\n onPictureProcessed = (event) =&gt; {\n   this.props.onPictureProcessed(event);\n   this.setState({\n     image: event,\n     takingPicture: false,\n     processingImage: false,\n     showScannerView: this.props.cameraIsOn || false,\n   });\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Depending on whether the device has a flashlight or not, the renderFlashControl() function will return a flash icon. To use the icons for the UI we will have to import vector icons as follows.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>import Icon from 'react-native-vector-icons\/Ionicons';<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>renderFlashControl() {\n   const { flashEnabled, device } = this.state;\n   if (!device.flashIsAvailable) return null;\n   return (\n     &lt;TouchableOpacity\n       style={&#091;styles.flashControl, { backgroundColor: flashEnabled ? '#FFFFFF80' : '#00000080' }]}\n       activeOpacity={0.8}\n       onPress={() =&gt; this.setState({ flashEnabled: !flashEnabled })}\n     &gt;\n       &lt;Icon name=\"ios-flashlight\" style={&#091;styles.buttonIcon, { fontSize: 28, color: flashEnabled ? '#333' : '#FFF' }]} \/&gt;\n     &lt;\/TouchableOpacity&gt;\n   );\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>renderCameraControls() returns the camera capture button along with the flash button only when the loading or processing states are off.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>renderCameraControls() {\n   const cameraIsDisabled = this.state.takingPicture || this.state.processingImage;\n   const disabledStyle = { opacity: cameraIsDisabled ? 0.8 : 1 };\n \n   return (\n     &lt;&gt;\n       &lt;View style={styles.buttonBottomContainer}&gt;\n         &lt;View style={styles.cameracontainer}&gt;\n           &lt;View style={&#091;styles.cameraOutline, disabledStyle]}&gt;\n             &lt;TouchableOpacity\n               activeOpacity={0.8}\n               style={styles.cameraButton}\n               onPress={this.capture}\n             \/&gt;\n           &lt;\/View&gt;\n         &lt;\/View&gt;\n         &lt;View&gt;\n           {this.renderFlashControl()}\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     &lt;\/&gt;\n   );\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>The function renderCameraOverlay() is used to conditionally display a loading screen or processing screen and the camera controls.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>renderCameraOverlay() {\n   let loadingState = null;\n   if (this.state.loadingCamera) {\n     loadingState = (\n       &lt;View style={styles.overlay}&gt;\n         &lt;View style={styles.loadingContainer}&gt;\n           &lt;ActivityIndicator color=\"white\" \/&gt;\n           &lt;Text style={styles.loadingCameraMessage}&gt;Loading Camera&lt;\/Text&gt;\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     );\n   } else if (this.state.processingImage) {\n     loadingState = (\n       &lt;View style={styles.overlay}&gt;\n         &lt;View style={styles.loadingContainer}&gt;\n           &lt;View style={styles.processingContainer}&gt;\n             &lt;ActivityIndicator color=\"#333333\" size=\"large\" \/&gt;\n             &lt;Text style={{ color: '#333333', fontSize: 30, marginTop: 10 }}&gt;Processing&lt;\/Text&gt;\n           &lt;\/View&gt;\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     );\n   }\n \n   return (\n     &lt;&gt;\n       {loadingState}\n       &lt;SafeAreaView style={&#091;styles.overlay]}&gt;\n         {this.renderCameraControls()}\n       &lt;\/SafeAreaView&gt;\n     &lt;\/&gt;\n   );\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>The renderCameraView() function&nbsp; is rendering either the camera view or a loading state, depending or an error message. Here the allowDetection prop is set true which allows automatic detection of an identifiable rectangular region and then triggers the onDetectedCapture prop where we extract and process the detected document.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>You have to import the Scanner and RectangleOverlay from the react-native-rectangle-scanner package.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>import Scanner, { RectangleOverlay } from 'react-native-rectangle-scanner';\n\nrenderCameraView() {\n   if (this.state.showScannerView) {\n     const previewSize = this.getPreviewSize();\n     let rectangleOverlay = null;\n     if (!this.state.loadingCamera &amp;&amp; !this.state.processingImage) {\n       rectangleOverlay = (\n         &lt;RectangleOverlay\n           detectedRectangle={this.state.detectedRectangle}\n           backgroundColor=\"rgba(255,181,6, 0.2)\"\n           borderColor=\"rgb(255,181,6)\"\n           borderWidth={4}\n           detectedBackgroundColor=\"rgba(255,181,6, 0.3)\"\n           detectedBorderWidth={6}\n           detectedBorderColor=\"rgb(255,218,124)\"\n           onDetectedCapture={this.capture}\n           allowDetection\n         \/&gt;\n       );\n     }\n     return (\n       &lt;View style={{ backgroundColor: 'rgba(0, 0, 0, 0)', position: 'relative', marginTop: previewSize.marginTop, marginLeft: previewSize.marginLeft, height: `${previewSize.height * 100}%`, width: `${previewSize.width * 100}%` }}&gt;\n         &lt;Scanner\n           onPictureTaken={this.onPictureTaken}\n           onPictureProcessed={this.onPictureProcessed}\n           enableTorch={this.state.flashEnabled}\n           ref={this.camera}\n           capturedQuality={0.6}\n           onRectangleDetected={({ detectedRectangle }) =&gt; this.setState({ detectedRectangle })}\n           onDeviceSetup={this.onDeviceSetup}\n           onTorchChanged={({ enabled }) =&gt; this.setState({ flashEnabled: enabled })}\n           style={styles.scanner}\n           onErrorProcessingImage={(err) =&gt; console.log('error', err)}\n         \/&gt;\n         {rectangleOverlay}\n         &lt;Animated.View style={{ ...styles.overlay, backgroundColor: 'white', opacity: this.state.overlayFlashOpacity }} \/&gt;\n         {this.renderCameraOverlay()}\n       &lt;\/View&gt;\n     );\n   }\n \n   let message = null;\n   if (this.state.loadingCamera) {\n     message = (\n       &lt;View style={styles.overlay}&gt;\n         &lt;View style={styles.loadingContainer}&gt;\n           &lt;ActivityIndicator color=\"white\" \/&gt;\n           &lt;Text style={styles.loadingCameraMessage}&gt;Loading Camera&lt;\/Text&gt;\n         &lt;\/View&gt;\n       &lt;\/View&gt;\n     );\n   } else {\n     message = (\n       &lt;Text style={styles.cameraNotAvailableText}&gt;\n         {this.getCameraDisabledMessage()}\n       &lt;\/Text&gt;\n     );\n   }\n   return (\n     &lt;View style={styles.cameraNotAvailableContainer}&gt;\n       {message}\n     &lt;\/View&gt;\n \n   );\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Now we can piece this all together to render the final UI. Here, if the image state is set, we will be redirected to the preview page and if not, the camera view will be rendered from which we can capture the image.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>render() {\n   if (this.state.image) {\n     return (\n       &lt;View style={styles.previewContainer}&gt;\n         &lt;View style={styles.previewBox}&gt;\n           &lt;Image source={{ uri: this.state.image.croppedImage }} style={styles.preview} \/&gt;\n         &lt;\/View&gt;\n         &lt;TouchableOpacity style={styles.buttonContainer} onPress={this.retryCapture}&gt;\n           &lt;Text style={styles.buttonText}&gt;Retry&lt;\/Text&gt;\n         &lt;\/TouchableOpacity&gt;\n       &lt;\/View&gt;\n     )\n   } else {\n     return (\n       &lt;View\n         style={styles.container}\n         onLayout={(event) =&gt; {\n           \/\/ This is used to detect multi tasking mode on iOS\/iPad\n           \/\/ Camera use is not allowed\n           this.props.onLayout(event);\n           if (this.state.didLoadInitialLayout &amp;&amp; Platform.OS === 'ios') {\n             const screenWidth = Dimensions.get('screen').width;\n             const isMultiTasking = (\n               Math.round(event.nativeEvent.layout.width) &lt; Math.round(screenWidth)\n             );\n             if (isMultiTasking) {\n               this.setState({ isMultiTasking: true, loadingCamera: false });\n             } else {\n               this.setState({ isMultiTasking: false });\n             }\n           } else {\n             this.setState({ didLoadInitialLayout: true });\n           }\n         }}\n       &gt;\n         &lt;StatusBar backgroundColor=\"black\" barStyle=\"light-content\" hidden={Platform.OS !== 'android'} \/&gt;\n         {this.renderCameraView()}\n       &lt;\/View&gt;\n     );\n   }\n }\n \n retryCapture = () =&gt; {\n   this.setState({\n     image: null\n   });\n }\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>You might have noticed that there are a lot of custom style references which scaffolds out the views properly which is defined below.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>const styles = StyleSheet.create({\n preview: {\n   flex: 1, width: null, height: null, resizeMode: 'contain'\n },\n previewBox: {\n   width: 350, height: 350\n },\n previewContainer: {\n   justifyContent: 'center', alignItems: 'center', flex:1\n },\n buttonBottomContainer: {\n   display:'flex', bottom:40, flexDirection:'row', position:'absolute',\n },\n buttonContainer: {\n   position: 'relative', backgroundColor: '#000000', alignSelf: 'center', alignItems: 'center', borderRadius: 10, marginTop: 40, padding: 10, width: 100\n },\n buttonGroup: {\n   backgroundColor: '#00000080', borderRadius: 17,\n },\n buttonIcon: {\n   color: 'white', fontSize: 22, marginBottom: 3, textAlign: 'center',\n },\n buttonText: {\n   color: 'white', fontSize: 13,\n },\n cameraButton: {\n   backgroundColor: 'white', borderRadius: 50, flex: 1, margin: 3\n },\n cameraNotAvailableContainer: {\n   alignItems: 'center', flex: 1, justifyContent: 'center',  marginHorizontal: 15,\n },\n cameraNotAvailableText: {\n   color: 'white', fontSize: 25, textAlign: 'center',\n },\n cameracontainer: {\n   flex: 1, display: 'flex', justifyContent: 'center',\n },\n cameraOutline: {\n   alignSelf: \"center\", left: 30, borderColor: 'white', borderRadius: 50,\n   borderWidth: 3, height: 70, width: 70,\n },\n container: {\n   backgroundColor: 'black', flex: 1,\n },\n flashControl: {\n   alignItems: 'center', borderRadius: 30, height: 50, justifyContent: 'center', margin: 8, paddingTop: 7, width: 50\n },\n loadingCameraMessage: {\n   color: 'white', fontSize: 18, marginTop: 10, textAlign: 'center'\n },\n loadingContainer: {\n   alignItems: 'center', flex: 1, justifyContent: 'center'\n },\n overlay: {\n   bottom: 0, flex: 1, left: 0, position: 'absolute', right: 0, top: 0,\n },\n processingContainer: {\n   alignItems: 'center', backgroundColor: 'rgba(220, 220, 220, 0.7)', borderRadius: 16,height: 140,justifyContent: 'center',width: 200,\n },\n scanner: {\n   flex: 1,\n },\n});\n<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p><strong>Conclusion<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The field of software engineering is vast and diverse but gets simplified with the passage of time and advancing technology. Every day we see even simpler solutions to problems that were impossible or difficult previously. Writing such an <a href=\"https:\/\/www.confianzit.com\/android-application-development\" class=\"rank-math-link\">application for Android<\/a> or <a href=\"https:\/\/www.confianzit.com\/ios-application-development\" class=\"rank-math-link\">iOS<\/a> earlier would have involved using the native libraries and wiring up much more code for even working out a basic implementation which also requires a fundamental understanding of OpenCV library. This would be much more tedious to maintain across the two platforms.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With the intelligent use of open source libraries and React Native we can scaffold out a relatively robust rectangle detection app without much effort. The is just a simple demonstration using a commonly available plugin for rectangle detection. Keep in mind this is just a barebones implementation of the plugin and while it does the job quite well, diving into the plugin documentation and experimenting should allow you to build up a better version.<\/p>\n<!-- \/wp:paragraph -->","_et_gb_content_width":"","footnotes":""},"categories":[78,79,80],"tags":[170,169,167,166],"_links":{"self":[{"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/posts\/1016"}],"collection":[{"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/comments?post=1016"}],"version-history":[{"count":4,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/posts\/1016\/revisions"}],"predecessor-version":[{"id":1110,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/posts\/1016\/revisions\/1110"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/media\/1125"}],"wp:attachment":[{"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/media?parent=1016"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/categories?post=1016"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.confianzit.com\/cit-blog\/wp-json\/wp\/v2\/tags?post=1016"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}