How to identify and read American driver license information on Android devices

Keywords: Mobile Google SDK Android

According to the American Association of motor vehicle managers (AAMVA https://www.aamva.org/DL-ID-Card-Design-Standard/ )According to the regulations of PDF417, the US driver's license uses PDF417 code, as shown in the following figure:

Google Mobile Vision SDK

The Google service supports driver license resolution. You can find the corresponding class in the Mobile Vision SDK. To test license recognition, you can slightly change Google's example code https://github.com/googlesamples/android-vision/tree/master/visionSamples/barcode-reader.

In the onBarcodeDetected(Barcode barcode) callback, judge whether the barcode category is 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);
    }
}

If you get the license information, start a new Activity to display the results:

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();
    }
}

Operation effect of the program:

Dynamsoft Barcode Reader SDK

Google services are not available on many domestic mobile phones, such as Huawei. So you can also write your own parsing according to the definition.

First, use fotoapparat to quickly create a camera application:

implementation 'io.fotoapparat.fotoapparat:library:2.3.1'

Through the frame callback function, call Dynamsoft Barcode Reader to decode:

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);
      }
   }
}
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, "");

Next, follow the example of Google to define a DriverLicense class:

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 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";

Verify that the decoded string conforms to 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' &amp;&amp; data[1] == 'N' &amp;&amp; data[2] == 'S' &amp;&amp; data[3] == 'I' &amp;&amp; data[4] == ' ') || (data[0] == 'A' &amp;&amp; data[1] == 'A' &amp;&amp; data[2] == 'M' &amp;&amp; data[3] == 'V' &amp;&amp; data[4] == 'A'))
                &amp;&amp; (data[5] >= '0' &amp;&amp; data[5] <= '9') &amp;&amp; (data[6] >= '0' &amp;&amp; data[6] <= '9') &amp;&amp; (data[7] >= '0' &amp;&amp; data[7] <= '9')
                &amp;&amp; (data[8] >= '0' &amp;&amp; data[8] <= '9') &amp;&amp; (data[9] >= '0' &amp;&amp; data[9] <= '9') &amp;&amp; (data[10] >= '0' &amp;&amp; data[10] <= '9')
                &amp;&amp; (data[11] >= '0' &amp;&amp; data[11] <= '9') &amp;&amp; (data[12] >= '0' &amp;&amp; data[12] <= '9')
                &amp;&amp; (data[13] >= '0' &amp;&amp; data[13] <= '9') &amp;&amp; (data[14] >= '0' &amp;&amp; data[14] <= '9')
        ) {
            return true;
        }
    }
    return false;
}

Read the license information and store the returned result through 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;
}

Put the results in the DriverLicense to display:

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);

Finally, run the Android driver license recognition app that does not rely on Google services:

Google vs. Dynamsoft

In addition to license recognition, you can also compare Google's and Dynamsoft's multi code recognition capabilities:

Source code

https://github.com/yushulx/android-driver-license

Posted by colinexl on Tue, 02 Jun 2020 08:32:46 -0700