Image Cropping | Android Document Scanner
The Scanbot SDK provides the ability to crop, rotate and perform perspective correction on the source images.
The Image cropping functionality is available both as an RTU UI and as a classic component (types of components are explained here).
Integration
Take a look at our Example Apps to see how to integrate Image Cropping.
- Ready-To-Use UI: ready-to-use-ui-demo
- Classic UI Components: edit-polygon-view
Add Feature as a Dependency
Image Cropping is available with SDK Package 1. You have to add the following dependency for it:
implementation("io.scanbot:sdk-package-1:$latestSdkVersion")
Initialize the SDK
The Scanbot SDK must be initialized before use. Add the following code snippet to your Application
class:
import io.scanbot.sdk.ScanbotSDKInitializer
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// Initialize the Scanbot Scanner SDK:
ScanbotSDKInitializer().initialize(this)
}
}
Ready-To-Use UI Component
Ready-To-Use UI Component (activity) that is responsible for the Cropping functionality is CroppingActivity
.
Have a look at our end-to-end working example of the RTU components usage here.
Starting and configuring RTU Cropping screen
First of all, you have to add the SDK package and feature dependencies as described here.
Initialize the SDK as described here. More information about the SDK license initialization can be found here.
To use any of the RTU UI components you need to include the corresponding dependency in your build.gradle
file:
implementation("io.scanbot:sdk-package-ui:$scanbotSdkVersion")
Get the latest $scanbotSdkVersion
from the Changelog.
To start the RTU Cropping screen you only have to start a new activity and be ready to process its result later.
Starting from version 1.90.0, the SDK RTU components contain predefined AndroidX Result API contracts. They handle part of the boilerplate for starting the RTU activity component and mapping the result once it finishes.
If your code is bundled with Android's deprecated startActivityForResult
API - check the other approach we offer for this case.
- AndroidX Result API
- old 'startActivityForResult' approach
val croppingActivityResult: ActivityResultLauncher<CroppingConfiguration>
...
croppingActivityResult =
activity.registerForActivityResultOk(CroppingActivity.ResultContract()) { result ->
val page: Page? = result.result
//TODO: add your logic here to handle the result Page object
}
...
myButton.setOnClickListener {
val croppingConfiguration = CroppingConfiguration()
croppingConfiguration.setPage(page)
croppingActivityResult.launch(croppingConfiguration)
}
myButton.setOnClickListener {
val croppingConfiguration = CroppingConfiguration()
croppingConfiguration.setPage(page)
val intent = CroppingActivity.newIntent(context, croppingConfiguration)
startActivityForResult(intent, CROP_UI_REQUEST_CODE_CONSTANT)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CROP_UI_REQUEST_CODE_CONSTANT) {
val result = CroppingActivity.extractResult(resultCode, data)!!
if (result.resultOk) {
val page: Page? = result.result
//TODO: add your logic here to handle the result Page object
}
}
}
We offer some syntactic sugar for handling the result from RTU components via AndroidX Result API:
every RTU component's activity contains a
Result
class which, in turn, along with theresultCode
value exposes a BooleanresultOk
property. This will be true ifresultCode
equalsActivity.RESULT_OK
;when you only expect
Activity.RESULT_OK
result code - you can use theAppCompatActivity.registerForActivityResultOk
extension method instead ofregisterForActivityResult
- it will be triggered only when there is a non-nullable result entity present.
Always use the corresponding activity's static newIntent
method to create intent when starting the RTU UI activity using deprecated startActivityForResult
approach. Creating android.content.Intent
object using its constructor (passing the activity's class as a parameter) will lead to the RTU UI component malfunctioning.
An instance of CroppingConfiguration
is required for starting the RTU UI activity. It allows configuration changes through methods it exposes:
val croppingConfiguration = CroppingConfiguration()
// mandatory configuration parameter! All other options are optional.
croppingConfiguration.setPage(page)
croppingConfiguration.setPage(page: Page)
is a mandatory config parameter. It defines which page will be presented in the CroppingActivity
.
Full API references for these methods can be found on this page.
Handling the result
CroppingActivity
returns a CroppingActivity.Result
object which aggregates the processed (cropped and rotated) Page
object.
The Scanbot SDK provides a bunch of different page processors. For example, you can use PageProcessor
to perform additional image modifications (see more) or use PageFileStorage
to extract the Page content as Bitmap or JPG images (see more)
Accessibility customization
CroppingActivity
provides an ability to customize the Text resources for the accessibility feature.
Users can set a custom configuration CroppingAccessibilityConfiguration
in CroppingConfiguration
.
Here is an example:
val croppingConfiguration = CroppingConfiguration()
croppingConfiguration.setPage(page)
croppingConfiguration.setAccessibilityConfiguration(CroppingAccessibilityConfiguration(
cancelButtonAccessibilityLabel = "Cancel button",
cancelButtonAccessibilityHint = "Go back to the previous screen",
doneButtonAccessibilityLabel = "Done button",
doneButtonAccessibilityHint = "Finish the flow",
detectButtonAccessibilityLabel = "Detect the polygon",
detectButtonAccessibilityHint = "Automatically detect the polygon",
resetButtonAccessibilityLabel = "Reset the polygon",
resetButtonAccessibilityHint = "Discard the current polygon",
rotateButtonAccessibilityLabel = "Rotate",
rotateButtonAccessibilityHint = "Rotate the image clockwise"
))
Classic component
The classic component of the cropping functionality is presented by 2 main views - EditPolygonImageView
and MagnifierView
. They provide the ability to precisely modify the adjustable cropping polygon on the screen.
To integrate the classic component of the Cropping feature you can take a look at our Cropping Example App or check the following step-by-step integration instructions.
Add feature dependencies and initialize the SDK
First of all, you have to add the SDK package and feature dependencies as described here.
Initialize the SDK as described here. More information about the SDK license initialization can be found here.
Add EditPolygonImageView
to your layout
<io.scanbot.sdk.ui.EditPolygonImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:edgeColor="#00cea6"
app:cornerImageSrc="@drawable/ui_crop_corner_handle"
app:edgeImageSrc="@drawable/ui_crop_side_handle"
app:editPolygonHandleSize="48dp"
app:magneticLineTreshold="10dp"
app:editPolygonStrokeWidth="3dp"/>
The customizable attributes here are:
edgeColor
- the color of the polygon line. Default is undefined - meaning no color.cornerImageSrc
- the image to be used as the polygon corner handleedgeImageSrc
- the image to be used as the edge handleeditPolygonHandleSize
- defines the touchable area size for the polygon edge and corner handles. Default is 48dp.editPolygonStrokeWidth
- the width of the polygon line. Default is 3dp.magneticLineTreshold
- the edge should be this close to the magnetic line to snap in place. Default is 10dp.edgeColorOnLine
- the color of the edge when it aligns with the detected magnetic line. Default is undefined - meaning no color change.
Set the detected polygon
The default polygon for EditPolygonImageView
can be received from ContourDetector
.
ContourDetector
always contains the latest detected contours information like lines and polygons. After the first detection you can set the latest detected contour to EditPolygonImageView
.
val detector = ScanbotSDK(context).createContourDetector()
detector.detect(image)
editPolygonView.polygon = detector.polygonF
EditPolygonImageView
supports the magnetic lines feature. For this you have to set the detected horizontal and vertical lines:
editPolygonView.setLines(detector.horizontalLines, detector.verticalLines)
Add magnifying glass
EditPolygonImageView
supports a magnifying glass feature. To enable it, you should
add io.scanbot.sdk.ui.MagnifierView
to your custom layout.
<io.scanbot.sdk.ui.MagnifierView
android:id="@+id/magnifier"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:magnifierImageSrc="@drawable/ui_crop_magnifier"
app:magnifierRadius="36dp"
app:magnifierMargin="16dp"
app:magnifierEnableBounding="true"/>
The customizable attributes here are:
magnifierImageSrc
- the magnifier image. To be used as a mask - make sure it has a transparent section to see the magnified content. No default value.magnifierRadius
- the magnifier's size. Default is 36dp.magnifierMargin
- the magnifier's margin (distance to the screen border). Default is 16dp.magnifierEnableBounding
- bounce the magnifier to the opposite part of screen when editing the polygon's corner. Default is true.
You should set up the MagnifierView
every time editPolygonView is set with a new image:
magnifierView.setupMagnifier(editPolygonView)
Get the selected polygon
If you want to get a selected polygon from EditPolygonImageView
just call:
val currentPolygon = editPolygonView.polygon
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 LicenseWhat do you think of this documentation?
What can we do to improve it? Please be as detailed as you like.