記錄一次OCR程式開發的嘗試

Al發表於2020-09-14

記錄一次OCR程式開發的嘗試

最近工作中涉及到一部分文件和紙質文件的校驗工作,就想把紙質檔案拍下來,用文字來互相校驗。想到之前呼叫有道智雲介面做了文件翻譯。看了下OCR文字識別的API介面,有道提供了多種OCR識別的不同介面,有手寫體、印刷體、表格、整題識別、購物小票識別、身份證、名片等。乾脆這次就繼續用有道智雲介面做個小demo,把這些功能都試了試,當練手,也當為以後的可能用到的功能做準備了。

呼叫API介面的準備工作

首先,是需要在有道智雲的個人頁面上建立例項、建立應用、繫結應用和例項,獲取到應用的id和金鑰。具體個人註冊的過程和應用建立過程詳見文章分享一次批量檔案翻譯的開發過程

開發過程詳細介紹

下面介紹具體的程式碼開發過程:

這次的demo使用python3開發,包括maindow.py,ocrprocesser.py,ocrtools.py三個檔案。介面部分,為了簡化開發過程,使用python自帶的tkinter庫,提供選擇待識別檔案和識別型別、展示識別結果的功能;ocrprocesser.py根據所選型別呼叫相應api介面,完成識別過程並返回結果;ocrtools.py封裝了經整理後的有道ocr 的各類api,實現了分類呼叫。

  1. 介面部分:

    介面部分程式碼如下,使用了tkinter的grid來排列元素。

    root=tk.Tk()
    root.title("netease youdao ocr test")
    frm = tk.Frame(root)
    frm.grid(padx='50', pady='50')
    
    btn_get_file = tk.Button(frm, text='選擇待識別圖片', command=get_files)
    btn_get_file.grid(row=0, column=0,  padx='10', pady='20')
    text1 = tk.Text(frm, width='40', height='5')
    text1.grid(row=0, column=1)
    
    combox=ttk.Combobox(frm,textvariable=tk.StringVar(),width=38)
    combox["value"]=img_type_dict
    combox.current(0)
    combox.bind("<<ComboboxSelected>>",get_img_type)
    combox.grid(row=1,column=1)
    
    label=tk.Label(frm,text="識別結果:")
    label.grid(row=2,column=0)
    text_result=tk.Text(frm,width='40',height='10')
    text_result.grid(row=2,column=1)
    
    btn_sure=tk.Button(frm,text="開始識別",command=ocr_files)
    btn_sure.grid(row=3,column=1)
    btn_clean=tk.Button(frm,text="清空",command=clean_text)
    btn_clean.grid(row=3,column=2)
    
    root.mainloop()
    

    其中btn_sure的繫結事件ocr_files()將檔案路徑和識別型別傳入ocrprocesser:

    def ocr_files():
        if ocr_model.img_paths:
            ocr_result=ocr_model.ocr_files()
            text_result.insert(tk.END,ocr_result)
        else :
            tk.messagebox.showinfo("提示","無檔案")
    
  2. ocrprocesser中主要方法為ocr_files(),將圖片base64處理後呼叫封裝的api。

    def ocr_files(self):
        for img_path in self.img_paths:
            img_file_name=os.path.basename(img_path).split('.')[0]
            #print('==========='+img_file_name+'===========')
            f=open(img_path,'rb')
            img_code=base64.b64encode(f.read()).decode('utf-8')
            f.close()
            print(img_code)
            ocr_result= self.ocr_by_netease(img_code, self.img_type)
            print(ocr_result)
            return ocr_result
    
  3. 經本人通讀整理有道api的文件,大致分為以下四個api入口:手寫體/印刷體識別身份證/名片識別表格識別整題識別,每個介面的url不同,請求引數也不全一致,因此demo中首先根據識別型別加以區分:

    # 0-hand write
    # 1-print
    # 2-ID card
    # 3-name card
    # 4-table
    # 5-problem
    def get_ocr_result(img_code,img_type):
        if img_type==0 or img_type==1:
            return ocr_common(img_code)
        elif img_type==2 or img_type==3 :
            return ocr_card(img_code,img_type)
        elif img_type==4:
            return ocr_table(img_code)
        elif img_type==5:
            return ocr_problem(img_code)
        else:
            return "error:undefined type!"
    

    而後根據介面所需的引數組織data等欄位,並針對不同介面的返回值進行簡單解析和處理,並返回:

    def ocr_common(img_code):
        YOUDAO_URL='https://openapi.youdao.com/ocrapi'
        data = {}
        data['detectType'] = '10012'
        data['imageType'] = '1'
        data['langType'] = 'auto'
        data['img'] =img_code
        data['docType'] = 'json'
        data=get_sign_and_salt(data,img_code)
        response=do_request(YOUDAO_URL,data)['regions']
        result=[]
        for r in response:
            for line in r['lines']:
                result.append(line['text'])
        return result
    
    
    def ocr_card(img_code,img_type):
        YOUDAO_URL='https://openapi.youdao.com/ocr_structure'
        data={}
        if img_type==2:
            data['structureType'] = 'idcard'
        elif img_type==3:
            data['structureType'] = 'namecard'
        data['q'] = img_code
        data['docType'] = 'json'
        data=get_sign_and_salt(data,img_code)
        return do_request(YOUDAO_URL,data)
    
    def ocr_table(img_code):
        YOUDAO_URL='https://openapi.youdao.com/ocr_table'
        data = {}
        data['type'] = '1'
        data['q'] = img_code
        data['docType'] = 'json'
        data=get_sign_and_salt(data,img_code)
        return do_request(YOUDAO_URL,data)
    
    def ocr_problem(img_code):
        YOUDAO_URL='https://openapi.youdao.com/ocr_formula'
        data = {}
        data['detectType'] = '10011'
        data['imageType'] = '1'
        data['img'] = img_code
        data['docType'] = 'json'
        data=get_sign_and_salt(data,img_code)
        response=do_request(YOUDAO_URL,data)['regions']
        result = []
        for r in response:
            for line in r['lines']:
                for l in line:
                    result.append(l['text'])
        return result
    

    get_sign_and_salt()為data加入了必要的簽名等資訊:

    def get_sign_and_salt(data,img_code):
        data['signType'] = 'v3'
        curtime = str(int(time.time()))
        data['curtime'] = curtime
        salt = str(uuid.uuid1())
        signStr = APP_KEY + truncate(img_code) + salt + curtime + APP_SECRET
        sign = encrypt(signStr)
        data['appKey'] = APP_KEY
        data['salt'] = salt
        data['sign'] = sign
        return data
    

效果展示

手寫體結果展示:

印刷體(程式媛拿來程式碼識別一番):

名片識別,這裡我找來了一個名片模板,看起來準度還是可以的:

身份證(同樣是模板):

表格識別(這超長的json, >_< emmm......):

整題識別(公式識別也有做,識別結果json比較長,看起來沒那麼直觀,就不在這裡貼了):

整題識別

總結

總的而言,介面功能還是很強大的,各種都支援。就是視覺演算法工程師沒有做分類功能,需要自己分別對每一類的影像進行分介面呼叫,而且介面完全不可混用,比如在開發過程中我將名片圖片當作身份證提交給api,結果返回了“Items not found!”,對於呼叫api的開發者來講有點麻煩,當然這樣也在一定程度上提高了識別準確率,而且個人猜測應該也是為了方便分介面計費 : P。

專案地址:https://github.com/LemonQH/WordPicsOCRDemo

相關文章