#-*- coding: UTF-8 -*- # Copyright 2022 the HuggingFace Team. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import base64 import traceback import gradio as gr import cv2 from paddlenlp import Taskflow from paddlenlp.utils.doc_parser import DocParser doc_parser = DocParser() task_instance = Taskflow( "information_extraction", model="uie-x-base", task_path="PaddlePaddle/uie-x-base", from_hf_hub=True, schema="") examples = [ [ "business_card.png", "Name;Title;Web Link;Email;Address", ], [ "license.jpeg", "Name;DOB;ISS;EXP", ], [ "invoice.jpeg", "名称;纳税人识别号;开票日期", ], [ "custom.jpeg", "收发货人;进口口岸;进口日期;运输方式;征免性质;境内目的地;运输工具名称;包装种类;件数;合同协议号" ], [ "resume.png", "职位;年龄;学校|时间;学校|专业", ], ] example_files = { "Name;Title;Web Link;Email;Address": "business_card.png", "Name;DOB;ISS;EXP": "license.jpeg", "职位;年龄;学校|时间;学校|专业": "resume.png", "收发货人;进口口岸;进口日期;运输方式;征免性质;境内目的地;运输工具名称;包装种类;件数;合同协议号": "custom.jpeg", "名称;纳税人识别号;开票日期": "invoice.jpeg", } lang_map = { "resume.png": "ch", "custom.jpeg": "ch", "business_card.png": "en", "invoice.jpeg": "ch", "license.jpeg": "en", } def dbc2sbc(s): rs = "" for char in s: code = ord(char) if code == 0x3000: code = 0x0020 else: code -= 0xfee0 if not (0x0021 <= code and code <= 0x7e): rs += char continue rs += chr(code) return rs def process_path(path): error = None if path: try: images_list = [doc_parser.read_image(path)] return ( path, gr.update(visible=True, value=images_list), gr.update(visible=True), gr.update(visible=False, value=None), gr.update(visible=False, value=None), None, ) except Exception as e: traceback.print_exc() error = str(e) return ( None, gr.update(visible=False, value=None), gr.update(visible=False), gr.update(visible=False, value=None), gr.update(visible=False, value=None), gr.update(visible=True, value=error) if error is not None else None, None, ) def process_upload(file): if file: return process_path(file.name) else: return ( None, gr.update(visible=False, value=None), gr.update(visible=False), gr.update(visible=False, value=None), gr.update(visible=False, value=None), None, ) def BGR2RGB(img): pilimg = img.copy() pilimg[:, :, 0] = img[:, :, 2] pilimg[:, :, 2] = img[:, :, 0] return pilimg def np2base64(image_np): image_np = BGR2RGB(image_np) image = cv2.imencode('.jpg', image_np)[1] base64_str = str(base64.b64encode(image))[2:-1] return base64_str def get_schema(schema_str): def _is_ch(s): for ch in s: if "\u4e00" <= ch <= "\u9fff": return True return False schema_lang = "ch" if _is_ch(schema_str) else "en" schema = schema_str.split(";") schema_list = [] for s in schema: cand = s.split("|") if len(cand) == 1: schema_list.append(cand[0]) else: subject = cand[0] relations = cand[1:] added = False for a in schema_list: if isinstance(a, dict): if subject in a.keys(): a[subject].extend(relations) added = True break if not added: a = {subject: relations} schema_list.append(a) return schema_list, schema_lang def run_taskflow(document, schema): task_instance.set_schema(schema) return task_instance({'doc': document}) def process_doc(document, schema, ocr_lang, layout_analysis): if not schema: schema = '时间;组织机构;人物' if document is None: return None, None schema, schema_lang = get_schema(dbc2sbc(schema)) argument = { "ocr_lang": ocr_lang, "schema_lang": schema_lang, "layout_analysis": layout_analysis } prediction = run_taskflow(document, schema, argument)[0] img_show = doc_parser.write_image_with_results( document, result=prediction, return_image=True) img_list = [img_show] return ( gr.update(visible=True, value=img_list), gr.update(visible=True, value=prediction), ) def load_example_document(img, schema, ocr_lang, layout_analysis): if img is not None: document = example_files[schema] choice = lang_map[document].split("-") ocr_lang = choice[0] layout_analysis = False if len(choice) == 1 else True preview, answer = process_doc(document, schema, ocr_lang, layout_analysis) return document, schema, preview, gr.update(visible=True), answer else: return None, None, None, gr.update(visible=False), None def read_content(file_path: str) -> str: """read the content of target file """ with open(file_path, 'r', encoding='utf-8') as f: content = f.read() return content CSS = """ #prompt input { font-size: 16px; } #url-textbox { padding: 0 !important; } #short-upload-box .w-full { min-height: 10rem !important; } /* I think something like this can be used to re-shape * the table */ /* .gr-samples-table tr { display: inline; } .gr-samples-table .p-2 { width: 100px; } */ #select-a-file { width: 100%; } #file-clear { padding-top: 2px !important; padding-bottom: 2px !important; padding-left: 8px !important; padding-right: 8px !important; margin-top: 10px; } .gradio-container .gr-button-primary { background: linear-gradient(180deg, #CDF9BE 0%, #AFF497 100%); border: 1px solid #B0DCCC; border-radius: 8px; color: #1B8700; } .gradio-container.dark button#submit-button { background: linear-gradient(180deg, #CDF9BE 0%, #AFF497 100%); border: 1px solid #B0DCCC; border-radius: 8px; color: #1B8700 } table.gr-samples-table tr td { border: none; outline: none; } table.gr-samples-table tr td:first-of-type { width: 0%; } div#short-upload-box div.absolute { display: none !important; } gradio-app > div > div > div > div.w-full > div, .gradio-app > div > div > div > div.w-full > div { gap: 0px 2%; } gradio-app div div div div.w-full, .gradio-app div div div div.w-full { gap: 0px; } gradio-app h2, .gradio-app h2 { padding-top: 10px; } #answer { overflow-y: scroll; color: white; background: #666; border-color: #666; font-size: 20px; font-weight: bold; } #answer span { color: white; } #answer textarea { color:white; background: #777; border-color: #777; font-size: 18px; } #url-error input { color: red; } """ with gr.Blocks(css=CSS) as demo: gr.HTML(read_content("header.html")) gr.Markdown( "**UIE-X 🧾 🎓** is a universal information extraction engine which supports both document and text inputs. It is powered by BAIDU and released on PaddleNLP. " "Our extraction target(schema) can be set in natural language without limitation, and it also supports most extraction tasks. " "The model performs well on zero-shot and few-shot settings. Moreover, on PaddleNLP, we provide a comprehensive and easy-to-use fine-tuning customization workflow." "For more details, please visit the [GitHub](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/information_extraction)" ) document = gr.Variable() is_text = gr.Variable() example_schema = gr.Textbox(visible=False) example_image = gr.Image(visible=False) with gr.Row(equal_height=True): with gr.Column(): with gr.Row(): gr.Markdown("## 1. 选择文件 / Select a file 📄", elem_id="select-a-file") img_clear_button = gr.Button( "Clear", variant="secondary", elem_id="file-clear", visible=False ) image = gr.Gallery(visible=False) with gr.Row(equal_height=True): with gr.Column(): with gr.Row(): url = gr.Textbox( show_label=False, placeholder="URL", lines=1, max_lines=1, elem_id="url-textbox", ) submit = gr.Button("Get") url_error = gr.Textbox( visible=False, elem_id="url-error", max_lines=1, interactive=False, label="Error", ) gr.Markdown("— or —") upload = gr.File(label=None, interactive=True, elem_id="short-upload-box") gr.Examples( examples=examples, inputs=[example_image, example_schema], ) with gr.Column(): gr.Markdown("## 2. 信息抽取 / Information extraction ℹ️ ") gr.Markdown("### 👉 设置schema") gr.Markdown("实体抽取:实体类别之间以';'分割,例如 **人物;组织机构**") gr.Markdown("关系抽取:需配置主体和关系类别,中间以'|'分割,例如 **人物|出生时间;人物|邮箱**") gr.Markdown("### 👉 Set a schema") gr.Markdown("Entity extraction: entity label should be separated by ';', e.g. **Person;Organization**") gr.Markdown("Relation extraction: set the subject and relation type, separated by '|', e.g. **Person|Date;Person|Email**") gr.Markdown("### 💪 模型定制 / Model customization") gr.Markdown("我们建议通过[数据标注+微调](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/information_extraction/document)的流程进一步增强模型在特定场景的效果") gr.Markdown("We recommend to further improve the extraction performance in specific domain through the process of [data annotation & fine-tuning](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/information_extraction/document)") schema = gr.Textbox( label="Schema", placeholder="e.g. Name|Company;Name|Position;Email;Phone Number", lines=1, max_lines=1, ) ocr_lang = gr.Radio( choices=["ch", "en"], value="en", label="OCR语言 / OCR Language (Please choose ch for Chinese images.)", ) layout_analysis = gr.Radio( choices=["yes", "no"], value="no", label="版面分析 / Layout analysis (Better extraction for multi-line text)", ) with gr.Row(): clear_button = gr.Button("Clear", variant="secondary") submit_button = gr.Button( "Submit", variant="primary", elem_id="submit-button" ) with gr.Column(): output = gr.JSON(label="Output", visible=False) for cb in [img_clear_button, clear_button]: cb.click( lambda _: ( gr.update(visible=False, value=None), None, gr.update(visible=False, value=None), gr.update(visible=False), None, None, None, gr.update(visible=False, value=None), None, ), inputs=clear_button, outputs=[ image, document, output, img_clear_button, example_image, upload, url, url_error, schema, ], ) upload.change( fn=process_upload, inputs=[upload], outputs=[document, image, img_clear_button, output, url_error], ) submit.click( fn=process_path, inputs=[url], outputs=[document, image, img_clear_button, output, url_error], ) schema.submit( fn=process_doc, inputs=[document, schema, ocr_lang, layout_analysis], outputs=[image, output], ) submit_button.click( fn=process_doc, inputs=[document, schema, ocr_lang, layout_analysis], outputs=[image, output], ) example_image.change( fn=load_example_document, inputs=[example_image, example_schema, ocr_lang, layout_analysis], outputs=[document, schema, image, img_clear_button, output], ) gr.Markdown("[![Stargazers repo roster for @PaddlePaddle/PaddleNLP](https://reporoster.com/stars/PaddlePaddle/PaddleNLP)](https://github.com/PaddlePaddle/PaddleNLP)") gr.HTML(read_content("footer.html")) if __name__ == "__main__": demo.launch(enable_queue=False)