如何在Android设备上识别读取美国驾照信息
16lz
2021-01-26
根据美国机动车管理者协会(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' && 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;}
读取驾照信息,通过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
更多相关文章
- Android之旅十六 android中各种资源的使用
- Android(安卓)中TabLayout自定义选择背景滑块的实例代码
- [android] 调试linux input子系统驱动的用户空间命令 getevent/s
- Android(安卓)自定义View探索——图片
- Android自定义控件(一)
- 自定义View详解
- Android(安卓)自定义流式布局
- Android(安卓)ListView自定义和选中效果
- Android自定义控件——自定义属性