BlinkID / blinkid-android

Everything you need to add AI-driven ID scanning into your native Android app.
https://microblink.com/identity/identity-document-scanning/
452 stars 154 forks source link

Modify To Scan University Student Identification Card. #11

Closed leeyikkong closed 8 years ago

leeyikkong commented 8 years ago

Hi, may I know that is it possible to customize the template (CroationIDFrontSide) to scan the other types of card for example Student Identification Card? If can, what are the steps to customize it? Thank you.

DoDoENT commented 8 years ago

Hi CroatianIDFrontSide is an example which shows how Templating API can be used to scan any document. You can modify it as you wish for your use case. The documentation for Templating API can help you understand what is being done inside the demo and demo is there to back the documentation with well-commented example.

leeyikkong commented 8 years ago

Ok, I will try it. Now we just close the issue, after that if got any problem, I will reopen or create another issue, is that ok?

DoDoENT commented 8 years ago

Sure.

leeyikkong commented 8 years ago

OK thanks alot.

leeyikkong commented 8 years ago

already defined DetectorSettings Hi, I already follow the step but still not working. I already set BlinkOCRRecognizerSettings already set setDecodingInfos I just want to scan one kind of the card so i skip then Classifier Class.

But now, when I want to scan the card, the screen do not have any respond, means never scan the card. May I know what are the steps that missed cause it cannot scan the card? How to get the result after the scan done?

thank you

DoDoENT commented 8 years ago

Does the demo app work for you? Can you provide a minimal Android Studio project which demonstrates the issue?

leeyikkong commented 8 years ago

Can I post the code for the template class I created to let you check?

DoDoENT commented 8 years ago

Please do so.

leeyikkong commented 8 years ago

public class CustomiseCard { private static final String NAME = "name"; private static final String PASSPORT_NUMBER = "passportNumber"; private static final String DATE_OF_BIRTH = "dateOfBirth"; private static final String GENDER = "gender"; private static final String ADDRESS = "address"; private static final String CARD = "card"; private static final String TAG = "CustomiseCard";

private static void setupName(TemplatingRecognizerSettings settings, List<DecodingInfo> card){

    //String name = firstName ? NAME : NAME;
    String name = NAME;
    //int dewarpHeight = firstName ? 150:100;
   /**
    * For extracting first and last names, we will use regex parser with regular expression which
    * attempts to extract as may uppercase words as possible from single line.
    */
    RegexParserSettings nameParser = new RegexParserSettings("([A-Z]+ ?)+");

    /**
     * add parser to parser group
     */
    settings.addParserToParserGroup(name, name, nameParser);

    Rectangle locationName = new Rectangle(0.565f, 0.259f, 0.412f, 0.056f);
    card.add(new DecodingInfo(locationName, 350, name ));
}

private static void setupDateOfBirth(TemplatingRecognizerSettings settings, List<DecodingInfo> card){
    card.add(new DecodingInfo(new Rectangle(0.565f, 0.389f, 0.176f, 0.056f),150, DATE_OF_BIRTH));

    settings.addParserToParserGroup(DATE_OF_BIRTH,DATE_OF_BIRTH, new DateParserSettings());
}

private static void setupPassportNumber(TemplatingRecognizerSettings settings, List<DecodingInfo> card){
    card.add(new DecodingInfo(new Rectangle(0.565f, 0.315f, 0.412f, 0.056f), 350, PASSPORT_NUMBER));

    RegexParserSettings documentNumberParser = new RegexParserSettings("\\d{9}");
    settings.addParserToParserGroup(PASSPORT_NUMBER,PASSPORT_NUMBER,documentNumberParser );
}

private static void setupAddress(TemplatingRecognizerSettings settings, List<DecodingInfo> card){
    card.add(new DecodingInfo(new Rectangle(0.565f, 0.556f, 0.412f, 0.241f), 350, ADDRESS));

    RegexParserSettings passportParser = new RegexParserSettings("([A-Z]+ ?)+");
    settings.addParserToParserGroup(PASSPORT_NUMBER,PASSPORT_NUMBER,passportParser );
}

private static void setupGender(TemplatingRecognizerSettings settings, List<DecodingInfo> card) {
    card.add(new DecodingInfo(new Rectangle(0.565f, 0.389f, 0.176f, 0.056f), 150, GENDER));

    RegexParserSettings genderParser = new RegexParserSettings("[M]/[F]");
    settings.addParser(GENDER, genderParser);
    genderParser.getOcrEngineOptions().addCharToWhitelist('M', OcrFont.OCR_FONT_ANY)
            .addCharToWhitelist('F', OcrFont.OCR_FONT_ANY);
    genderParser.setMustEndWithWhitespace(true);
    genderParser.setMustStartWithWhitespace(true);

    settings.addParserToParserGroup(GENDER,GENDER, genderParser);
}

public static BlinkOCRRecognizerSettings buildICardRecogniserSettings(){
    BlinkOCRRecognizerSettings settings = new BlinkOCRRecognizerSettings();

    List<DecodingInfo> cardDecodingInfos =  new ArrayList<>();
    setupName(settings, cardDecodingInfos);
    setupDateOfBirth(settings, cardDecodingInfos);
    setupPassportNumber(settings, cardDecodingInfos);
    setupAddress(settings, cardDecodingInfos);
    setupGender(settings, cardDecodingInfos);

    // setup card detector
    DocumentSpecification idSpec = DocumentSpecification.createFromPreset(DocumentSpecificationPreset.DOCUMENT_SPECIFICATION_PRESET_ID1_CARD);

    /*// set decoding info objects inherent to this document specification
    idSpec.setDecodingInfos(TemplatingUtils.listToArray(classificationDecodingInfos));*/

    // create card detector with single document specification
    DocumentDetectorSettings dds = new DocumentDetectorSettings(new DocumentSpecification[]{idSpec});

    // ensure this detector will be used when performing object detection
    settings.setDetectorSettings(dds);

    settings.setParserDecodingInfos(TemplatingUtils.listToArray( cardDecodingInfos),CARD );

    settings.setAllowFlippedRecognition(true);
    return settings;
}

}

leeyikkong commented 8 years ago

cannot upload the file because not supported by github even I compressed it become zip file. So i post it in the comment.

DoDoENT commented 8 years ago

From the quick look, the offending line would be

 /*// set decoding info objects inherent to this document specification
 idSpec.setDecodingInfos(TemplatingUtils.listToArray(classificationDecodingInfos));*/

This line is commented-out in your code, yet it contains decoding infos which are essential to determine the type of document, see the documentation. If you are only targeting single type of document, then decoding infos should be set in commented-out line. If you are targeting multiple types of documents, then with this line you set the decoding infos of locations which you use for classification of the document and then after classifier returns the classification result, you use decoding infos for that type of document set with setParserDecodingInfos.

Basically, this means that in your code, you should this line

settings.setParserDecodingInfos(TemplatingUtils.listToArray( cardDecodingInfos),CARD );

replace with this line:

idSpec.setDecodingInfos(TemplatingUtils.listToArray(cardDecodingInfos));

Just make sure you set decoding infos to IDSpec before using it to create DocumentDetectorSettings.

leeyikkong commented 8 years ago

Oh yea.. its work!! now got respond already. But now it cannot recognize the card. I will prompt out a dialog ask me to re-scan the card if it cannot be recognize. Is it because the position of the information that I want to scan not match with the exact value of the information on the card?

leeyikkong commented 8 years ago

and for the position of the address, basically address have few lines in identity card, so it will auto scan through all the lines in the position?

leeyikkong commented 8 years ago

and one more question is can I straight away use the MyKadRecognitionResult after the scanning done? If can, I just change the name in the template?

for example:

private static final String NAME = "name";

change to

private static final String NAME = "ownerFullName";

ownerFullName is the name get from MyKadRecognitionResult class

DoDoENT commented 8 years ago

Is it because the position of the information that I want to scan not match with the exact value of the information on the card?

Yes, you need to define locations very precisely. Also, if card is mis-detected, then relative positions are also calculated incorrectly. You should take this into account.

and for the position of the address, basically address have few lines in identity card, so it will auto scan through all the lines in the position?

The OCR is performed on entire decoding location. For filtering-out patterns you truly need, you should define a good regular expression in regex parser or use specialised parsers, like date parser.

and one more question is can I straight away use the MyKadRecognitionResult after the scanning done?

MyKadRecognitionResult is result of MyKad recognizer. After obtaining the result of your card, you can reconfigure RecognizerView and set it to use only MyKad recognizer.

leeyikkong commented 8 years ago

Yes, you need to define locations very precisely.

the spacing between 2 information I want to get is only 1-2mm only, is it too small? the calculation I made I just take 3 decimal places which are follow the template.

MyKadRecognitionResult is result of MyKad recognizer. After obtaining the result of your card, you can reconfigure RecognizerView and set it to use only MyKad recognizer.

So I need to create my own RecognitionResult class? and inside the class add all the method for example getName(), getAddress(), etc...

leeyikkong commented 8 years ago

private static void setupDateOfBirth(TemplatingRecognizerSettings settings, List card){ card.add(new DecodingInfo(new Rectangle(0.565f, 0.389f, 0.176f, 0.056f),150, DATE_OF_BIRTH));

the parameter for 150 is for what value? range??

DoDoENT commented 8 years ago

So I need to create my own RecognitionResult class? and inside the class add all the method for example getName(), getAddress(), etc...

No, you will obtain results as written in documentation, using parser group/parser name strings you used when configuring parser groups (aka decoding infos) and parsers.

DoDoENT commented 8 years ago

the parameter for 150 is for what value?

This is the dewarp height of the location in pixels. Generally it should be set to number of expected text lines * 100, but feel free to experiment with different values until you get the best results.

leeyikkong commented 8 years ago

Cannot find parser group for location 'address'. Will not perform OCR and parsing of that location!

This message mean my calculation got problem?

leeyikkong commented 8 years ago

the coding below is the coding after on

public  Customer onScanningDone(RecognitionResults results){
    Customer customer = null;
    BaseRecognitionResult[] dataArray = results.getRecognitionResults();
    for(BaseRecognitionResult baseResult : dataArray) {
        if(baseResult instanceof BlinkOCRRecognitionResult) {
            BlinkOCRRecognitionResult result = (BlinkOCRRecognitionResult) baseResult;
            // you can use getters of BlinkOCRRecognitionResult class to
            // obtain scanned information
            if(result.isValid() && !result.isEmpty()) {
                // use the parser name provided to BlinkOCRRecognizerSettings to
                // obtain parsed result provided by given parser
                // obtain result of "myAmountParser" in default parsing group
                Log.i(TAG, "card result is valid!!");
                String name = result.getParsedResult("name");
                String passport = result.getParsedResult("passportNumber");
                String dob = result.getParsedResult("dateOfBirth");
                String gender = result.getParsedResult("gender");
                String address = result.getParsedResult("address");

                Log.i(TAG, "Scan From ---> Name ))) " + name);
                Log.i(TAG, "Scan From ---> passport ))) " + passport);
                Log.i(TAG, "Scan From ---> dob ))) " + dob);
                Log.i(TAG, "Scan From i---> gender ))) " + gender);
                Log.i(TAG, "Scan From ---> address ))) " + address);

                customer.setName(name);
                customer.setGender(gender);
                customer.setPassport(passport);
                customer.setDob(dob);
                customer.setAddress(address);
            }
        }
    }
    return customer;
}

for Date I use DateParserSettings for other value I use RegexParserSettings

private static void setupDateOfBirth(TemplatingRecognizerSettings settings, List<DecodingInfo> card){
    card.add(new DecodingInfo(new Rectangle(0.565f, 0.389f, 0.176f, 0.056f),100, DATE_OF_BIRTH));

    settings.addParserToParserGroup(DATE_OF_BIRTH,DATE_OF_BIRTH, new DateParserSettings());
    Log.i(TAG, "Scan DOB");
}

private static void setupPassportNumber(TemplatingRecognizerSettings settings, List<DecodingInfo> card){
    card.add(new DecodingInfo(new Rectangle(0.565f, 0.315f, 0.412f, 0.056f), 100, PASSPORT_NUMBER));

    RegexParserSettings documentNumberParser = new RegexParserSettings("\\d{9}");
    settings.addParser(PASSPORT_NUMBER,documentNumberParser );
    Log.i(TAG, "Scan Passport");
}

the coding above are two function in my program. is that any problem? because after the scanning done, my application will crash. and I cannot get the value after scanning done.

leeyikkong commented 8 years ago

is it need use addParserToParseGroup? because after I use it, the scanned image got detected each character already. but now the problem is it said that the result is not valid or empty, ask me scan again.

DoDoENT commented 8 years ago

Cannot find parser group for location 'address'. Will not perform OCR and parsing of that location! This message mean my calculation got problem?

No, this means that you have created decoding info with name 'address', and have not set any parsers to parser group called 'address'.

DoDoENT commented 8 years ago

the coding above are two function in my program. is that any problem? because after the scanning done, my application will crash. and I cannot get the value after scanning done.

It should not be. Which crash are you having?

DoDoENT commented 8 years ago

is it need use addParserToParseGroup? because after I use it, the scanned image got detected each character already. but now the problem is it said that the result is not valid or empty, ask me scan again.

Yes, each decoding info / location on detected document represents a new parser group and only parsers added to parser group of same name as decoding info name will run on OCR result obtained on that location.

For more information about parser groups, please read this. For more information how parser group fit into Templating API, please read this. For a detailed explanation of code examples, please check this whitepaper.

leeyikkong commented 8 years ago

yea.. I got it already. now just need to adjust the position for the information. THANKS A LOT!!

DoDoENT commented 8 years ago

If that solved your problem, please close this issue. If you run into a new problem, please open a new issue.

leeyikkong commented 8 years ago

ok thanks for your help.