Workflow Guide Pixelnetica™ Document Scanning SDK for Android
This guide walks through the standard workflow for processing a document photo with the Pixelnetica Document Scanning SDK (DSSDK): open an image, detect the document in it, correct its orientation, refine it, recognize its text, and save the result as a searchable PDF. The pipeline needs no camera of its own — the input is any image URI, whether it came from the Smart Camera, the system photo picker, or a file. Each step is a short snippet you can adapt; the sample application source code shows the same pipeline in a complete application.
Every stage links to its step below. Steps 2 and 4 are optional — skip orientation detection or recognition if your application does not need them — and the Release the Page section closes the loop on the native memory the pipeline holds.
Step 1: Open an Image and Detect Document Bounds
Prerequisites: An imageUri obtained from the Image Picker, Gallery, or other local storage sources.
Do not use an internet URL!
// Create and configure ScanPicture
val picture = ScanPicture(context, imageUri)
picture.shadows = true // Even out shadows and uneven lighting
// Detect document corners
val cutout = picture.detectCutout()
if (!cutout.isDefined) {
// In cases where document borders cannot be determined,
// consider displaying a warning to the user.
}
A ScanPicture holds its image in native memory, outside the Java heap — and so does the cutout it just returned. You can release that memory the moment you are done by calling close(); because this guide keeps both objects across several steps, it closes them together in the last step. The other native-handle objects the guide creates, the detector and the reader, live inside a single step each, so they are scoped with use { } and released as soon as their step completes. The native memory guide explains the contract in full.
Step 2: Automatically Detect Picture Orientation
Prerequisites: A language directory path (languagesDir) as described in the Setup OCR Languages section.
// The detector holds native memory: use { } releases it
// as soon as orientation detection is done
ScanDetector(languagesDir).use { orientationDetector ->
picture.detectOrientation(orientationDetector)
}
Step 3: Process the Image
Execute the refine pipeline to crop the page to its detected outline, apply the desired colour profile, and rotate it upright for display. Pass a list holding at most one RefineFeature of each kind; kinds you leave out stay unchanged. The colour profiles guide shows what each profile is for and how the results look.
picture.refine(
listOf(
RefineFeature.Rectify.WithCutout(cutout), // Crop and straighten to the detected outline
RefineFeature.Profile(RefineFeature.Profile.Type.Bitonal), // Black-and-white (bitonal) processing
RefineFeature.Display.Normal, // Rotate the page upright for display
)
)
// Obtain the processed image
val bitmap: Bitmap = picture.createBitmap()
When no document outline is available, pass RefineFeature.Rectify.SkipCutout to leave the page uncropped, or RefineFeature.Rectify.AutoDetect to let the SDK find the corners without asking the user.
Step 4: Recognize Text in the Image
Prerequisites: A language directory path (languagesDir) and a list of languages (languageNames), as described in the Setup OCR Languages section.
// The reader holds native memory too: use { } releases it after recognition
ScanReader(languagesDir, languageNames).use { scanReader ->
picture.read(scanReader)
}
// Retrieve the recognized text
val text: String = picture.scanText.toString()
Pass the picture at full resolution — no caller-side resize is needed. The SDK manages memory for the recognition step internally, and recognized text positions are reported in the original image’s coordinates. If a device does run out of memory, read() fails with a catchable ScanningSdkException instead of crashing, and the reader stays usable for the next page.
Step 5: Save Results as a Searchable PDF
Prerequisites:
picturefrom the Process the Image section.- TrueType font files supporting the necessary languages.
To create and save a layered (sandwiched) PDF file with embedded searchable text above image layers, a set of TrueType font files (.ttf) must be stored in an accessible directory. DSSDK does not provide fonts. The demo application includes several royalty-free Google fonts. Additionally, any other fonts that support the necessary languages can be used.
- Define a list, e.g.,
fontList, containing the font files. Set the desired image compression using predefined values:
val imageCompression = ImageWriterPdf.ImageCompression.MediumAlternatively, specify the image compression ratio manually:
val imageCompression = ImageWriterPdf.ImageCompression(60.0F)
DSSDK provides five compression presets for images in PDFs: Lossless, Low, Medium, High, and Extreme.
Note: Compression levels apply only to color and grayscale images. Black-and-white (bitonal) images always use highly efficient lossless compression.
// Obtain an ImageWriterPdf instance
ImageWriterPdf(fileName).use { writer ->
writer.setFontFiles(fontList)
writer.setImageCompression(imageCompression)
writer.write(picture)
}
Release the Page
When the page’s work is finished, release the native memory of both objects the guide kept — the cutout from step 1 and the page itself:
cutout.close()
picture.close()
Closing is optional — if you skip it, the garbage collector releases the memory eventually, and existing code keeps working unchanged. It matters in loops: an application that scans or recognizes many pages in a row keeps its native memory flat by closing each page as it finishes, instead of accumulating pages until the collector runs. close() is safe to call twice, and an operation that needs a closed page’s native state throws a catchable ScanningSdkException. The native memory guide covers the details.
Performance Notes
Numbers measured with release builds on a mid-range reference phone, so you can set expectations before profiling your own integration:
- Document detection takes about 43 ms.
- Refining a page costs 161–220 ms per colour profile.
- Recognizing a full-resolution page takes about 55 seconds and peaks at about 57 MB of native memory.
- PDF export is the memory-hungriest step, peaking at about 113 MB.
Debug builds are several times slower than these figures — profile with a release build before drawing conclusions.