According to the American Association of Motor Vehicle Administrators (AAMVA) specification, PDF417 symbology is used for storing personal information on US driver’s license. In this article, I will use Google mobile vision APIs to recognize a driver’s license. Besides, I will create a similar Android barcode reader app to extract information from PDF417 symbology by Dynamsoft Barcode Reader SDK.
Driver’s License Recognition on Android
Here is a forged driver’s license used for testing.

Google Barcode Detection
Mobile Vision APIs are capable of detecting some mainstream barcode symbologies. There is a DriverLicense class that defines common fields existing on all barcode standards.
To quickly verify Google’s API, I downloaded the sample code of Android Vision.
Create a ResultActivity.java file for displaying the information of a driver’s license:
public class ResultActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.setVerticalScrollBarEnabled(true); tv.setText(""); tv.setMovementMethod(new ScrollingMovementMethod()); Intent intent = getIntent(); if (intent != null) { Barcode.DriverLicense driverLicense = (Barcode.DriverLicense) intent.getParcelableExtra("DriverLicense"); if (driverLicense != null) { String documentType = driverLicense.documentType; tv.append("Document Type:\n" + documentType + "\n\n"); String firstName = driverLicense.firstName; tv.append("First Name:\n" + firstName + "\n\n"); String middleName = driverLicense.middleName; tv.append("Middle Name:\n" + middleName + "\n\n"); String lastName = driverLicense.lastName; tv.append("Last Name:\n" + lastName + "\n\n"); String gender = driverLicense.gender; tv.append("Gender: \n" + gender + "\n\n"); String addressStreet = driverLicense.addressStreet; tv.append("Street:\n" + addressStreet + "\n\n"); String addressCity = driverLicense.addressCity; tv.append("City:\n" + addressCity + "\n\n"); String addressState = driverLicense.addressState; tv.append("State:\n" + addressState + "\n\n"); String addressZip = driverLicense.addressZip; tv.append("Zip:\n" + addressZip + "\n\n"); String licenseNumber = driverLicense.licenseNumber; tv.append("License Number:\n" + licenseNumber + "\n\n"); String issueDate = driverLicense.issueDate; tv.append("Issue Date:\n" + issueDate + "\n\n"); String expiryDate = driverLicense.expiryDate; tv.append("Expiry Date:\n" + expiryDate + "\n\n"); String birthDate = driverLicense.birthDate; tv.append("Birth Date:\n" + birthDate + "\n\n"); String issuingCountry = driverLicense.issuingCountry; tv.append("Issue Country:\n" + issuingCountry + "\n\n"); } } setContentView(tv); } @Override public void onBackPressed() { super.onBackPressed(); } }
Find the onBarcodeDetected(Barcode barcode) function in BarcodeCaptureActivity.java, and then add the following code to start the ResultActivity class when a barcode format is detected as PDF417:
if (barcode.format == Barcode.PDF417) { Barcode.DriverLicense driverLicense = barcode.driverLicense; if (driverLicense != null) { Intent intent = new Intent(BarcodeCaptureActivity.this, ResultActivity.class); intent.putExtra("DriverLicense", driverLicense); startActivity(intent); } }
Configure the activity in AndroidManifest.xml:
<activity android:name=".ResultActivity" />
Build and launch the app.

Dynamsoft Barcode Detection
Create an Android camera project using fotoapparat:
implementation 'io.fotoapparat.fotoapparat:library:2.3.1'
Get camera frames in the callback function process(Frame frame):
class CodeFrameProcesser implements FrameProcessor { @Override public void process(@NonNull Frame frame) { //isDetected = false; if (fotPreviewSize == null) { handler.sendEmptyMessage(0); } if (!detectStart && isCameraOpen) { detectStart = true; wid = frame.getSize().width; hgt = frame.getSize().height; Message message = decodeHandler.obtainMessage(); message.obj = frame; decodeHandler.sendMessage(message); } } }
Decode barcodes from the frame by calling decodeBuffer() function. The image data format is NV21:
Frame frame = (Frame) msg.obj; PointResult pointResult = new PointResult(); pointResult.textResults = reader.decodeBuffer(frame.getImage(), frame.getSize().width, frame.getSize().height, frame.getSize().width, EnumImagePixelFormat.IPF_NV21, "");
Dynamsoft Barcode Reader SDK does not provide a driver’s license class yet. We can create a custom class by referring to the AAMVA standard and Google’s Mobile Vision.
Create a class named DriverLicense:
public class DriverLicense implements Parcelable { public String documentType; public String firstName; public String middleName; public String lastName; public String gender; public String addressStreet; public String addressCity; public String addressState; public String addressZip; public String licenseNumber; public String issueDate; public String expiryDate; public String birthDate; public String issuingCountry; public DriverLicense() { } protected DriverLicense(Parcel in) { documentType = in.readString(); firstName = in.readString(); middleName = in.readString(); lastName = in.readString(); gender = in.readString(); addressStreet = in.readString(); addressCity = in.readString(); addressState = in.readString(); addressZip = in.readString(); licenseNumber = in.readString(); issueDate = in.readString(); expiryDate = in.readString(); birthDate = in.readString(); issuingCountry = in.readString(); } public static final Creator<DriverLicense> CREATOR = new Creator<DriverLicense>() { @Override public DriverLicense createFromParcel(Parcel in) { return new DriverLicense(in); } @Override public DriverLicense[] newArray(int size) { return new DriverLicense[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(documentType); parcel.writeString(firstName); parcel.writeString(middleName); parcel.writeString(lastName); parcel.writeString(gender); parcel.writeString(addressStreet); parcel.writeString(addressCity); parcel.writeString(addressState); parcel.writeString(addressZip); parcel.writeString(licenseNumber); parcel.writeString(issueDate); parcel.writeString(expiryDate); parcel.writeString(birthDate); parcel.writeString(issuingCountry); } }
Create a DBRDriverLicenseUtil class to define the common AAMVA fields:
public static final String CITY = "DAI"; public static final String STATE = "DAJ"; public static final String STREET = "DAG"; public static final String ZIP = "DAK"; public static final String BIRTH_DATE = "DBB"; public static final String EXPIRY_DATE = "DBA"; public static final String FIRST_NAME = "DAC"; public static final String GENDER = "DBC"; public static final String ISSUE_DATE = "DBD"; public static final String ISSUING_COUNTRY = "DCG"; public static final String LAST_NAME = "DCS"; public static final String LICENSE_NUMBER = "DAQ"; public static final String MIDDLE_NAME = "DAD";
Check whether the string recognized from PDF417 complying with the AAMVA standard:
public static boolean ifDriverLicense(String barcodeText) { if (barcodeText == null || barcodeText.length() < 21) { return false; } String str = barcodeText.trim().replace("\r", "\n"); String[] strArray = str.split("\n"); ArrayList<String> strList = new ArrayList<>(); for (int i = 0; i < strArray.length; i++) { if (strArray[i].length() != 0) { strList.add(strArray[i]); } } if (strList.get(0).equals("@")) { byte[] data = strList.get(2).getBytes(); if (((data[0] == 'A' && data[1] == 'N' && data[2] == 'S' && data[3] == 'I' && data[4] == ' ') || (data[0] == 'A' && data[1] == 'A' && data[2] == 'M' && data[3] == 'V' && data[4] == 'A')) && (data[5] >= '0' && data[5] <= '9') && (data[6] >= '0' && data[6] <= '9') && (data[7] >= '0' && data[7] <= '9') && (data[8] >= '0' && data[8] <= '9') && (data[9] >= '0' && data[9] <= '9') && (data[10] >= '0' && data[10] <= '9') && (data[11] >= '0' && data[11] <= '9') && (data[12] >= '0' && data[12] <= '9') && (data[13] >= '0' && data[13] <= '9') && (data[14] >= '0' && data[14] <= '9') ) { return true; } } return false; }
Fetch the driver’s license information and save data to a HashMap:
public static HashMap<String, String> readUSDriverLicense(String resultText) { HashMap<String, String> resultMap = new HashMap<String, String>(); resultText = resultText.substring(resultText.indexOf("\n") + 1); int end = resultText.indexOf("\n"); String firstLine = resultText.substring(0, end + 1); boolean findFirstLine = false; for (Map.Entry<String, String> entry : DRIVER_LICENSE_INFO.entrySet()) { try { int startIndex = resultText.indexOf("\n" + entry.getKey()); if (startIndex != -1) { int endIndex = resultText.indexOf("\n", startIndex + entry.getKey().length() + 1); String value = resultText.substring(startIndex + entry.getKey().length() + 1, endIndex); resultMap.put(entry.getKey(), value); } else if (!findFirstLine) { int index = firstLine.indexOf(entry.getKey()); if (index != -1) { int endIndex = firstLine.indexOf("\n", entry.getKey().length() + 1); String value = firstLine.substring(index + entry.getKey().length(), endIndex); resultMap.put(entry.getKey(), value); findFirstLine = true; } } } catch (Exception ex) { ex.printStackTrace(); } } return resultMap; }
Create an instance of DriverLicense class and send it to ResultActivity :
HashMap<String, String> resultMaps = DBRDriverLicenseUtil.readUSDriverLicense(result.barcodeText); Intent intent = new Intent(MainActivity.this, ResultActivity.class); DriverLicense driverLicense = new DriverLicense(); driverLicense.documentType = "DL"; driverLicense.firstName = resultMaps.get(DBRDriverLicenseUtil.FIRST_NAME); driverLicense.middleName = resultMaps.get(DBRDriverLicenseUtil.MIDDLE_NAME); driverLicense.lastName = resultMaps.get(DBRDriverLicenseUtil.LAST_NAME); driverLicense.gender = resultMaps.get(DBRDriverLicenseUtil.GENDER); driverLicense.addressStreet = resultMaps.get(DBRDriverLicenseUtil.STREET); driverLicense.addressCity = resultMaps.get(DBRDriverLicenseUtil.CITY); driverLicense.addressState = resultMaps.get(DBRDriverLicenseUtil.STATE); driverLicense.addressZip = resultMaps.get(DBRDriverLicenseUtil.ZIP); driverLicense.licenseNumber = resultMaps.get(DBRDriverLicenseUtil.LICENSE_NUMBER); driverLicense.issueDate = resultMaps.get(DBRDriverLicenseUtil.ISSUE_DATE); driverLicense.expiryDate = resultMaps.get(DBRDriverLicenseUtil.EXPIRY_DATE); driverLicense.birthDate = resultMaps.get(DBRDriverLicenseUtil.BIRTH_DATE); driverLicense.issuingCountry = resultMaps.get(DBRDriverLicenseUtil.ISSUING_COUNTRY); intent.putExtra("DriverLicense", driverLicense); startActivity(intent);
After launching the app, I could get the same results as Google’s.

One More Thing: Google vs. Dynamsoft
Since I have implemented two Android barcode reader apps, why not make a comparison?

The left one is based on Google Mobile Vision SDK and the right one is based on Dynamsoft Barcode Reader SDK.
Source Code
https://github.com/yushulx/android-driver-license
The post How to Recognize US Driver’s License on Android Mobile Apps appeared first on Dev Blog.