Skip to content

Commit

Permalink
MRZ Scanning improvement.
Browse files Browse the repository at this point in the history
  • Loading branch information
alimertozdemir committed Oct 24, 2020
1 parent 8914e6d commit b7828a2
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.alimert.passportreader.mlkit.other.FrameMetadata;
import com.alimert.passportreader.mlkit.other.GraphicOverlay;
import com.alimert.passportreader.model.DocType;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
Expand All @@ -17,11 +18,15 @@
import com.google.mlkit.vision.text.TextRecognition;
import com.google.mlkit.vision.text.TextRecognizer;

import net.sf.scuba.data.Gender;

import org.jmrtd.lds.icao.MRZInfo;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TextRecognitionProcessor {

Expand All @@ -33,15 +38,28 @@ public class TextRecognitionProcessor {

private String scannedTextBuffer;

private DocType docType;

public static final String TYPE_PASSPORT = "P<";

public static final String TYPE_ID_CARD = "I<";

public static final String ID_CARD_TD_1_LINE_1_REGEX = "([A|C|I][A-Z0-9<]{1})([A-Z]{3})([A-Z0-9<]{31})";

public static final String ID_CARD_TD_1_LINE_2_REGEX = "([0-9]{6})([0-9]{1})([M|F|X|<]{1})([0-9]{6})([0-9]{1})([A-Z]{3})([A-Z0-9<]{11})([0-9]{1})";

public static final String ID_CARD_TD_1_LINE_3_REGEX = "([A-Z0-9<]{30})";

public static final String PASSPORT_TD_3_LINE_1_REGEX = "(P[A-Z0-9<]{1})([A-Z]{3})([A-Z0-9<]{39})";

public static final String PASSPORT_TD_3_LINE_2_REGEX = "([A-Z0-9<]{9})([0-9]{1})([A-Z]{3})([0-9]{6})([0-9]{1})([M|F|X|<]{1})([0-9]{6})([0-9]{1})([A-Z0-9<]{14})([0-9]{1})([0-9]{1})";

// Whether we should ignore process(). This is usually caused by feeding input data faster than
// the model can handle.
private final AtomicBoolean shouldThrottle = new AtomicBoolean(false);

public TextRecognitionProcessor(ResultListener resultListener) {
public TextRecognitionProcessor(DocType docType, ResultListener resultListener) {
this.docType = docType;
this.resultListener = resultListener;
textRecognizer = TextRecognition.getClient();
}
Expand Down Expand Up @@ -100,12 +118,57 @@ protected void onSuccess(@NonNull Text results, @NonNull FrameMetadata frameMeta
private void filterScannedText(GraphicOverlay graphicOverlay, Text.Element element) {
GraphicOverlay.Graphic textGraphic = new TextGraphic(graphicOverlay, element, Color.GREEN);
scannedTextBuffer += element.getText();
String docPrefix;
if(scannedTextBuffer.contains(TYPE_PASSPORT) || scannedTextBuffer.contains(TYPE_ID_CARD)) {
graphicOverlay.add(textGraphic);
docPrefix = scannedTextBuffer.contains(TYPE_PASSPORT) ? TYPE_PASSPORT : TYPE_ID_CARD;
scannedTextBuffer = scannedTextBuffer.substring(scannedTextBuffer.indexOf(docPrefix));
finishScanning(scannedTextBuffer);

if(docType == DocType.ID_CARD) {

Pattern patternIDCardTD1Line1 = Pattern.compile(ID_CARD_TD_1_LINE_1_REGEX);
Matcher matcherIDCardTD1Line1 = patternIDCardTD1Line1.matcher(scannedTextBuffer);

Pattern patternIDCardTD1Line2 = Pattern.compile(ID_CARD_TD_1_LINE_2_REGEX);
Matcher matcherIDCardTD1Line2 = patternIDCardTD1Line2.matcher(scannedTextBuffer);

if(matcherIDCardTD1Line1.find() && matcherIDCardTD1Line2.find()) {
graphicOverlay.add(textGraphic);
String line1 = matcherIDCardTD1Line1.group(0);
String line2 = matcherIDCardTD1Line2.group(0);
if (line1.indexOf(TYPE_ID_CARD) > 0) {
line1 = line1.substring(line1.indexOf(TYPE_ID_CARD));
String documentNumber = line1.substring(5, 14);
documentNumber = documentNumber.replace("O", "0");
String dateOfBirthDay = line2.substring(0, 6);
String expiryDate = line2.substring(8, 14);

Log.d(TAG, "Scanned Text Buffer ID Card ->>>> " + "Doc Number: " + documentNumber + " DateOfBirth: " + dateOfBirthDay + " ExpiryDate: " + expiryDate);

MRZInfo mrzInfo = buildTempMrz(documentNumber, dateOfBirthDay, expiryDate);

if (mrzInfo != null)
finishScanning(mrzInfo);
}
}
} else if (docType == DocType.PASSPORT) {

Pattern patternPassportTD3Line1 = Pattern.compile(PASSPORT_TD_3_LINE_1_REGEX);
Matcher matcherPassportTD3Line1 = patternPassportTD3Line1.matcher(scannedTextBuffer);

Pattern patternPassportTD3Line2 = Pattern.compile(PASSPORT_TD_3_LINE_2_REGEX);
Matcher matcherPassportTD3Line2 = patternPassportTD3Line2.matcher(scannedTextBuffer);

if(matcherPassportTD3Line1.find() && matcherPassportTD3Line2.find()) {
graphicOverlay.add(textGraphic);
String line2 = matcherPassportTD3Line2.group(0);
String documentNumber = line2.substring(0, 9);
documentNumber = documentNumber.replace("O", "0");
String dateOfBirthDay = line2.substring(13, 19);
String expiryDate = line2.substring(21, 27);

Log.d(TAG, "Scanned Text Buffer Passport ->>>> " + "Doc Number: " + documentNumber + " DateOfBirth: " + dateOfBirthDay + " ExpiryDate: " + expiryDate);

MRZInfo mrzInfo = buildTempMrz(documentNumber, dateOfBirthDay, expiryDate);

if (mrzInfo != null)
finishScanning(mrzInfo);
}
}
}

Expand Down Expand Up @@ -138,9 +201,8 @@ public void onFailure(@NonNull Exception e) {
shouldThrottle.set(true);
}

private void finishScanning(final String mrzText) {
private void finishScanning(final MRZInfo mrzInfo) {
try {
MRZInfo mrzInfo = new MRZInfo(mrzText);
if(isMrzValid(mrzInfo)) {
// Delay returning result 1 sec. in order to make mrz text become visible on graphicOverlay by user
// You want to call 'resultListener.onSuccess(mrzInfo)' without no delay
Expand All @@ -152,6 +214,17 @@ private void finishScanning(final String mrzText) {
}
}

private MRZInfo buildTempMrz(String documentNumber, String dateOfBirth, String expiryDate) {
MRZInfo mrzInfo = null;
try {
mrzInfo = new MRZInfo("P","NNN", "", "", documentNumber, "NNN", dateOfBirth, Gender.UNSPECIFIED, expiryDate, "");
} catch (Exception e) {
Log.d(TAG, "MRZInfo error : " + e.getLocalizedMessage());
}

return mrzInfo;
}

private boolean isMrzValid(MRZInfo mrzInfo) {
return mrzInfo.getDocumentNumber() != null && mrzInfo.getDocumentNumber().length() >= 8 &&
mrzInfo.getDateOfBirth() != null && mrzInfo.getDateOfBirth().length() == 6 &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class CaptureActivity extends AppCompatActivity implements TextRecognitio
public static final String MRZ_RESULT = "MRZ_RESULT";
public static final String DOC_TYPE = "DOC_TYPE";

private DocType docType = DocType.OTHER;

private static String TAG = CaptureActivity.class.getSimpleName();

@Override
Expand All @@ -43,7 +45,7 @@ protected void onCreate(Bundle savedInstanceState) {
actionBar.setCustomView(R.layout.action_bar_title);

if(getIntent().hasExtra(DOC_TYPE)) {
DocType docType = (DocType) getIntent().getSerializableExtra(DOC_TYPE);
docType = (DocType) getIntent().getSerializableExtra(DOC_TYPE);
if(docType == DocType.PASSPORT) {
actionBar.hide();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Expand Down Expand Up @@ -93,7 +95,7 @@ private void createCameraSource() {
cameraSource.setFacing(CameraSource.CAMERA_FACING_BACK);
}

cameraSource.setMachineLearningFrameProcessor(new TextRecognitionProcessor(this));
cameraSource.setMachineLearningFrameProcessor(new TextRecognitionProcessor(docType, this));
}

private void startCameraSource() {
Expand Down

0 comments on commit b7828a2

Please sign in to comment.