-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(capture-sdk): Add MLKit text recognizer and a fake IBAN recognizer
The IBAN recognizer is fake to get the text recognizer working first. If text is recognized, then a hardcoded IBAN is returned. IBAN recognition is only started if the entry point is set to FIELD.
- Loading branch information
1 parent
e0c732b
commit 6ad6d57
Showing
8 changed files
with
519 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
...sdk/sdk/src/main/java/net/gini/android/capture/internal/textrecognition/IBANRecognizer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package net.gini.android.capture.internal.textrecognition | ||
|
||
import android.media.Image | ||
import kotlin.jvm.Throws | ||
|
||
/** | ||
* Use this class to recognize IBANs in images. | ||
* | ||
* @param textRecognizer a [TextRecognizer] implementation | ||
*/ | ||
internal class IBANRecognizer(private val textRecognizer: TextRecognizer) { | ||
|
||
/** | ||
* Processes the given [Image] and returns the recognized IBAN in the callback. | ||
* | ||
* @param image the image to process | ||
* @param width the width of the image | ||
* @param height the height of the image | ||
* @param rotationDegrees the rotation of the image | ||
* @param doneCallback the callback which will receive the recognized IBAN or null if no IBAN was found | ||
*/ | ||
@Throws(IllegalArgumentException::class) | ||
fun processImage(image: Image, width: Int, height: Int, rotationDegrees: Int, doneCallback: (String?) -> Unit) { | ||
if (width == 0) { | ||
throw IllegalArgumentException("Image width is 0") | ||
} | ||
if (height == 0) { | ||
throw IllegalArgumentException("Image height is 0") | ||
} | ||
textRecognizer.processImage(image, width, height, rotationDegrees) { recognizedText -> | ||
val withoutWhitespace = recognizedText?.replace("\\s".toRegex(), "") | ||
// TODO: Replace with IBAN recognition logic | ||
val result = if (!withoutWhitespace.isNullOrEmpty()) { | ||
"DE78500105172594181438" | ||
} else { | ||
null | ||
} | ||
doneCallback(result) | ||
} | ||
} | ||
|
||
/** | ||
* Processes the given image byte array and returns the recognized IBAN in the callback. | ||
* | ||
* @param byteArray the image byte array to process | ||
* @param width the width of the image | ||
* @param height the height of the image | ||
* @param rotationDegrees the rotation of the image | ||
* @param doneCallback the callback which will receive the recognized IBAN or null if no IBAN was found | ||
*/ | ||
@Throws(IllegalArgumentException::class) | ||
fun processByteArray(byteArray: ByteArray, width: Int, height: Int, rotationDegrees: Int, doneCallback: (String?) -> Unit) { | ||
if (width == 0) { | ||
throw IllegalArgumentException("Image width is 0") | ||
} | ||
if (height == 0) { | ||
throw IllegalArgumentException("Image height is 0") | ||
} | ||
textRecognizer.processByteArray(byteArray, width, height, rotationDegrees) { recognizedText -> | ||
val withoutWhitespace = recognizedText?.replace("\\s".toRegex(), "") | ||
val result = if (!withoutWhitespace.isNullOrEmpty()) { | ||
"DE78500105172594181438" | ||
} else { | ||
null | ||
} | ||
doneCallback(result) | ||
} | ||
} | ||
|
||
/** | ||
* Closes the IBAN recognizer. | ||
* | ||
* **IMPORTANT**: You must call this method when you are done with the IBAN recognizer. | ||
*/ | ||
fun close() { | ||
textRecognizer.close() | ||
} | ||
} |
118 changes: 118 additions & 0 deletions
118
...dk/src/main/java/net/gini/android/capture/internal/textrecognition/MLKitTextRecognizer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package net.gini.android.capture.internal.textrecognition | ||
|
||
import android.media.Image | ||
import com.google.android.gms.tasks.Task | ||
import com.google.mlkit.vision.common.InputImage | ||
import com.google.mlkit.vision.text.Text | ||
import com.google.mlkit.vision.text.TextRecognition | ||
import com.google.mlkit.vision.text.latin.TextRecognizerOptions | ||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
|
||
/** | ||
* Use this class to recognize text in images via the ML Kit Text Recognition API. | ||
*/ | ||
internal class MLKitTextRecognizer(private val recognizer: com.google.mlkit.vision.text.TextRecognizer) : | ||
TextRecognizer { | ||
|
||
private var processingTask: Task<Text>? = null | ||
|
||
/** | ||
* Processes the given [Image] and returns the recognized text in the callback. | ||
* | ||
* **IMPORTANT**: If an image is already processing then `doneCallback` will be called with null and the new image | ||
* will not be processed. | ||
* | ||
* @param image the image to process | ||
* @param width the width of the image | ||
* @param height the height of the image | ||
* @param rotationDegrees the rotation of the image | ||
* @param doneCallback the callback which will receive the recognized text or null if no text was found | ||
*/ | ||
override fun processImage( | ||
image: Image, | ||
width: Int, | ||
height: Int, | ||
rotationDegrees: Int, | ||
doneCallback: (String?) -> Unit | ||
) { | ||
if (processingTask != null) { | ||
LOG.warn("Text recognizer is already processing an image") | ||
doneCallback(null) | ||
return | ||
} | ||
|
||
processingTask = recognizer.process(InputImage.fromMediaImage(image, rotationDegrees)) | ||
handleProcessingTask(doneCallback) | ||
} | ||
|
||
/** | ||
* Processes the given image byte array and returns the recognized text in the callback. | ||
* | ||
* **IMPORTANT**: If an image is already processing then `doneCallback` will be called with null and the new image | ||
* will not be processed. | ||
* | ||
* @param byteArray the image byte array to process | ||
* @param width the width of the image | ||
* @param height the height of the image | ||
* @param rotationDegrees the rotation of the image | ||
* @param doneCallback the callback which will receive the recognized text or null if no text was found | ||
*/ | ||
override fun processByteArray( | ||
byteArray: ByteArray, | ||
width: Int, | ||
height: Int, | ||
rotationDegrees: Int, | ||
doneCallback: (String?) -> Unit | ||
) { | ||
if (processingTask != null) { | ||
if (DEBUG) { | ||
LOG.warn("Text recognizer is already processing an image") | ||
} | ||
doneCallback(null) | ||
return | ||
} | ||
|
||
processingTask = recognizer.process( | ||
InputImage.fromByteArray( | ||
byteArray, | ||
width, | ||
height, | ||
rotationDegrees, | ||
InputImage.IMAGE_FORMAT_NV21 | ||
) | ||
) | ||
handleProcessingTask(doneCallback) | ||
} | ||
|
||
override fun close() { | ||
recognizer.close() | ||
} | ||
|
||
private fun handleProcessingTask(doneCallback: (String?) -> Unit) { | ||
processingTask | ||
?.addOnSuccessListener { result -> | ||
if (DEBUG) { | ||
LOG.debug("Text recognizer success: {}", result.text) | ||
} | ||
doneCallback(result.text) | ||
} | ||
?.addOnFailureListener { e -> | ||
if (DEBUG) { | ||
LOG.error("Text recognizer failed", e) | ||
} | ||
doneCallback(null) | ||
} | ||
?.addOnCompleteListener { | ||
processingTask = null | ||
} | ||
} | ||
|
||
companion object { | ||
const val DEBUG = false | ||
val LOG: Logger = LoggerFactory.getLogger(MLKitTextRecognizer::class.java) | ||
|
||
@JvmStatic | ||
fun newInstance() = MLKitTextRecognizer(TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)) | ||
} | ||
} |
Oops, something went wrong.