Luke
commited on
Commit
·
e651999
1
Parent(s):
7336a40
no message
Browse files- IdentifyModel/cardModel.py +26 -0
- Plan/AiLLM.py +16 -0
- Plan/pytesseractOCR.py +27 -0
- Preprocess/preprocessImg.py +19 -0
- app.py +49 -22
- eurotext.png +0 -0
- requirements.txt +6 -1
IdentifyModel/cardModel.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
def parse_id_card(text, validation_type, entities=None):
|
3 |
+
if validation_type == "身分證正面":
|
4 |
+
result = {
|
5 |
+
"解析全文內容": text,
|
6 |
+
"姓名": entities.get('B-PER', '無法解析') if entities else '無法解析',
|
7 |
+
"出生年月日": entities.get('B-DATE', '無法解析') if entities else '無法解析',
|
8 |
+
"發證日期": entities.get('I-DATE', '無法解析') if entities else '無法解析',
|
9 |
+
"統一編號": entities.get('B-NUM', '無法解析') if entities else '無法解析'
|
10 |
+
}
|
11 |
+
elif validation_type == "身分證反面":
|
12 |
+
result = {
|
13 |
+
"解析全文內容": text,
|
14 |
+
"父": entities.get('B-FATHER', '無法解析') if entities else '無法解析',
|
15 |
+
"母": entities.get('B-MOTHER', '無法解析') if entities else '無法解析',
|
16 |
+
"配偶": entities.get('B-SPOUSE', '無法解析') if entities else '無法解析',
|
17 |
+
"出生地": entities.get('B-LOC', '無法解析') if entities else '無法解析',
|
18 |
+
"住址": entities.get('I-LOC', '無法解析') if entities else '無法解析',
|
19 |
+
"編號": entities.get('B-ID', '無法解析') if entities else '無法解析'
|
20 |
+
}
|
21 |
+
else:
|
22 |
+
result = {
|
23 |
+
"解析全文內容": text,
|
24 |
+
}
|
25 |
+
|
26 |
+
return result
|
Plan/AiLLM.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import pytesseract
|
3 |
+
from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline
|
4 |
+
from IdentifyModel.cardModel import parse_id_card
|
5 |
+
|
6 |
+
# 初始化 Taiwanese BERT 模型
|
7 |
+
tokenizer = AutoTokenizer.from_pretrained("ckiplab/bert-base-chinese")
|
8 |
+
model = AutoModelForTokenClassification.from_pretrained("ckiplab/bert-base-chinese-ner")
|
9 |
+
ner_pipeline = pipeline("ner", model=model, tokenizer=tokenizer)
|
10 |
+
|
11 |
+
|
12 |
+
def llm_recognition(image, validation_type, language):
|
13 |
+
text = pytesseract.image_to_string(image, lang=language)
|
14 |
+
ner_results = ner_pipeline(text)
|
15 |
+
entities = {result['entity']: text[result['start']:result['end']] for result in ner_results}
|
16 |
+
return parse_id_card(text, validation_type, entities)
|
Plan/pytesseractOCR.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import cv2
|
2 |
+
import os
|
3 |
+
import pytesseract
|
4 |
+
|
5 |
+
from IdentifyModel.cardModel import parse_id_card
|
6 |
+
from Preprocess.preprocessImg import preprocess_image001
|
7 |
+
|
8 |
+
|
9 |
+
def ocr_recognition(image, validation_type, language):
|
10 |
+
try:
|
11 |
+
preprocessed_image = preprocess_image001(image)
|
12 |
+
custom_config = r'--oem 3 --psm 6'
|
13 |
+
text = pytesseract.image_to_string(preprocessed_image, lang=language, config=custom_config)
|
14 |
+
return parse_id_card(text, validation_type)
|
15 |
+
except Exception as e:
|
16 |
+
return str(e)
|
17 |
+
|
18 |
+
# def ocr_recognition_2(image: str, lang: str = 'chi_tra') -> str:
|
19 |
+
# try:
|
20 |
+
# img = cv2.imread(image)
|
21 |
+
# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
22 |
+
# threshold_img = cv2.threshold(gray, 127, 255, cv2.THRESH_TOZERO)[1]
|
23 |
+
# result = pytesseract.image_to_string(threshold_img, lang=lang)
|
24 |
+
# os.remove(image)
|
25 |
+
# return result
|
26 |
+
# except Exception as e:
|
27 |
+
# return str(e)
|
Preprocess/preprocessImg.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
from PIL import Image, ImageEnhance
|
4 |
+
|
5 |
+
|
6 |
+
# 方案一
|
7 |
+
def preprocess_image001(image):
|
8 |
+
# 將影像轉換為 NumPy 數組
|
9 |
+
image = np.array(image)
|
10 |
+
# 轉為灰階影像
|
11 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
12 |
+
# 調整對比度
|
13 |
+
enhancer = ImageEnhance.Contrast(Image.fromarray(gray))
|
14 |
+
enhanced_image = enhancer.enhance(2)
|
15 |
+
# 二值化
|
16 |
+
_, binary = cv2.threshold(np.array(enhanced_image), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
17 |
+
# 去雜訊
|
18 |
+
denoised = cv2.fastNlMeansDenoising(binary, None, 30, 7, 21)
|
19 |
+
return Image.fromarray(denoised)
|
app.py
CHANGED
@@ -1,13 +1,15 @@
|
|
1 |
-
from PIL import Image
|
2 |
-
import pytesseract
|
3 |
-
import gradio as gr
|
4 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
langs = []
|
6 |
|
7 |
choices = os.popen('tesseract --list-langs').read().split('\n')[1:-1]
|
8 |
|
9 |
-
blocks = gr.Blocks()
|
10 |
-
|
11 |
|
12 |
# If you don't have tesseract executable in your PATH, include the following:
|
13 |
# pytesseract.pytesseract.tesseract_cmd = r'<full_path_to_your_tesseract_executable>'
|
@@ -29,24 +31,49 @@ blocks = gr.Blocks()
|
|
29 |
# print(pytesseract.image_to_osd(Image.open('test.png'))
|
30 |
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
|
38 |
with gr.Blocks() as demo:
|
39 |
-
gr.Markdown("## Hello pytesseract!")
|
40 |
with gr.Row():
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
+
|
3 |
+
import gradio as gr
|
4 |
+
|
5 |
+
from Plan.AiLLM import llm_recognition
|
6 |
+
from Plan.pytesseractOCR import ocr_recognition
|
7 |
+
from Preprocess.preprocessImg import preprocess_image001
|
8 |
+
|
9 |
langs = []
|
10 |
|
11 |
choices = os.popen('tesseract --list-langs').read().split('\n')[1:-1]
|
12 |
|
|
|
|
|
13 |
|
14 |
# If you don't have tesseract executable in your PATH, include the following:
|
15 |
# pytesseract.pytesseract.tesseract_cmd = r'<full_path_to_your_tesseract_executable>'
|
|
|
31 |
# print(pytesseract.image_to_osd(Image.open('test.png'))
|
32 |
|
33 |
|
34 |
+
# 取得所有語言清單
|
35 |
+
languages = os.popen('tesseract --list-langs').read().split('\n')[1:-1]
|
36 |
+
|
37 |
+
|
38 |
+
print(' ======================================================== ')
|
39 |
+
print(' ###### choices:' + choices)
|
40 |
+
print(' ###### GET ENV - TESSDATA_PREFIX:' + os.getenv('TESSDATA_PREFIX'))
|
41 |
+
print(' ###### OS - TESSDATA_PREFIX:' + os.environ['TESSDATA_PREFIX'])
|
42 |
+
# os.environ['TESSDATA_PREFIX'] = os.getenv('TESSDATA_PREFIX')
|
43 |
+
print(' ###### Tesseract_Cmd:' + pytesseract.pytesseract.tesseract_cmd)
|
44 |
+
# pytesseract.pytesseract.tesseract_cmd = os.getenv('TESSDATA_PREFIX')
|
45 |
+
print(' ======================================================== ')
|
46 |
+
|
47 |
+
|
48 |
+
def preprocess_and_ocr(image, validation_type, language):
|
49 |
+
preprocessed_image = preprocess_image001(image)
|
50 |
+
ocr_result = ocr_recognition(preprocessed_image, validation_type, language)
|
51 |
+
return preprocessed_image, ocr_result
|
52 |
+
|
53 |
+
|
54 |
+
def preprocess_and_llm(image, validation_type, language):
|
55 |
+
preprocessed_image = preprocess_image001(image)
|
56 |
+
llm_result = llm_recognition(preprocessed_image, validation_type, language)
|
57 |
+
return preprocessed_image, llm_result
|
58 |
|
59 |
|
60 |
with gr.Blocks() as demo:
|
|
|
61 |
with gr.Row():
|
62 |
+
image_input = gr.Image(type="pil", label="上傳圖片")
|
63 |
+
validation_type = gr.Dropdown(choices=["身分證正面", "身分證反面"], label="驗證類別")
|
64 |
+
language_dropdown = gr.Dropdown(choices=languages, value="chi_tra", label="語言")
|
65 |
+
|
66 |
+
with gr.Row():
|
67 |
+
ocr_button = gr.Button("使用 OCR")
|
68 |
+
llm_button = gr.Button("使用 AI LLM")
|
69 |
+
|
70 |
+
with gr.Row():
|
71 |
+
preprocess_output = gr.Image(label="OCR 預處理圖片")
|
72 |
+
with gr.Row():
|
73 |
+
ocr_output = gr.JSON(label="OCR 解析結果")
|
74 |
+
llm_output = gr.JSON(label="AI LLM 解析結果")
|
75 |
+
|
76 |
+
ocr_button.click(preprocess_and_ocr, inputs=[image_input, validation_type, language_dropdown], outputs=ocr_output)
|
77 |
+
llm_button.click(preprocess_and_llm, inputs=[image_input, validation_type, language_dropdown], outputs=llm_output)
|
78 |
+
|
79 |
+
demo.launch(share=False)
|
eurotext.png
DELETED
Binary file (31.5 kB)
|
|
requirements.txt
CHANGED
@@ -1,2 +1,7 @@
|
|
1 |
gradio
|
2 |
-
pytesseract
|
|
|
|
|
|
|
|
|
|
|
|
1 |
gradio
|
2 |
+
pytesseract
|
3 |
+
transformers
|
4 |
+
Pillow
|
5 |
+
torch
|
6 |
+
huggingface-hub
|
7 |
+
opencv-python
|