根据美国机动车管理者协会(AAMVA https://www.aamva.org/DL-ID-Card-Design-Standard/)的规定,美国驾照使用PDF417编码,如下图:

Google Mobile Vision SDK

谷歌服务是支持驾照解析的,可以在Mobile Vision SDK中找到对应的类。要测试驾照识别,可以稍微改造下谷歌的示例代码https://github.com/googlesamples/android-vision/tree/master/visionSamples/barcode-reader。

onBarcodeDetected(Barcode barcode)回调中判断barcode类别是否是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);    }}

如果得到了驾照信息,就启动一个新的Activity显示结果:

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

程序的运行效果:

Dynamsoft Barcode Reader SDK

谷歌服务在很多的国内手机上用不了,比如华为。所以也可以根据定义自己写解析。

首先用fotoapparat快速创建一个摄像头应用:

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

通过帧回调函数,调用Dynamsoft Barcode Reader进行解码:

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

接下来仿照Google,定义一个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);    }}

再创建一个DBRDriverLicenseUtil类,定义字段:

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

校验解码后的字符串是否符合AAMVA标准:

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

读取驾照信息,通过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;}

把结果放到DriverLicense里用于显示:

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

最后运行这个不依赖谷歌服务的安卓驾照识别应用:

Google vs. Dynamsoft

除了驾照识别,还可以比较下Google和Dynamsoft的多码识别能力:

源码

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

更多相关文章

  1. Android之旅十六 android中各种资源的使用
  2. Android(安卓)中TabLayout自定义选择背景滑块的实例代码
  3. [android] 调试linux input子系统驱动的用户空间命令 getevent/s
  4. Android(安卓)自定义View探索——图片
  5. Android自定义控件(一)
  6. 自定义View详解
  7. Android(安卓)自定义流式布局
  8. Android(安卓)ListView自定义和选中效果
  9. Android自定义控件——自定义属性

随机推荐

  1. Android各个版本命名(从1.5到6.0)
  2. 史上最详细的Android(安卓)Studio系列教
  3. Android(安卓)监控网络状态
  4. Android的AIDL以及挂断电话
  5. Cannot retrieve repository metadata (r
  6. WebStorm 2021.3 下载+破解补丁+破解教程
  7. GIT生成ssh秘钥windows
  8. GIT生成ssh秘钥linux
  9. PHP使用SW-X框架的Redis-UML组件,对用户信
  10. Docker部署homeassitant