Update app.py
Browse files
app.py
CHANGED
|
@@ -17,35 +17,18 @@ import requests
|
|
| 17 |
# Tắt log thừa
|
| 18 |
logging.getLogger("ppocr").setLevel(logging.WARNING)
|
| 19 |
|
| 20 |
-
|
| 21 |
-
# Cache để lưu các model đã load, tránh load lại gây chậm
|
| 22 |
-
OCR_CACHE = {}
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
| 29 |
-
try:
|
| 30 |
-
# Cấu hình tối ưu (giữ nguyên logic cũ)
|
| 31 |
-
model = PaddleOCR(use_textline_orientation=True,
|
| 32 |
-
use_doc_orientation_classify=False,
|
| 33 |
-
use_doc_unwarping=False,
|
| 34 |
-
lang=lang_code)
|
| 35 |
-
except Exception as e:
|
| 36 |
-
print(f"Lỗi khởi tạo nâng cao cho {lang_code}: {e}. Chuyển về chế độ mặc định.")
|
| 37 |
-
model = PaddleOCR(lang=lang_code)
|
| 38 |
-
|
| 39 |
-
OCR_CACHE[lang_code] = model
|
| 40 |
-
print(f"Model {lang_code} đã sẵn sàng!")
|
| 41 |
-
return model
|
| 42 |
|
| 43 |
-
#
|
| 44 |
-
print("Pre-loading models...")
|
| 45 |
-
get_ocr_model('ch')
|
| 46 |
-
# get_ocr_model('vi') # Bỏ comment nếu muốn load sẵn tiếng Việt ngay khi bật app
|
| 47 |
-
|
| 48 |
-
# --- TẢI FONT (GIỮ NGUYÊN) ---
|
| 49 |
def check_and_download_font():
|
| 50 |
font_path = "./simfang.ttf"
|
| 51 |
if not os.path.exists(font_path):
|
|
@@ -60,7 +43,7 @@ def check_and_download_font():
|
|
| 60 |
|
| 61 |
FONT_PATH = check_and_download_font()
|
| 62 |
|
| 63 |
-
# --- HÀM VẼ ĐA NĂNG
|
| 64 |
def universal_draw(image, raw_data, font_path):
|
| 65 |
if image is None: return image
|
| 66 |
|
|
@@ -91,6 +74,7 @@ def universal_draw(image, raw_data, font_path):
|
|
| 91 |
items_to_draw = []
|
| 92 |
|
| 93 |
# Logic tìm box/text
|
|
|
|
| 94 |
processed = False
|
| 95 |
if isinstance(raw_data, list) and len(raw_data) > 0 and isinstance(raw_data[0], dict):
|
| 96 |
data_dict = raw_data[0]
|
|
@@ -141,7 +125,7 @@ def universal_draw(image, raw_data, font_path):
|
|
| 141 |
|
| 142 |
return canvas
|
| 143 |
|
| 144 |
-
# --- HÀM XỬ LÝ TEXT
|
| 145 |
def deep_extract_text(data):
|
| 146 |
found_texts = []
|
| 147 |
if isinstance(data, str):
|
|
@@ -159,47 +143,39 @@ def clean_text_result(text_list):
|
|
| 159 |
block_list = ['min', 'max', 'general', 'header', 'footer', 'structure']
|
| 160 |
for t in text_list:
|
| 161 |
t = t.strip()
|
| 162 |
-
|
| 163 |
-
# Tuy nhiên với Tiếng Việt, các từ ngắn vẫn quan trọng, nhưng để "giữ nguyên" logic cũ, ta không sửa dòng này.
|
| 164 |
-
if len(t) < 2 and not any(u'\u4e00' <= c <= u'\u9fff' for c in t):
|
| 165 |
-
# Logic cũ ưu tiên tiếng Trung, có thể lọc mất từ tiếng Việt ngắn (ví dụ "a", "à").
|
| 166 |
-
# Nhưng theo yêu cầu "tuyệt đối giữ nguyên", tôi sẽ không sửa logic lọc này.
|
| 167 |
-
continue
|
| 168 |
-
|
| 169 |
if t.lower().endswith(('.ttf', '.json', '.pdparams', '.yml', '.log')): continue
|
| 170 |
if t.lower() in block_list: continue
|
| 171 |
if not re.search(r'[\w\u4e00-\u9fff]', t): continue
|
| 172 |
cleaned.append(t)
|
| 173 |
return cleaned
|
| 174 |
|
| 175 |
-
# --- MAIN PREDICT
|
| 176 |
-
def predict(image
|
| 177 |
if image is None: return None, "Chưa có ảnh.", "No Data"
|
| 178 |
|
| 179 |
try:
|
| 180 |
-
# Lấy model đúng theo ngôn ngữ người dùng chọn
|
| 181 |
-
current_ocr = get_ocr_model(lang_choice)
|
| 182 |
-
|
| 183 |
# Chuẩn bị ảnh đầu vào
|
| 184 |
original_pil = image.copy() if isinstance(image, Image.Image) else Image.fromarray(image).copy()
|
| 185 |
image_np = np.array(image)
|
| 186 |
|
| 187 |
# 1. OCR
|
| 188 |
-
raw_result =
|
| 189 |
|
| 190 |
# 2. XỬ LÝ ẢNH ĐỂ VẼ (KEY FIX: Lấy ảnh từ Preprocessor nếu có)
|
| 191 |
target_image_for_drawing = original_pil
|
| 192 |
|
| 193 |
-
# Kiểm tra xem Paddle có chỉnh sửa ảnh không
|
| 194 |
if isinstance(raw_result, list) and len(raw_result) > 0 and isinstance(raw_result[0], dict):
|
| 195 |
if 'doc_preprocessor_res' in raw_result[0]:
|
| 196 |
proc_res = raw_result[0]['doc_preprocessor_res']
|
|
|
|
| 197 |
if 'output_img' in proc_res:
|
| 198 |
print("Phát hiện ảnh đã qua xử lý hình học. Đang đồng bộ tọa độ...")
|
| 199 |
numpy_img = proc_res['output_img']
|
| 200 |
target_image_for_drawing = Image.fromarray(numpy_img)
|
| 201 |
|
| 202 |
-
# 3. Vẽ lên ảnh ĐÚNG
|
| 203 |
annotated_image = universal_draw(target_image_for_drawing, raw_result, FONT_PATH)
|
| 204 |
|
| 205 |
# 4. Xử lý Text
|
|
@@ -209,7 +185,7 @@ def predict(image, lang_choice):
|
|
| 209 |
|
| 210 |
# Debug Info
|
| 211 |
debug_str = str(raw_result)[:1000]
|
| 212 |
-
debug_info = f"
|
| 213 |
|
| 214 |
return annotated_image, text_output, debug_info
|
| 215 |
|
|
@@ -217,15 +193,13 @@ def predict(image, lang_choice):
|
|
| 217 |
import traceback
|
| 218 |
return image, f"Lỗi: {str(e)}", traceback.format_exc()
|
| 219 |
|
| 220 |
-
# --- GIAO DIỆN
|
| 221 |
-
with gr.Blocks(title="PaddleOCR
|
| 222 |
-
gr.Markdown("## PaddleOCR
|
| 223 |
|
| 224 |
with gr.Row():
|
| 225 |
with gr.Column():
|
| 226 |
input_img = gr.Image(type="pil", label="Input Image")
|
| 227 |
-
# Thêm Dropdown chọn ngôn ngữ
|
| 228 |
-
lang_select = gr.Dropdown(choices=["ch", "vi", "en"], value="ch", label="Chọn Ngôn ngữ (Language)")
|
| 229 |
submit_btn = gr.Button("RUN OCR", variant="primary")
|
| 230 |
|
| 231 |
with gr.Column():
|
|
@@ -239,7 +213,7 @@ with gr.Blocks(title="PaddleOCR Multi-Lang Overlay") as iface:
|
|
| 239 |
|
| 240 |
submit_btn.click(
|
| 241 |
fn=predict,
|
| 242 |
-
inputs=
|
| 243 |
outputs=[output_img, output_txt, output_debug]
|
| 244 |
)
|
| 245 |
|
|
|
|
| 17 |
# Tắt log thừa
|
| 18 |
logging.getLogger("ppocr").setLevel(logging.WARNING)
|
| 19 |
|
| 20 |
+
print("Đang khởi tạo PaddleOCR (Coordinate Sync Mode)...")
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
try:
|
| 23 |
+
ocr = PaddleOCR(use_textline_orientation=True, use_doc_orientation_classify=False,
|
| 24 |
+
use_doc_unwarping=False, lang='ch')
|
| 25 |
+
except Exception as e:
|
| 26 |
+
print(f"Lỗi khởi tạo: {e}. Chuyển về chế độ mặc định.")
|
| 27 |
+
ocr = PaddleOCR(lang='ch')
|
| 28 |
|
| 29 |
+
print("Model đã sẵn sàng!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
+
# --- TẢI FONT ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
def check_and_download_font():
|
| 33 |
font_path = "./simfang.ttf"
|
| 34 |
if not os.path.exists(font_path):
|
|
|
|
| 43 |
|
| 44 |
FONT_PATH = check_and_download_font()
|
| 45 |
|
| 46 |
+
# --- HÀM VẼ ĐA NĂNG ---
|
| 47 |
def universal_draw(image, raw_data, font_path):
|
| 48 |
if image is None: return image
|
| 49 |
|
|
|
|
| 74 |
items_to_draw = []
|
| 75 |
|
| 76 |
# Logic tìm box/text
|
| 77 |
+
# Ưu tiên cấu trúc PaddleX: rec_texts + dt_polys
|
| 78 |
processed = False
|
| 79 |
if isinstance(raw_data, list) and len(raw_data) > 0 and isinstance(raw_data[0], dict):
|
| 80 |
data_dict = raw_data[0]
|
|
|
|
| 125 |
|
| 126 |
return canvas
|
| 127 |
|
| 128 |
+
# --- HÀM XỬ LÝ TEXT ---
|
| 129 |
def deep_extract_text(data):
|
| 130 |
found_texts = []
|
| 131 |
if isinstance(data, str):
|
|
|
|
| 143 |
block_list = ['min', 'max', 'general', 'header', 'footer', 'structure']
|
| 144 |
for t in text_list:
|
| 145 |
t = t.strip()
|
| 146 |
+
if len(t) < 2 and not any(u'\u4e00' <= c <= u'\u9fff' for c in t): continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
if t.lower().endswith(('.ttf', '.json', '.pdparams', '.yml', '.log')): continue
|
| 148 |
if t.lower() in block_list: continue
|
| 149 |
if not re.search(r'[\w\u4e00-\u9fff]', t): continue
|
| 150 |
cleaned.append(t)
|
| 151 |
return cleaned
|
| 152 |
|
| 153 |
+
# --- MAIN PREDICT ---
|
| 154 |
+
def predict(image):
|
| 155 |
if image is None: return None, "Chưa có ảnh.", "No Data"
|
| 156 |
|
| 157 |
try:
|
|
|
|
|
|
|
|
|
|
| 158 |
# Chuẩn bị ảnh đầu vào
|
| 159 |
original_pil = image.copy() if isinstance(image, Image.Image) else Image.fromarray(image).copy()
|
| 160 |
image_np = np.array(image)
|
| 161 |
|
| 162 |
# 1. OCR
|
| 163 |
+
raw_result = ocr.ocr(image_np)
|
| 164 |
|
| 165 |
# 2. XỬ LÝ ẢNH ĐỂ VẼ (KEY FIX: Lấy ảnh từ Preprocessor nếu có)
|
| 166 |
target_image_for_drawing = original_pil
|
| 167 |
|
| 168 |
+
# Kiểm tra xem Paddle có chỉnh sửa ảnh không (dựa vào key 'doc_preprocessor_res')
|
| 169 |
if isinstance(raw_result, list) and len(raw_result) > 0 and isinstance(raw_result[0], dict):
|
| 170 |
if 'doc_preprocessor_res' in raw_result[0]:
|
| 171 |
proc_res = raw_result[0]['doc_preprocessor_res']
|
| 172 |
+
# Nếu có ảnh đầu ra đã chỉnh sửa (output_img)
|
| 173 |
if 'output_img' in proc_res:
|
| 174 |
print("Phát hiện ảnh đã qua xử lý hình học. Đang đồng bộ tọa độ...")
|
| 175 |
numpy_img = proc_res['output_img']
|
| 176 |
target_image_for_drawing = Image.fromarray(numpy_img)
|
| 177 |
|
| 178 |
+
# 3. Vẽ lên ảnh ĐÚNG (Target Image)
|
| 179 |
annotated_image = universal_draw(target_image_for_drawing, raw_result, FONT_PATH)
|
| 180 |
|
| 181 |
# 4. Xử lý Text
|
|
|
|
| 185 |
|
| 186 |
# Debug Info
|
| 187 |
debug_str = str(raw_result)[:1000]
|
| 188 |
+
debug_info = f"Used Image Source: {'Preprocessed' if target_image_for_drawing != original_pil else 'Original'}\nData Preview:\n{debug_str}..."
|
| 189 |
|
| 190 |
return annotated_image, text_output, debug_info
|
| 191 |
|
|
|
|
| 193 |
import traceback
|
| 194 |
return image, f"Lỗi: {str(e)}", traceback.format_exc()
|
| 195 |
|
| 196 |
+
# --- GIAO DIỆN ---
|
| 197 |
+
with gr.Blocks(title="PaddleOCR Perfect Overlay") as iface:
|
| 198 |
+
gr.Markdown("## PaddleOCR Chinese - High Precision Overlay")
|
| 199 |
|
| 200 |
with gr.Row():
|
| 201 |
with gr.Column():
|
| 202 |
input_img = gr.Image(type="pil", label="Input Image")
|
|
|
|
|
|
|
| 203 |
submit_btn = gr.Button("RUN OCR", variant="primary")
|
| 204 |
|
| 205 |
with gr.Column():
|
|
|
|
| 213 |
|
| 214 |
submit_btn.click(
|
| 215 |
fn=predict,
|
| 216 |
+
inputs=input_img,
|
| 217 |
outputs=[output_img, output_txt, output_debug]
|
| 218 |
)
|
| 219 |
|