In this post,I will explain about business card scanner using OCR API (Text Recognizer) in android.
Before that you need to read about OCR API released by google in following link,https://developers.google.com/vision/text-overviewStep 1 : We need to add following in dependenciescompile 'com.google.android.gms:play-services-vision:11.0.2'Step 3 : For Handling run time permission, I user TedPermission library. It's very easy for handle accept and denied permissions. We need to add following line to dependencies,<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" />Step 4 : Add the following code for handle run time permission in your activity filecompile 'gun0912.ted:tedpermission:1.0.0'Step 5 : We need to capture and store the image in internal storage with temporary name. So, We will add the following codes in ScanImage() function.btnScanner.setOnClickListener { val permissionlistener = object : PermissionListener { override fun onPermissionGranted() { scanImage() } override fun onPermissionDenied(deniedPermissions: java.util.ArrayList<String>) { Toast.makeText(this@MainActivity, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show() } } TedPermission(this@MainActivity) .setPermissionListener(permissionlistener) .setDeniedMessage("If you reject permission,you can not use this service\n\n Please turn on permissions at [Settings] > [Permission]") .setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA) .check() }Step 6: Handle Activity result and process the image in Asynctaskprivate fun scanImage() { val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) if (takePictureIntent.resolveActivity(packageManager) != null) { val fileName = "temp.jpg" val values = ContentValues() values.put(MediaStore.Images.Media.TITLE, fileName) mCapturedImageURI = contentResolver.insert (MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI) startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) } }Step 7: In Asynctask, we will get the captured image from storage and convert to bitmap. In doInBackground() function we convert and process the image.override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) { ProcessImageTask(resultCode).execute() } }Step 8 : We process image using TextRecognizer method from bitmapinner class ProcessImageTask(requestCode: Int) : AsyncTask<Void, Void, String>() { private val resultCode = requestCode override fun onPreExecute() { super.onPreExecute() Alert.showProgress(progressDialog) } override fun doInBackground(vararg params: Void?): String? { if (REQUEST_IMAGE_CAPTURE == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) { val projection = arrayOf(MediaStore.Images.Media.DATA) val cursor = managedQuery(mCapturedImageURI, projection, null, null, null) val column_index_data = cursor.getColumnIndexOrThrow (MediaStore.Images.Media.DATA) cursor.moveToFirst() picturePath = cursor.getString(column_index_data) val image = File(picturePath) val bmOptions = BitmapFactory.Options() val bitmap = BitmapFactory.decodeFile(image.absolutePath, bmOptions) cardBitmap = rotateImage(bitmap, 90f) processImage(cardBitmap) } return "finish" } override fun onPostExecute(result: String?) { super.onPostExecute(result) Alert.hideProgress(progressDialog) } }Step 9 : After converting the bitmap into text block, we add some pattern for find Name, email number and mobile numberprivate fun processImage(bitmap: Bitmap) { val textRecognizer = TextRecognizer.Builder(applicationContext).build() if (textRecognizer.isOperational) { val frame = Frame.Builder().setBitmap(bitmap).build() val items = textRecognizer.detect(frame) val stringBuilder = StringBuilder() for (i in 0 until items.size()) { val textBlock = items.valueAt(i) stringBuilder.append(textBlock.value) extractName(textBlock.value, false) extractEmail(textBlock.value) extractPhone(textBlock.value) stringBuilder.append("\n") } if (nameString.equals("")) { for (i in 0 until items.size()) { val textBlock = items.valueAt(i) stringBuilder.append(textBlock.value) extractName(textBlock.value, true) stringBuilder.append("\n") } } val fdelete = File(picturePath) if (fdelete.exists()) { if (fdelete.delete()) { println("file Deleted :" + picturePath) } else { println("file not Deleted :" + picturePath) } } } else { Toast.makeText(this@MainActivity, "Please rescan the card", Toast.LENGTH_SHORT).show() } }Thank you :)private fun extractName(str: String, retryFetchSingleWord: Boolean) { if (nameString == "") { if (!retryFetchSingleWord) { val strList: List<String> = str.split("\n") if (strList.size == 2 || strList.size == 3) { if (isAlpha(strList[0])) { nameString = strList[0] } } val NAME_REGEX = "^([A-Z]([a-z]*|\\.) *){1,2}([A-Z][a-z]+-?)+$" val p = Pattern.compile(NAME_REGEX, Pattern.MULTILINE) val m = p.matcher(str) if (m.find()) { nameString = m.group() } } else { val strList: List<String> = str.split("\n") if (strList.count() == 1 && isAlpha(str)) { if (findWordsLength(str) < 3) { nameString = str return } } } } } private fun extractEmail(str: String) { val EMAIL_REGEX = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\. [a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\ x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]* [a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]| [01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: (?:[\\x01-\\\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]| \\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])" val p = Pattern.compile(EMAIL_REGEX, Pattern.MULTILINE) val m = p.matcher(str) // get a matcher object if (m.find()) { emailString = m.group() } } private fun extractPhone(str: String) { if (mobileNumberString == "") { var numbericString = extractNumber(str) if (numbericString.length >= 10) { if (numbericString.length > 10) { numbericString = numbericString.substring(numbericString.length - 10) } println("Getting Phone Number") val PHONE_REGEX = "(?:^|\\D)(\\d{3})[)\\-. ]*?(\\d{3})[\\-. ]*?(\\d{4})(?:$|\\D)" val p = Pattern.compile(PHONE_REGEX, Pattern.MULTILINE) val m = p.matcher(numbericString) // get a matcher object if (m.find()) { mobileNumberString = m.group() } } } } private fun extractNumber(str: String?): String { var str = str str = str!!.replace(" ".toRegex(), "") str = str.replace("-".toRegex(), "") if (str == null || str.isEmpty()) return "" val sb = StringBuilder() var found = false for (c in str.toCharArray()) { if (Character.isDigit(c)) { sb.append(c) found = true } else if (found) { // If we already found a digit before and this char is not a digit, stop looping if (sb.length < 10) { sb.delete(0, sb.length) } else { if (sb.get(0).equals('9') || sb.get(0).equals('8') || sb.get(0).equals('7') || sb.get(0).equals('6')) { break } else { sb.delete(0, sb.length) } } } } return</span><span style="font-family: "trebuchet ms" , sans-serif;"> sb.toString() }
First Class Quality PenPower WorldCard Pro Scanner in UAE, Business Card Scanner in UAE, English Scanner in UAE Visit now https://gccgamers.com/penpower-worldcard-pro-business-card-scanner-english-pt-wocpe.html
ReplyDeleteTechlancers Middle East is recognized as the best website development company in Dubai, delivering custom-built websites that blend creativity with functionality. We focus on creating responsive, user-friendly, and performance-driven websites that truly represent your brand. With a team of experienced developers and designers, we turn your ideas into powerful digital experiences that drive real results.
ReplyDelete