Skip to main content

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

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()
caution

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 your free "no-strings-attached" Trial License and properly test the Scanbot SDK.

Get your free Trial License

What do you think of this documentation?