Python MRZ 스캐너 SDK를 빌드하고 PyPI에 게시하는 방법

69434 단어 pypipassportmrzpython
Dynamsoft Label Recognizer C++ SDK의 최신 버전 2.2.10은 이전 버전보다 훨씬 작고 정확한 MRZ 인식 모델을 최적화했습니다. 이제 여권, 비자, 신분증 및 여행 서류에서 MRZ 인식을 지원합니다. 데스크톱 MRZ 인식 소프트웨어 개발을 용이하게 하기 위해 C++ OCR API를 Python에 바인딩할 수 있습니다.
이 문서에서는 Dynamsoft Label Recognizer C++ API를 기반으로 Python MRZ 스캐너 모듈을 빌드하는 단계를 살펴봅니다.

C/C++에서 Python MRZ 스캐너 SDK 구현



다음은 Python MRZ 확장 프로젝트의 구조입니다.



C/C++ MRZ SDK는 Windows 및 Linux용 MRZ 모델, JSON 형식의 모델 구성 파일, 헤더 파일 및 공유 라이브러리(*.dll 및 *.so)로 구성됩니다.
mrzscanner.cpp는 Python MRZ 스캐너 SDK의 진입점입니다. 두 가지 방법을 정의합니다.
  • initLicense() : 글로벌 라이센스 키를 초기화합니다.
  • createInstance() : DynamsoftMrzReader 클래스의 인스턴스를 만듭니다.

  • #include <Python.h>
    #include <stdio.h>
    #include "dynamsoft_mrz_reader.h"
    #define INITERROR return NULL
    
    struct module_state {
        PyObject *error;
    };
    
    #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
    
    static PyObject *
    error_out(PyObject *m)
    {
        struct module_state *st = GETSTATE(m);
        PyErr_SetString(st->error, "something bad happened");
        return NULL;
    }
    
    static PyObject *createInstance(PyObject *obj, PyObject *args)
    {
        if (PyType_Ready(&DynamsoftMrzReaderType) < 0)
             INITERROR;
    
        DynamsoftMrzReader* reader = PyObject_New(DynamsoftMrzReader, &DynamsoftMrzReaderType);
        reader->handler = DLR_CreateInstance();
        return (PyObject *)reader;
    }
    
    static PyObject *initLicense(PyObject *obj, PyObject *args)
    {
        char *pszLicense;
        if (!PyArg_ParseTuple(args, "s", &pszLicense))
        {
            return NULL;
        }
    
        char errorMsgBuffer[512];
        // Click https://www.dynamsoft.com/customer/license/trialLicense/?product=dbr to get a trial license.
        int ret = DLR_InitLicense(pszLicense, errorMsgBuffer, 512);
        printf("DLR_InitLicense: %s\n", errorMsgBuffer);
    
        return Py_BuildValue("i", ret);
    }
    
    static PyMethodDef mrzscanner_methods[] = {
      {"initLicense", initLicense, METH_VARARGS, "Set license to activate the SDK"},
      {"createInstance", createInstance, METH_VARARGS, "Create Dynamsoft MRZ Reader object"},
      {NULL, NULL, 0, NULL}       
    };
    
    static struct PyModuleDef mrzscanner_module_def = {
      PyModuleDef_HEAD_INIT,
      "mrzscanner",
      "Internal \"mrzscanner\" module",
      -1,
      mrzscanner_methods
    };
    
    PyMODINIT_FUNC PyInit_mrzscanner(void)
    {
        PyObject *module = PyModule_Create(&mrzscanner_module_def);
        if (module == NULL)
            INITERROR;
    
    
        if (PyType_Ready(&DynamsoftMrzReaderType) < 0)
           INITERROR;
    
        Py_INCREF(&DynamsoftMrzReaderType);
        PyModule_AddObject(module, "DynamsoftMrzReader", (PyObject *)&DynamsoftMrzReaderType);
    
        if (PyType_Ready(&MrzResultType) < 0)
           INITERROR;
    
        Py_INCREF(&MrzResultType);
        PyModule_AddObject(module, "MrzResult", (PyObject *)&MrzResultType);
    
        PyModule_AddStringConstant(module, "version", DLR_GetVersion());
        return module;
    }
    
    DynamsoftMrzReader 클래스는 dynamsoft_mrz_reader.h에 정의되어 있습니다. 세 가지 방법을 정의합니다.
  • decodeFile() : 이미지 파일에서 MRZ를 인식합니다.
  • decodeMat() : OpenCV Mat에서 MRZ를 인식합니다.
  • loadModel() : JSON 형식의 구성 파일을 구문 분석하여 MRZ 모델을 로드합니다.

  • #ifndef __MRZ_READER_H__
    #define __MRZ_READER_H__
    
    #include <Python.h>
    #include <structmember.h>
    #include "DynamsoftLabelRecognizer.h"
    #include "mrz_result.h"
    
    #define DEBUG 0
    
    typedef struct
    {
        PyObject_HEAD
        void *handler;
    } DynamsoftMrzReader;
    
    static int DynamsoftMrzReader_clear(DynamsoftMrzReader *self)
    {
        if(self->handler) {
            DLR_DestroyInstance(self->handler);
            self->handler = NULL;
        }
        return 0;
    }
    
    static void DynamsoftMrzReader_dealloc(DynamsoftMrzReader *self)
    {
        DynamsoftMrzReader_clear(self);
        Py_TYPE(self)->tp_free((PyObject *)self);
    }
    
    static PyObject *DynamsoftMrzReader_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    {
        DynamsoftMrzReader *self;
    
        self = (DynamsoftMrzReader *)type->tp_alloc(type, 0);
        if (self != NULL)
        {
            self->handler = DLR_CreateInstance();
        }
    
        return (PyObject *)self;
    }
    
    static PyMethodDef instance_methods[] = {
      {"decodeFile", decodeFile, METH_VARARGS, NULL},
      {"decodeMat", decodeMat, METH_VARARGS, NULL},
      {"loadModel", loadModel, METH_VARARGS, NULL},
      {NULL, NULL, 0, NULL}       
    };
    
    static PyTypeObject DynamsoftMrzReaderType = {
        PyVarObject_HEAD_INIT(NULL, 0) "mrzscanner.DynamsoftMrzReader", /* tp_name */
        sizeof(DynamsoftMrzReader),                              /* tp_basicsize */
        0,                                                           /* tp_itemsize */
        (destructor)DynamsoftMrzReader_dealloc,                  /* tp_dealloc */
        0,                                                           /* tp_print */
        0,                                                           /* tp_getattr */
        0,                                                           /* tp_setattr */
        0,                                                           /* tp_reserved */
        0,                                                           /* tp_repr */
        0,                                                           /* tp_as_number */
        0,                                                           /* tp_as_sequence */
        0,                                                           /* tp_as_mapping */
        0,                                                           /* tp_hash  */
        0,                                                           /* tp_call */
        0,                                                           /* tp_str */
        PyObject_GenericGetAttr,                                                           /* tp_getattro */
        PyObject_GenericSetAttr,                                                           /* tp_setattro */
        0,                                                           /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,                    /*tp_flags*/
        "DynamsoftMrzReader",                          /* tp_doc */
        0,                                                           /* tp_traverse */
        0,                                                           /* tp_clear */
        0,                                                           /* tp_richcompare */
        0,                                                           /* tp_weaklistoffset */
        0,                                                           /* tp_iter */
        0,                                                           /* tp_iternext */
        instance_methods,                                                 /* tp_methods */
        0,                                                 /* tp_members */
        0,                                                           /* tp_getset */
        0,                                                           /* tp_base */
        0,                                                           /* tp_dict */
        0,                                                           /* tp_descr_get */
        0,                                                           /* tp_descr_set */
        0,                                                           /* tp_dictoffset */
        0,                       /* tp_init */
        0,                                                           /* tp_alloc */
        DynamsoftMrzReader_new,                                  /* tp_new */
    };
    
    #endif
    

    모델 구성 파일은 다음과 같습니다.

    {
       "CharacterModelArray" : [
        {
          "DirectoryPath": "model",
          "FilterFilePath": "",
          "Name": "MRZ"
        }
       ],
       "LabelRecognizerParameterArray" : [
          {
             "BinarizationModes" : [
                {
                   "BlockSizeX" : 0,
                   "BlockSizeY" : 0,
                   "EnableFillBinaryVacancy" : 1,
                   "LibraryFileName" : "",
                   "LibraryParameters" : "",
                   "Mode" : "BM_LOCAL_BLOCK",
                   "ThreshValueCoefficient" : 15
                }
             ],
             "CharacterModelName" : "MRZ",
             "LetterHeightRange" : [ 5, 1000, 1 ],
             "LineStringLengthRange" : [30, 44],
             "MaxLineCharacterSpacing" : 130,
             "LineStringRegExPattern" : "([ACI][A-Z<][A-Z<]{3}[A-Z0-9<]{9}[0-9][A-Z0-9<]{15}){(30)}|([0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z<]{3}[A-Z0-9<]{11}[0-9]){(30)}|([A-Z<]{0,26}[A-Z]{1,3}[(<<)][A-Z]{1,3}[A-Z<]{0,26}<{0,26}){(30)}|([ACIV][A-Z<][A-Z<]{3}([A-Z<]{0,27}[A-Z]{1,3}[(<<)][A-Z]{1,3}[A-Z<]{0,27}){(31)}){(36)}|([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{8}){(36)}|([PV][A-Z<][A-Z<]{3}([A-Z<]{0,35}[A-Z]{1,3}[(<<)][A-Z]{1,3}[A-Z<]{0,35}<{0,35}){(39)}){(44)}|([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{14}[A-Z0-9<]{2}){(44)}",
             "MaxThreadCount" : 4,
             "Name" : "locr",
             "TextureDetectionModes" :[
                {
                    "Mode" : "TDM_GENERAL_WIDTH_CONCENTRATION",
                    "Sensitivity" : 8
                }
             ],
             "ReferenceRegionNameArray" : [ "DRRegion" ]
          }
       ],
       "LineSpecificationArray" : [
        {
            "Name":"L0",
            "LineNumber":"",
            "BinarizationModes" : [
                {
                   "BlockSizeX" : 30,
                   "BlockSizeY" : 30,
                   "Mode" : "BM_LOCAL_BLOCK"
                }
             ]
        }
        ],
       "ReferenceRegionArray" : [
          {
             "Localization" : {
                "FirstPoint" : [ 0, 0 ],
                "SecondPoint" : [ 100, 0 ],
                "ThirdPoint" : [ 100, 100 ],
                "FourthPoint" : [ 0, 100 ],
                "MeasuredByPercentage" : 1,
                "SourceType" : "LST_MANUAL_SPECIFICATION"
             },
             "Name" : "DRRegion",
             "TextAreaNameArray" : [ "DTArea" ]
          }
       ],
       "TextAreaArray" : [
          {
             "LineSpecificationNameArray" : ["L0"],
             "Name" : "DTArea",
             "FirstPoint" : [ 0, 0 ],
             "SecondPoint" : [ 100, 0 ],
             "ThirdPoint" : [ 100, 100 ],
             "FourthPoint" : [ 0, 100 ]
          }
       ]
    }
    

    DirectoryPath 를 제외하고는 기본 매개변수를 변경할 필요가 없습니다. 이 매개변수는 실행 디렉토리에 대한 상대 경로이거나 모델 파일이 있는 디렉토리에 대한 절대 경로일 수 있습니다. Python에서 모델 파일을 성공적으로 로드하려면 MRZ.json에 모델 파일의 절대 경로를 설정해야 합니다. Python 환경에 따라 달라지는 Pythonsite-packages 폴더에 MRZ 스캐너 SDK가 설치되어야 모델 경로가 확정됩니다. 따라서 MRZ.json에서 구현된 get_model_path() 함수를 호출할 때 __init__.py에서 모델 파일의 절대 경로를 확인하고 동적으로 수정합니다.

    def get_model_path():
        config_file = os.path.join(os.path.dirname(__file__), 'MRZ.json')
        try:
            # open json file
            with open(config_file, 'r+') as f:
                data = json.load(f)
                if data['CharacterModelArray'][0]['DirectoryPath'] == 'model':
                    data['CharacterModelArray'][0]['DirectoryPath'] = os.path.join(os.path.dirname(__file__), 'model')
                    # print(data['CharacterModelArray'][0]['DirectoryPath'])
    
                    # write json file
                    f.seek(0) # rewind
                    f.write(json.dumps(data))
        except Exception as e:
            print(e)
            pass
    
        return config_file
    

    mrz_result.h 파일에는 MRZ 인식 결과의 구조가 포함되어 있습니다.

    typedef struct 
    {
        PyObject_HEAD
        PyObject *confidence;
        PyObject *text;
        PyObject *x1;
        PyObject *y1;
        PyObject *x2;
        PyObject *y2;
        PyObject *x3;
        PyObject *y3;
        PyObject *x4;
        PyObject *y4;
    } MrzResult;
    


    Python에서 MRZ 인식을 수행하려면:

  • 라이센스 키를 설정하십시오.

    mrzscanner.initLicense("your license key")
    


  • MRZ 스캐너 개체를 만듭니다.

    scanner = mrzscanner.createInstance()
    


  • MRZ 인식 모델을 로드합니다.

    scanner.loadModel(mrzscanner.get_model_path())
    


  • MRZ를 알아보려면 decodeFile() 또는 decodeMat()로 전화하세요.

    results = scanner.decodeFile()
    # or
    results = scanner.decodeMat()
    


  • 텍스트 결과를 출력합니다.

    for result in results:
        print(result.text)
    


  • Python C 확장 빌드 및 패키징을 위한 Setup.py 파일 구성



    다음 코드는 Windows 및 Linux용 공유 라이브러리를 사용하여 Python C 확장을 빌드하는 방법을 보여줍니다.

    dbr_lib_dir = ''
    dbr_include = ''
    dbr_lib_name = 'DynamsoftLabelRecognizer'
    
    if sys.platform == "linux" or sys.platform == "linux2":
        # Linux
        dbr_lib_dir = 'lib/linux'
    elif sys.platform == "win32":
        # Windows
        dbr_lib_name = 'DynamsoftLabelRecognizerx64'
        dbr_lib_dir = 'lib/win'
    
    if sys.platform == "linux" or sys.platform == "linux2":
        ext_args = dict(
            library_dirs=[dbr_lib_dir],
            extra_compile_args=['-std=c++11'],
            extra_link_args=["-Wl,-rpath=$ORIGIN"],
            libraries=[dbr_lib_name],
            include_dirs=['include']
        )
    
    
    long_description = io.open("README.md", encoding="utf-8").read()
    
    if sys.platform == "linux" or sys.platform == "linux2" or sys.platform == "darwin":
        module_mrzscanner = Extension(
            'mrzscanner', ['src/mrzscanner.cpp'], **ext_args)
    else:
        module_mrzscanner = Extension('mrzscanner',
                                      sources=['src/mrzscanner.cpp'],
                                      include_dirs=['include'], library_dirs=[dbr_lib_dir], libraries=[dbr_lib_name])
    


    Python 확장을 빌드한 후 한 가지 더 중요한 단계는 Python MRZ 스캐너 모듈을 패키징하기 전에 모델 파일과 모든 종속 공유 라이브러리를 출력 디렉터리에 복사하는 것입니다.

    def copyfiles(src, dst):
        if os.path.isdir(src):
            filelist = os.listdir(src)
            for file in filelist:
                libpath = os.path.join(src, file)
                shutil.copy2(libpath, dst)
        else:
            shutil.copy2(src, dst)
    
    class CustomBuildExt(build_ext.build_ext):
        def run(self):
            build_ext.build_ext.run(self)
            dst = os.path.join(self.build_lib, "mrzscanner")
            copyfiles(dbr_lib_dir, dst)
            filelist = os.listdir(self.build_lib)
            for file in filelist:
                filePath = os.path.join(self.build_lib, file)
                if not os.path.isdir(file):
                    copyfiles(filePath, dst)
                    # delete file for wheel package
                    os.remove(filePath)
    
            model_dest = os.path.join(dst, 'model')
            if (not os.path.exists(model_dest)):
                os.mkdir(model_dest)
    
            copyfiles(os.path.join(os.path.join(
                Path(__file__).parent, 'model')), model_dest)
            shutil.copy2('MRZ.json', dst)
    
    setup(name='mrz-scanner-sdk',
          ...
          cmdclass={
              'build_ext': CustomBuildExt},
          )
    


    패키지를 로컬로 빌드하고 설치하려면:

    python setup.py build install
    


    소스 배포를 빌드하려면:

    python setup.py sdist
    


    휠 배포를 빌드하려면 다음을 수행하십시오.

    pip wheel . --verbose
    # Or
    python setup.py bdist_wheel
    


    Python MRZ 스캐너 SDK 테스트



  • mrzopencv-python을 설치합니다.

    pip install mrz opencv-python
    


  • - `mrz` is used to extract and check MRZ information from recognized text.
    - `opencv-python` is used to display the image.
    
  • SDK를 활성화하기 위한 30-day FREE trial license을 가져옵니다.

  • 이미지 파일에서 MRZ 텍스트를 인식하는 app.py 파일을 생성합니다.

    import argparse
    import mrzscanner
    import cv2
    import sys
    import numpy as np
    
    from mrz.checker.td1 import TD1CodeChecker
    from mrz.checker.td2 import TD2CodeChecker
    from mrz.checker.td3 import TD3CodeChecker
    from mrz.checker.mrva import MRVACodeChecker
    from mrz.checker.mrvb import MRVBCodeChecker
    
    def check(lines):
        try:
            td1_check = TD1CodeChecker(lines)
            if bool(td1_check):
                return "TD1", td1_check.fields()
        except Exception as err:
            pass
    
        try:
            td2_check = TD2CodeChecker(lines)
            if bool(td2_check):
                return "TD2", td2_check.fields()
        except Exception as err:
            pass
    
        try:
            td3_check = TD3CodeChecker(lines)
            if bool(td3_check):
                return "TD3", td3_check.fields()
        except Exception as err:
            pass
    
        try:
            mrva_check = MRVACodeChecker(lines)
            if bool(mrva_check):
                return "MRVA", mrva_check.fields()
        except Exception as err:
            pass
    
        try:
            mrvb_check = MRVBCodeChecker(lines)
            if bool(mrvb_check):
                return "MRVB", mrvb_check.fields()
        except Exception as err:
            pass
    
        return 'No valid MRZ information found'
    
    def scanmrz():
        """
        Command-line script for recognize MRZ info from a given image
        """
        parser = argparse.ArgumentParser(description='Scan MRZ info from a given image')
        parser.add_argument('filename')
        args = parser.parse_args()
        try:
            filename = args.filename
            ui = args.ui
    
            # Get the license key from https://www.dynamsoft.com/customer/license/trialLicense/?product=dlr
            mrzscanner.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==")
    
            scanner = mrzscanner.createInstance()
            scanner.loadModel(mrzscanner.get_model_path())
            results = scanner.decodeFile(filename)
            for result in results:
                print(result.text)
                s += result.text + '\n'
    
            print(check(s[:-1]))
    
        except Exception as err:
            print(err)
            sys.exit(1)
    
    if __name__ == "__main__":
        scanmrz()
    


  • 명령줄 MRZ 스캐닝 응용 프로그램을 실행합니다.

    python app.py
    



  • GitHub 워크플로 구성



    다음과 같이 새 GitHub 작업 워크플로를 만듭니다.

    name: Build and upload to PyPI
    
    on: [push, pull_request]
    
    jobs:
      build_wheels:
        name: Build wheels on ${{ matrix.os }}
        runs-on: ${{ matrix.os }}
        strategy:
          matrix:
            os: [ubuntu-latest, windows-latest]
            python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
    
        steps:
          - uses: actions/checkout@v2
          - name: Set up Python
            uses: actions/setup-python@v4
            with:
              python-version: ${{ matrix.python-version }}
    
          - name: Run test.py in develop mode
            run: |
              python setup.py develop
              python -m pip install opencv-python mrz
              python --version
              python test.py
    
          - name: Build wheels for Linux
            if: matrix.os == 'ubuntu-latest'
            run: |
              pip install -U wheel setuptools auditwheel patchelf
              python setup.py bdist_wheel
              auditwheel repair dist/mrz_scanner_sdk*.whl --plat manylinux2014_$(uname -m)
    
          - name: Build wheels for Windows
            if: matrix.os == 'windows-latest'
            run: |
              pip install -U wheel setuptools
              python setup.py bdist_wheel -d wheelhouse
    
          - uses: actions/upload-artifact@v2
            with:
              path: wheelhouse/*.whl
    
      build_sdist:
        name: Build source distribution
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
    
          - name: Build sdist
            run: python setup.py sdist -d dist
    
          - uses: actions/upload-artifact@v2
            with:
              path: dist/*.tar.gz
    
      upload_pypi:
        needs: [build_wheels, build_sdist]
        runs-on: ubuntu-latest
    
        if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
        steps:
          - uses: actions/download-artifact@v2
            with:
              name: artifact
              path: dist
    
          - uses: pypa/[email protected]
            with:
              user: __token__
              password: ${{ secrets.pypi_password }}
              skip_existing: true
    


    워크플로우는 소스 및 휠 배포를 빌드하고 PyPI에 업로드하도록 구성됩니다.

    PyPI에서 mrz-scanner-sdk 설치



    https://pypi.org/project/mrz-scanner-sdk/

    pip install mrz-scanner-sdk
    


    소스 코드



    https://github.com/yushulx/python-mrz-scanner-sdk

    좋은 웹페이지 즐겨찾기