ranbac commited on
Commit
d3f0d33
·
verified ·
1 Parent(s): 37b255c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -65
app.py CHANGED
@@ -1,118 +1,192 @@
1
- import gradio as gr
2
- import logging
3
  import os
 
 
 
 
 
 
 
 
4
  import re
 
5
  from paddleocr import PaddleOCR
6
- from PIL import Image
7
  import numpy as np
 
8
 
9
- # Tắt log hệ thống
10
- os.environ["CPP_MIN_LOG_LEVEL"] = "3"
11
  logging.getLogger("ppocr").setLevel(logging.WARNING)
12
 
13
- print("Đang khởi tạo PaddleOCR (PaddleX 3.0)...")
14
 
15
- # --- PHẦN SỬA LỖI QUAN TRỌNG ---
16
- # Đã xóa hoàn toàn 'use_gpu=False' để tránh lỗi ValueError
17
  try:
18
- ocr = PaddleOCR(
19
- use_textline_orientation=True,
20
- lang='ch'
21
- )
22
  except Exception as e:
23
- print(f"Lỗi khởi tạo tham số: {e}. Chuyển sang chế độ mặc định.")
24
  ocr = PaddleOCR(lang='ch')
25
 
26
  print("Model đã sẵn sàng!")
27
 
28
- # --- HÀM QUÉT ĐỆ QUY (Để lấy text từ cấu trúc phức tạp) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  def deep_extract_text(data):
30
  found_texts = []
31
- # Nếu là chuỗi -> Lấy luôn
32
  if isinstance(data, str):
33
- if len(data.strip()) > 0:
34
- return [data]
35
  return []
36
- # Nếu là List/Tuple -> Quét từng phần tử
37
  if isinstance(data, (list, tuple)):
38
  for item in data:
39
  found_texts.extend(deep_extract_text(item))
40
- # Nếu là Dict -> Quét Values
41
  elif isinstance(data, dict):
42
  for val in data.values():
43
  found_texts.extend(deep_extract_text(val))
44
- # Nếu là Object lạ -> Quét thuộc tính
45
  elif hasattr(data, '__dict__'):
46
  found_texts.extend(deep_extract_text(data.__dict__))
47
  return found_texts
48
 
49
- # --- HÀM LỌC RÁC (Loại bỏ min, general, .ttf) ---
50
  def clean_text_result(text_list):
51
  cleaned = []
52
- # Danh sách từ khóa rác cần chặn
53
  block_list = ['min', 'max', 'general', 'header', 'footer', 'structure']
54
-
55
  for t in text_list:
56
  t = t.strip()
57
-
58
- # 1. Bỏ qua chuỗi quá ngắn (trừ khi chữ Hán)
59
- if len(t) < 2:
60
- # Kiểm tra Unicode range chữ Hán
61
- if not any(u'\u4e00' <= c <= u'\u9fff' for c in t):
62
- continue
63
-
64
- # 2. Bỏ qua file hệ thống
65
- if t.lower().endswith(('.ttf', '.json', '.pdparams', '.yml', '.yaml')):
66
- continue
67
-
68
- # 3. Bỏ qua từ khóa hệ thống
69
- if t.lower() in block_list:
70
- continue
71
-
72
- # 4. Chỉ lấy dòng có nội dung thực sự
73
- if not re.search(r'[\w\u4e00-\u9fff]', t):
74
- continue
75
-
76
  cleaned.append(t)
77
  return cleaned
78
 
 
79
  def predict(image):
80
  if image is None:
81
- return "请上传图片 / Vui lòng tải ảnh lên."
82
 
83
  try:
 
 
 
 
84
  if isinstance(image, Image.Image):
85
  image = np.array(image)
86
-
87
- # Gọi OCR (Không truyền tham số nào khác)
 
88
  raw_result = ocr.ocr(image)
89
 
90
- # Trích xuất toàn bộ text
91
  all_texts = deep_extract_text(raw_result)
92
-
93
- # Lọc sạch kết quả
94
  final_texts = clean_text_result(all_texts)
 
95
 
96
- if len(final_texts) > 0:
97
- return "\n".join(final_texts)
98
- else:
99
- return "Không tìm thấy văn bản hợp lệ."
100
-
 
 
 
 
 
101
  except Exception as e:
102
  import traceback
103
  traceback.print_exc()
104
- return f"Lỗi xử : {str(e)}"
105
-
106
- # Giao diện Gradio
107
- iface = gr.Interface(
108
- fn=predict,
109
- inputs=gr.Image(type="pil", label="Input Image"),
110
- # Bỏ show_copy_button=True để tránh lỗi với Gradio cũ
111
- outputs=gr.Textbox(label="Kết quả (Đã lọc nhiễu)", lines=15),
112
- title="PaddleOCR Tiếng Trung (PaddleX 3.0 Clean)",
113
- description="Phiên bản đã Fix lỗi use_gpu và tích hợp bộ lọc rác thông minh.",
114
- examples=[]
115
- )
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  if __name__ == "__main__":
118
  iface.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
1
  import os
2
+
3
+ # --- CẤU HÌNH HỆ THỐNG ---
4
+ os.environ["FLAGS_use_mkldnn"] = "0"
5
+ os.environ["FLAGS_enable_mkldnn"] = "0"
6
+ os.environ["DN_ENABLE_MKLDNN"] = "0"
7
+ os.environ["CPP_MIN_LOG_LEVEL"] = "3"
8
+
9
+ import logging
10
  import re
11
+ import gradio as gr
12
  from paddleocr import PaddleOCR
13
+ from PIL import Image, ImageDraw, ImageFont
14
  import numpy as np
15
+ import requests
16
 
17
+ # Tắt log Python
 
18
  logging.getLogger("ppocr").setLevel(logging.WARNING)
19
 
20
+ print("Đang khởi tạo PaddleOCR (Overlay Mode)...")
21
 
 
 
22
  try:
23
+ # Khởi tạo OCR
24
+ ocr = PaddleOCR(use_textline_orientation=True, lang='ch')
 
 
25
  except Exception as e:
26
+ print(f"Lỗi khởi tạo: {e}. Fallback...")
27
  ocr = PaddleOCR(lang='ch')
28
 
29
  print("Model đã sẵn sàng!")
30
 
31
+ # --- HÀM TẢI FONT (Bắt buộc để vẽ tiếng Trung) ---
32
+ def check_and_download_font():
33
+ font_path = "./simfang.ttf"
34
+ if not os.path.exists(font_path):
35
+ print("Đang tải font tiếng Trung (SimFang)...")
36
+ try:
37
+ # Link tải font SimFang (Font chuẩn cho tiếng Trung)
38
+ url = "https://github.com/StellarCN/scp_zh/raw/master/fonts/SimFang.ttf"
39
+ r = requests.get(url, allow_redirects=True)
40
+ with open(font_path, 'wb') as f:
41
+ f.write(r.content)
42
+ print("Đã tải font thành công!")
43
+ except Exception as e:
44
+ print(f"Lỗi tải font: {e}. Sẽ dùng font mặc định (có thể lỗi hiển thị).")
45
+ return None
46
+ return font_path
47
+
48
+ # Tải font ngay khi khởi động app
49
+ FONT_PATH = check_and_download_font()
50
+
51
+ # --- HÀM VẼ OVERLAY ---
52
+ def draw_ocr_results(image, ocr_result, font_path):
53
+ if image is None or ocr_result is None:
54
+ return image
55
+
56
+ # Chuyển sang PIL Image để vẽ (nếu chưa phải)
57
+ if isinstance(image, np.ndarray):
58
+ image = Image.fromarray(image)
59
+
60
+ draw = ImageDraw.Draw(image)
61
+
62
+ # Load Font
63
+ try:
64
+ font_size = 20
65
+ font = ImageFont.truetype(font_path, font_size) if font_path else ImageFont.load_default()
66
+ except:
67
+ font = ImageFont.load_default()
68
+
69
+ # Xử lý cấu trúc dữ liệu để vẽ
70
+ data_list = ocr_result
71
+ if isinstance(ocr_result, list) and len(ocr_result) > 0 and isinstance(ocr_result[0], list):
72
+ data_list = ocr_result[0]
73
+
74
+ for line in data_list:
75
+ # Cấu trúc mong đợi: [[box], [text, score]]
76
+ # box là [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
77
+ if isinstance(line, list) and len(line) == 2:
78
+ box = line[0]
79
+ text_info = line[1]
80
+
81
+ if isinstance(box, list) and isinstance(text_info, (list, tuple)):
82
+ txt = text_info[0]
83
+
84
+ # 1. Vẽ Khung (Polygon)
85
+ try:
86
+ # Chuyển đổi box thành list các tuple (x,y)
87
+ poly_points = [tuple(pt) for pt in box]
88
+ draw.polygon(poly_points, outline="red", width=2)
89
+
90
+ # 2. Vẽ Chữ (Text)
91
+ # Lấy toạ độ điểm đầu tiên để đặt chữ
92
+ txt_x, txt_y = poly_points[0]
93
+ # Vẽ nền đen nhỏ dưới chữ để dễ đọc
94
+ text_bbox = draw.textbbox((txt_x, txt_y - font_size), txt, font=font)
95
+ draw.rectangle(text_bbox, fill="red")
96
+ # Vẽ chữ màu trắng
97
+ draw.text((txt_x, txt_y - font_size), txt, fill="white", font=font)
98
+
99
+ except Exception as e:
100
+ continue # Bỏ qua nếu lỗi toạ độ
101
+
102
+ return image
103
+
104
+ # --- CÁC HÀM XỬ LÝ VĂN BẢN CŨ (Giữ nguyên) ---
105
  def deep_extract_text(data):
106
  found_texts = []
 
107
  if isinstance(data, str):
108
+ if len(data.strip()) > 0: return [data]
 
109
  return []
 
110
  if isinstance(data, (list, tuple)):
111
  for item in data:
112
  found_texts.extend(deep_extract_text(item))
 
113
  elif isinstance(data, dict):
114
  for val in data.values():
115
  found_texts.extend(deep_extract_text(val))
 
116
  elif hasattr(data, '__dict__'):
117
  found_texts.extend(deep_extract_text(data.__dict__))
118
  return found_texts
119
 
 
120
  def clean_text_result(text_list):
121
  cleaned = []
 
122
  block_list = ['min', 'max', 'general', 'header', 'footer', 'structure']
 
123
  for t in text_list:
124
  t = t.strip()
125
+ if len(t) < 2 and not any(u'\u4e00' <= c <= u'\u9fff' for c in t): continue
126
+ if t.lower().endswith(('.ttf', '.json', '.pdparams', '.yml', '.log')): continue
127
+ if t.lower() in block_list: continue
128
+ if not re.search(r'[\w\u4e00-\u9fff]', t): continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  cleaned.append(t)
130
  return cleaned
131
 
132
+ # --- HÀM PREDICT CHÍNH ---
133
  def predict(image):
134
  if image is None:
135
+ return None, "Vui lòng tải ảnh."
136
 
137
  try:
138
+ # Giữ lại bản copy của ảnh gốc để vẽ
139
+ original_image = image.copy()
140
+
141
+ # Convert sang numpy cho OCR engine
142
  if isinstance(image, Image.Image):
143
  image = np.array(image)
144
+
145
+ # 1. Thực hiện OCR
146
+ # cls=True bị bỏ để tránh lỗi, model tự xử lý
147
  raw_result = ocr.ocr(image)
148
 
149
+ # 2. Xử Văn bản (List Text)
150
  all_texts = deep_extract_text(raw_result)
 
 
151
  final_texts = clean_text_result(all_texts)
152
+ text_output = "\n".join(final_texts) if final_texts else "Không tìm thấy văn bản."
153
 
154
+ # 3. Vẽ Overlay (Image Output)
155
+ try:
156
+ # Truyền ảnh gốc (PIL) và kết quả thô vào hàm vẽ
157
+ annotated_image = draw_ocr_results(original_image, raw_result, FONT_PATH)
158
+ except Exception as e:
159
+ print(f"Lỗi vẽ ảnh: {e}")
160
+ annotated_image = original_image # Trả về ảnh gốc nếu lỗi vẽ
161
+
162
+ return annotated_image, text_output
163
+
164
  except Exception as e:
165
  import traceback
166
  traceback.print_exc()
167
+ return None, f"Lỗi hệ thống: {str(e)}"
168
+
169
+ # --- GIAO DIỆN GRADIO ---
170
+ with gr.Blocks(title="PaddleOCR Overlay") as iface:
171
+ gr.Markdown("# PaddleOCR Chinese - Overlay Mode")
172
+ gr.Markdown("Nhận dạng tiếng Trung và vẽ trực tiếp lên ảnh.")
173
+
174
+ with gr.Row():
175
+ with gr.Column():
176
+ input_img = gr.Image(type="pil", label="Input Image")
177
+ submit_btn = gr.Button("Nhận dạng / Predict", variant="primary")
178
+
179
+ with gr.Column():
180
+ # Output 1: Ảnh đã vẽ chữ
181
+ output_img = gr.Image(type="pil", label="Image Result")
182
+ # Output 2: Text trích xuất
183
+ output_txt = gr.Textbox(label="Text Result", lines=10)
184
+
185
+ submit_btn.click(
186
+ fn=predict,
187
+ inputs=input_img,
188
+ outputs=[output_img, output_txt]
189
+ )
190
 
191
  if __name__ == "__main__":
192
  iface.launch(server_name="0.0.0.0", server_port=7860)