Quantcast
Channel: Barcode – Dynamsoft Developers
Viewing all articles
Browse latest Browse all 145

How to Recognize US Driver’s License on Android Mobile Apps

$
0
0

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.

driver's license

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.

Google driver license

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 &amp;&amp; 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' &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;
}

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.

Dynamsoft driver's license

One More Thing: Google vs. Dynamsoft

Since I have implemented two Android barcode reader apps, why not make a comparison?

Google vs Dynamsoft

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.


Viewing all articles
Browse latest Browse all 145

Trending Articles