Scanning a document from an image | Android Document Scanner
The Scanbot SDK Document Scanner is a powerful tool that not only performs live document detection but also extracts content from still images imported from the gallery or any other source.
Integration
Add Document Scanner as a Dependency
The Document Scanner is available with SDK Package 1. To use the Document Scanner, the following dependency must be added:
implementation("io.scanbot:sdk-package-1:$latestSdkVersion")
Initialize the SDK
Before the Scanbot SDK can be used, it must be initialized in the Application
class:
import io.scanbot.sdk.ScanbotSDKInitializer
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// The Scanbot SDK is initialized:
ScanbotSDKInitializer().initialize(this)
}
}
Processing the Image
Importing the Image from the Gallery
To select an image from the photo library and run detection on it, a class for an image import contract is created using the modern Android result API.
class ImportImageContract(private val context: Context) : ActivityResultContract<Unit, Bitmap?>() {
override fun createIntent(context: Context, input: Unit): Intent {
// An image is selected from the photo library and document detection is run on it:
val imageIntent = Intent()
imageIntent.type = "image/*"
imageIntent.action = Intent.ACTION_GET_CONTENT
imageIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, false)
imageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
return Intent.createChooser(imageIntent, "Select a picture")
}
private fun processGalleryResult(data: Intent): Bitmap? {
val imageUri = data.data
return MediaStore.Images.Media.getBitmap(context.contentResolver, imageUri)
}
override fun parseResult(resultCode: Int, intent: Intent?): Bitmap? {
return if (resultCode == Activity.RESULT_OK && intent != null) {
return processGalleryResult(intent)
} else {
null
}
}
}
To run the gallery call and get a Bitmap using ImportImageContract
, the following code is used:
val galleryImageLauncher =
registerForActivityResult(ImportImageContract(this)) { resultEntity ->
lifecycleScope.launch(Dispatchers.Default) {
val activity = this@MainActivity
val sdk = ScanbotSDK(activity)
if (!sdk.licenseInfo.isValid) {
withContext(Dispatchers.Main) {
Toast.makeText(
activity,
"License has expired!",
Toast.LENGTH_LONG
).show()
}
} else {
resultEntity?.let { bitmap ->
// Image processing is carried out
// processImage()
}
}
}
}
findViewById<View>(R.id.import_image).setOnClickListener {
galleryImageLauncher.launch(Unit)
}
Creating Classes
The Scanbot SDK is used to detect the desired document element on the imported bitmap:
val scanbotSDK = ScanbotSDK(activity)
val pageFileStorage = scanbotSDK.createPageFileStorage()
val pageProcessor = scanbotSDK.createPageProcessor()
Detection
First Way: Creating a Page and Running Detection on it
fun processImage(
pageFileStorage: PageFileStorage,
pageProcessor: PageProcessor,
bitmap: Bitmap
) {
// A new Page object is created with the given image as the original image:
val pageId = pageFileStorage.add(bitmap)
var page = Page(pageId, emptyList(), DetectionStatus.OK)
// Auto document detection is run on it:
page = pageProcessor.detectDocument(page)
/// The page object is ready to be used.
}
Second Way: Running Detection Directly on Bitmap without Storing the Document Image in the SDK Storage
fun processImage(
contourDetector: ContourDetector,
bitmap: Bitmap
) {
val result = contourDetector.detect(bitmap)
if (result?.status == DetectionStatus.OK) {
// Proceeding with the detected contour on the image:
val croppedBitmap =
ImageProcessor(bitmap).crop(result.polygonF).processedBitmap()
}
}
Document Detection Details
ContourDetector
is used to perform document detection in Scanbot SDK.
The Document Detection in Scanbot SDK is based on edge detection. To detect edges and get the boundaries of a
document in a still image, use the detect(image)
method of ContourDetector
:
val detector = ScanbotSDK(context).createContourDetector()
val result = detector.detect(origininalBitmap) // pass a Bitmap or the image data as byte[]
val polygon = detector.getPolygonF()
This code detects boundaries and receives a DetectionResult
,
which is an enum that represents the recognition status.
Regardless of the DetectionResult
, you can acquire the detected boundaries as a polygon using the detector.getPolygonF()
method. If the polygon was not detected, it will return an empty List
. On successful detection, you will get a List
with 4 points, one for each corner - top_left, top_right, bottom_right, bottom_left. Each point has coordinates in a range between [0..1]
, representing a position relative to the image size. For instance, if a point has the coordinates (0.5, 0.5), it means that it is located exactly in the center of the image.
Types of Document Detection algorithms
The user can select the technology that will be used for document detection. There are two options: Edge-based and ML-based. The Edge-based technology detects all the edges in the picture and then predicts which of those edges may form the document. The ML-based approach finds a document using computer-vision technologies. The ML-based approach might be much faster for Live-detection than the Edge-based approach.
Since Scanbot SDK 1.81.0, it is included in io.scanbot:sdk-package-X
modules by default. If the application does not require the document detection feature at all, or an Edge-based approach is preferred, you can reduce the .apk size (~4MB) by excluding this Gradle dependency as follows:
implementation("io.scanbot:sdk-package-Y:$scanbotSdkVersion") { // REPLACE 'Y' with your SDK-package number!!!
exclude group: "io.scanbot", module: "bundle-sdk-ml-docdetector"
}
To select the type of Document Detection technology, use the following snippet during SDK initialization. The ML-based approach will be used by default.
import io.scanbot.sdk.ScanbotSDKInitializer
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// Initialize the Scanbot Scanner SDK:
ScanbotSDKInitializer()
.contourDetectorType(ContourDetector.Type.ML_BASED) // ML_BASED is default. Set it to EDGE_BASED to use the edge-based approach
.initialize(this)
}
}
To get an instance of a ContourDetector, use the ScanbotSDK
object. It will create a NEW contour detector for each call.
import io.scanbot.sdk.ScanbotSDKInitializer
...
val detector = ScanbotSDK(context).createContourDetector()
The ContourDetector()
constructor was marked as deprecated. It will create an instance of the document detector with Edge-based technology under the hood and ignore the setting passed to ScanbotSDKInitializer
.
Want to scan longer than one minute?
Generate a free trial license to test the Scanbot SDK thoroughly.
Get your free Trial LicenseWhat do you think of this documentation?
What can we do to improve it? Please be as detailed as you like.