KKingzor commited on
Commit
dd516dc
·
verified ·
1 Parent(s): 7197009

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +255 -230
app.py CHANGED
@@ -4,357 +4,382 @@ from PIL import Image
4
  import easyocr
5
  import numpy as np
6
  import re
7
- from datetime import datetime
8
  import traceback
9
 
10
- class StableOCRProcessor:
11
  def __init__(self):
12
  self.reader = None
 
13
  self.initialize_reader()
14
 
15
  def initialize_reader(self):
16
  """安全初始化EasyOCR"""
17
- try:
18
- # 首先嘗試繁體中文+英文
19
- self.reader = easyocr.Reader(['ch_tra', 'en'], gpu=False)
20
- self.lang_config = "繁體中文 + 英文"
21
- print("成功初始化:繁體中文 + 英文")
22
- except Exception as e1:
 
 
 
 
23
  try:
24
- # 如果失敗,嘗試簡體中文+英文
25
- self.reader = easyocr.Reader(['ch_sim', 'en'], gpu=False)
26
- self.lang_config = "簡體中文 + 英文"
27
- print("成功初始化:簡體中文 + 英文")
28
- except Exception as e2:
29
- try:
30
- # 如果還是失敗,只使用英文
31
- self.reader = easyocr.Reader(['en'], gpu=False)
32
- self.lang_config = "僅英文"
33
- print("成功初始化:僅英文")
34
- except Exception as e3:
35
- print(f"初始化失敗:{e3}")
36
- self.reader = None
37
- self.lang_config = "初始化失敗"
38
 
39
- def process_image_safely(self, image):
40
- """安全處理圖片"""
41
  if self.reader is None:
42
- return self.create_error_response("OCR引擎初始化失敗")
 
 
 
43
 
44
  try:
45
- # 確保輸入是PIL Image
46
  if isinstance(image, np.ndarray):
47
- image = Image.fromarray(image)
48
- elif image is None:
49
- return self.create_error_response("請上傳有效的圖片")
50
 
51
- # 轉換為numpy array
52
- img_array = np.array(image)
53
 
54
  # 執行OCR
55
- print("開始執行OCR...")
56
  results = self.reader.readtext(img_array)
57
- print(f"OCR完成,識別到 {len(results)} 個文字區塊")
58
 
59
- # 處理結果
60
- processed_data = []
61
- for i, (bbox, text, confidence) in enumerate(results):
62
- try:
63
- top_left = bbox[0]
64
- bottom_right = bbox[2]
65
- center_y = (top_left[1] + bottom_right[1]) / 2
66
- center_x = (top_left[0] + bottom_right[0]) / 2
67
-
68
- processed_data.append({
69
- '序號': i + 1,
70
- '識別文字': text.strip(),
71
- '信心度': round(float(confidence), 3),
72
- '中心X': int(center_x),
73
- '中心Y': int(center_y),
74
- '左上角X': int(top_left[0]),
75
- '左上角Y': int(top_left[1]),
76
- '右下角X': int(bottom_right[0]),
77
- '右下角Y': int(bottom_right[1]),
78
- '文字類型': self.classify_text(text.strip())
79
- })
80
- except Exception as e:
81
- print(f"處理第{i+1}個結果時出錯:{e}")
82
- continue
83
 
84
- # 創建DataFrame
85
- if processed_data:
86
- df = pd.DataFrame(processed_data)
87
- # 按位置排序
88
- df = df.sort_values(['中心Y', '中心X']).reset_index(drop=True)
89
-
90
- # 嘗試重建表格
91
- table_df = self.rebuild_table(df)
 
92
 
93
- # 創建摘要
94
- summary = self.create_summary(df)
95
-
96
- return df, table_df, summary
97
- else:
98
- return self.create_empty_response("未識別到任何文字")
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  except Exception as e:
101
- error_msg = f"處理圖片時發生:{str(e)}"
102
  print(error_msg)
103
  print(traceback.format_exc())
104
- return self.create_error_response(error_msg)
105
 
106
- def classify_text(self, text):
107
- """分類文字類型"""
108
  if not text:
109
- return '空白'
110
 
111
- try:
112
- # 日期格式 (如: 526日)
113
- if re.search(r'\d+月\d+', text):
114
- return '日期'
115
- # 時間範圍 (如: 07-15)
116
- elif re.search(r'\d{2}-\d{2}', text):
117
- return '時間'
118
- # 純數字
119
- elif re.match(r'^\d+$', text):
120
- return '數字'
121
- # 中文姓名或地名
122
- elif re.match(r'^[\u4e00-\u9fff]{2,4}$', text):
123
- return '中文名稱'
124
- # 包含中文
125
- elif re.search(r'[\u4e00-\u9fff]', text):
126
- return '中文內容'
127
- # 字母
128
- elif re.match(r'^[a-zA-Z]+$', text):
129
- return '英'
130
- else:
131
- return '其他'
132
- except Exception:
133
- return '未知'
 
 
134
 
135
- def rebuild_table(self, df):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  """重建表格結構"""
137
  if df.empty:
138
  return pd.DataFrame()
139
 
140
  try:
141
- # 按Y座標分組
142
- y_threshold = 30
143
  rows = []
 
 
144
  current_row = []
145
  last_y = None
 
146
 
147
- for _, row in df.iterrows():
148
- current_y = row['中心Y']
149
-
150
- if last_y is None or abs(current_y - last_y) <= y_threshold:
151
- current_row.append(row)
152
  else:
153
  if current_row:
 
154
  current_row.sort(key=lambda x: x['中心X'])
155
  rows.append(current_row)
156
- current_row = [row]
157
-
158
- last_y = current_y
159
 
 
160
  if current_row:
161
  current_row.sort(key=lambda x: x['中心X'])
162
  rows.append(current_row)
163
 
164
- # 構建表格
165
  if not rows:
166
  return pd.DataFrame()
167
 
168
- max_cols = max(len(row) for row in rows)
169
  table_data = []
 
170
 
171
- for i, row in enumerate(rows):
172
- row_data = {'行': i + 1}
173
- for j, cell in enumerate(row):
174
- row_data[f'列{j+1}'] = cell['識別文字']
 
 
175
 
176
  # 填充空列
177
- for j in range(len(row), max_cols):
178
- row_data[f'{j+1}'] = ''
 
179
 
180
- table_data.append(row_data)
181
 
182
  return pd.DataFrame(table_data)
183
 
184
  except Exception as e:
185
  print(f"重建表格失敗:{e}")
186
- return pd.DataFrame([{'錯誤': '表格重建失敗'}])
187
 
188
- def create_summary(self, df):
189
- """創建摘要報告"""
190
- try:
191
- if df.empty:
192
- return "未識別到任何內容"
193
-
194
- avg_confidence = df['信心度'].mean()
195
- high_conf = (df['信心度'] >= 0.8).sum()
196
- total_count = len(df)
197
-
198
- summary = f"""
199
- 🔍 OCR識別報告
200
- ═══════════════════════════════════
201
- 📊 基本統計:
202
- • 語言配置:{self.lang_config}
203
- • 識別區塊:{total_count} 個
204
- • 平均信心度:{avg_confidence:.3f}
205
- • 高信心度區塊:{high_conf} 個 ({high_conf/total_count*100:.1f}%)
206
-
207
- 📝 文字類型分布:
208
- """
209
- type_counts = df['文字類型'].value_counts()
210
- for text_type, count in type_counts.items():
211
- percentage = (count / total_count) * 100
212
- summary += f"• {text_type}:{count} 個 ({percentage:.1f}%)\n"
213
-
214
- # 品質評估
215
- if avg_confidence >= 0.8:
216
- summary += "\n✅ 識別品質:優秀"
217
- elif avg_confidence >= 0.6:
218
- summary += "\n⚠️ 識別品質:良好"
219
- else:
220
- summary += "\n❌ 識別品質:需改進"
221
-
222
- return summary
223
-
224
- except Exception as e:
225
- return f"生成報告時發生錯誤:{str(e)}"
226
-
227
- def create_error_response(self, error_msg):
228
- """創建錯誤響應"""
229
- error_df = pd.DataFrame([{'錯誤': error_msg}])
230
- return error_df, error_df, f"❌ 錯誤:{error_msg}"
231
 
232
- def create_empty_response(self, msg):
233
- """創建空響應"""
234
  empty_df = pd.DataFrame()
235
- return empty_df, empty_df, f"ℹ️ {msg}"
236
 
237
- def create_stable_interface():
238
- """創建穩定的Gradio界面"""
239
- processor = StableOCRProcessor()
240
 
241
- def process_with_error_handling(image, min_confidence):
242
- """帶錯誤處理的處理函數"""
 
 
 
243
  try:
244
- df, table_df, summary = processor.process_image_safely(image)
 
245
 
246
  # 應用信心度過濾
247
  if not df.empty and '信心度' in df.columns:
248
- filtered_df = df[df['信心度'] >= min_confidence].reset_index(drop=True)
249
- if len(filtered_df) != len(df):
250
- summary += f"\n\n🔍 已過濾低信心度結果:{len(df) - len(filtered_df)} 個"
251
- df = filtered_df
 
 
252
 
253
  return summary, df, table_df
254
 
255
  except Exception as e:
256
  error_msg = f"處理失敗:{str(e)}"
257
- error_df = pd.DataFrame([{'錯誤': error_msg}])
258
  return error_msg, error_df, error_df
259
 
260
- # 創建界面
261
- with gr.Blocks(title="穩定版中文OCR系統", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
262
  gr.HTML("""
263
- <div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
264
- <h1>🔍 穩定版中文OCR識別系統</h1>
265
- <p>支持中文表格和文檔的OCR識別,自動容錯處理</p>
 
 
266
  </div>
267
  """)
268
 
 
269
  with gr.Row():
 
270
  with gr.Column(scale=1):
271
  image_input = gr.Image(
272
  label="📤 上傳圖片",
273
- type="pil",
274
- height=400
275
  )
276
 
277
- min_confidence = gr.Slider(
278
  minimum=0.0,
279
  maximum=1.0,
280
  value=0.3,
281
  step=0.1,
282
- label="🎯 最低信心度",
283
- info="過濾掉信心度低於此值的結果"
284
  )
285
 
286
- process_btn = gr.Button(
287
  "🚀 開始識別",
288
- variant="primary",
289
- size="lg"
290
  )
291
 
 
292
  with gr.Column(scale=1):
293
- summary_text = gr.Textbox(
294
  label="📊 識別報告",
295
- lines=12,
296
- max_lines=15
297
  )
298
 
 
299
  with gr.Row():
300
  with gr.Column():
301
  gr.Markdown("### 📋 詳細識別結果")
302
- detail_table = gr.Dataframe(
303
- interactive=True,
304
- wrap=True,
305
- height=300
306
  )
307
 
308
  with gr.Column():
309
  gr.Markdown("### 🔄 重建表格")
310
- rebuilt_table = gr.Dataframe(
311
- interactive=True,
312
- wrap=True,
313
- height=300
314
  )
315
 
316
- # 說明區域
317
- with gr.Accordion("💡 使用說明", open=False):
318
  gr.Markdown("""
319
- ### 系統特色
320
- - **智能容錯**:自動處理各種錯誤情況
321
- - 🔧 **多語言支持**:自動選擇最佳語言配
322
- - 📊 **表格重建**:智能重建原始表格結構
323
- - 🎯 **品質控制**:可調整信心度閾值
324
 
325
- ### 使用技巧
326
- 1. **圖片要求**:清晰、對比度高圖片效果最佳
327
- 2. **信心度調整**:降低閾值可獲得更多果,提高閾值可獲得更準確結果
328
- 3. **表格處理**:會自動識別行列結構並重建表格
329
- 4. **結果導出**:可以複製表格數據到其他應用程序
330
 
331
- ### 支持內容
332
- - 中文文字(繁體/簡體)
333
- - 文字母和數字
334
- - 日期和時間格式
335
- - 表格和構化數據
336
  """)
337
 
338
  # 綁定事件
339
- process_btn.click(
340
- fn=process_with_error_handling,
341
- inputs=[image_input, min_confidence],
342
- outputs=[summary_text, detail_table, rebuilt_table]
343
  )
344
 
 
345
  image_input.change(
346
- fn=process_with_error_handling,
347
- inputs=[image_input, min_confidence],
348
- outputs=[summary_text, detail_table, rebuilt_table]
349
  )
350
 
351
- return demo
352
 
353
  if __name__ == "__main__":
354
- demo = create_stable_interface()
355
- demo.launch(
356
  server_name="0.0.0.0",
357
  server_port=7860,
358
- share=True,
359
- show_error=True
360
  )
 
4
  import easyocr
5
  import numpy as np
6
  import re
 
7
  import traceback
8
 
9
+ class FinalOCRProcessor:
10
  def __init__(self):
11
  self.reader = None
12
+ self.lang_config = "未初始化"
13
  self.initialize_reader()
14
 
15
  def initialize_reader(self):
16
  """安全初始化EasyOCR"""
17
+ print("正在初始化OCR引擎...")
18
+
19
+ # 嘗試不同的語言配置
20
+ configs = [
21
+ (['ch_tra', 'en'], "繁體中文+英文"),
22
+ (['ch_sim', 'en'], "簡體中文+英文"),
23
+ (['en'], "僅英文")
24
+ ]
25
+
26
+ for lang_list, description in configs:
27
  try:
28
+ print(f"嘗試配置:{description}")
29
+ self.reader = easyocr.Reader(lang_list, gpu=False)
30
+ self.lang_config = description
31
+ print(f"成功初始化:{description}")
32
+ return
33
+ except Exception as e:
34
+ print(f"✗ 配置 {description} 失敗:{str(e)}")
35
+ continue
36
+
37
+ print("❌ 所有配置都失敗了")
38
+ self.reader = None
39
+ self.lang_config = "初始化失敗"
 
 
40
 
41
+ def process_image(self, image):
42
+ """處理圖片的主要函數"""
43
  if self.reader is None:
44
+ return self.create_error_result("OCR引擎未正確初始化")
45
+
46
+ if image is None:
47
+ return self.create_error_result("請上傳圖片")
48
 
49
  try:
50
+ # 轉換圖片格式
51
  if isinstance(image, np.ndarray):
52
+ img_array = image
53
+ else:
54
+ img_array = np.array(image)
55
 
56
+ print("開始OCR識別...")
 
57
 
58
  # 執行OCR
 
59
  results = self.reader.readtext(img_array)
 
60
 
61
+ print(f"識別完成,找到 {len(results)} 個文字區塊")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
+ if not results:
64
+ return self.create_empty_result("圖片中未找到任何文字")
65
+
66
+ # 處理OCR結果
67
+ data_list = []
68
+ for i, (bbox, text, confidence) in enumerate(results):
69
+ # 計算位置信息
70
+ x_coords = [point[0] for point in bbox]
71
+ y_coords = [point[1] for point in bbox]
72
 
73
+ left = int(min(x_coords))
74
+ right = int(max(x_coords))
75
+ top = int(min(y_coords))
76
+ bottom = int(max(y_coords))
77
+ center_x = int((left + right) / 2)
78
+ center_y = int((top + bottom) / 2)
79
 
80
+ data_list.append({
81
+ '序號': i + 1,
82
+ '識別文字': text.strip(),
83
+ '信心度': round(float(confidence), 3),
84
+ '中心X': center_x,
85
+ '中心Y': center_y,
86
+ '左': left,
87
+ '上': top,
88
+ '右': right,
89
+ '下': bottom,
90
+ '寬度': right - left,
91
+ '高度': bottom - top,
92
+ '類型': self.get_text_type(text.strip())
93
+ })
94
+
95
+ # 創建DataFrame並排序
96
+ df = pd.DataFrame(data_list)
97
+ df = df.sort_values(['中心Y', '中心X']).reset_index(drop=True)
98
+
99
+ # 重新編號
100
+ df['序號'] = range(1, len(df) + 1)
101
+
102
+ # 生成摘要
103
+ summary = self.generate_summary(df)
104
+
105
+ # 嘗試重建表格
106
+ table_df = self.reconstruct_table(df)
107
+
108
+ return df, table_df, summary
109
+
110
  except Exception as e:
111
+ error_msg = f"處理圖片時錯:{str(e)}"
112
  print(error_msg)
113
  print(traceback.format_exc())
114
+ return self.create_error_result(error_msg)
115
 
116
+ def get_text_type(self, text):
117
+ """判斷文字類型"""
118
  if not text:
119
+ return "空白"
120
 
121
+ # 日期格式
122
+ if re.search(r'\d+\d+', text):
123
+ return "期"
124
+
125
+ # 時間格式
126
+ if re.search(r'\d{1,2}[-:]\d{1,2}', text):
127
+ return "時間"
128
+
129
+ # 純數字
130
+ if re.match(r'^\d+$', text):
131
+ return "數字"
132
+
133
+ # 中文或詞語
134
+ if re.match(r'^[\u4e00-\u9fff]{1,6}$', text):
135
+ return "中文"
136
+
137
+ # 包含中的混合內容
138
+ if re.search(r'[\u4e00-\u9fff]', text):
139
+ return "中混合"
140
+
141
+ # 英文
142
+ if re.match(r'^[a-zA-Z\s]+$', text):
143
+ return "英文"
144
+
145
+ return "其他"
146
 
147
+ def generate_summary(self, df):
148
+ """生成識別摘要"""
149
+ if df.empty:
150
+ return "沒有識別到任何內容"
151
+
152
+ total = len(df)
153
+ avg_conf = df['信心度'].mean()
154
+ high_conf = (df['信心度'] >= 0.8).sum()
155
+
156
+ summary = f"""🔍 OCR識別報告
157
+ {'='*40}
158
+ 📊 基本統計
159
+ • 引擎配置:{self.lang_config}
160
+ • 識別區塊:{total} 個
161
+ • 平均信心度:{avg_conf:.3f}
162
+ • 高信心度區塊:{high_conf} 個 ({high_conf/total*100:.1f}%)
163
+
164
+ 📝 文字類型統計
165
+ """
166
+
167
+ # 統計各類型數量
168
+ type_counts = df['類型'].value_counts()
169
+ for text_type, count in type_counts.items():
170
+ percentage = count / total * 100
171
+ summary += f"• {text_type}:{count} 個 ({percentage:.1f}%)\n"
172
+
173
+ # 品質評估
174
+ if avg_conf >= 0.8:
175
+ quality = "優秀 ✅"
176
+ elif avg_conf >= 0.6:
177
+ quality = "良好 ⚠️"
178
+ else:
179
+ quality = "待改進 ❌"
180
+
181
+ summary += f"\n🎯 整體品質:{quality}"
182
+
183
+ return summary
184
+
185
+ def reconstruct_table(self, df):
186
  """重建表格結構"""
187
  if df.empty:
188
  return pd.DataFrame()
189
 
190
  try:
191
+ # 按Y座標分組形成行
 
192
  rows = []
193
+ sorted_df = df.sort_values('中心Y')
194
+
195
  current_row = []
196
  last_y = None
197
+ y_threshold = 25 # Y座標閾值
198
 
199
+ for _, item in sorted_df.iterrows():
200
+ if last_y is None or abs(item['中心Y'] - last_y) <= y_threshold:
201
+ current_row.append(item)
 
 
202
  else:
203
  if current_row:
204
+ # 按X座標排序
205
  current_row.sort(key=lambda x: x['中心X'])
206
  rows.append(current_row)
207
+ current_row = [item]
208
+ last_y = item['中心Y']
 
209
 
210
+ # 添加最後一行
211
  if current_row:
212
  current_row.sort(key=lambda x: x['中心X'])
213
  rows.append(current_row)
214
 
 
215
  if not rows:
216
  return pd.DataFrame()
217
 
218
+ # 構建表格數據
219
  table_data = []
220
+ max_cols = max(len(row) for row in rows)
221
 
222
+ for row_idx, row_items in enumerate(rows):
223
+ row_dict = {'行': row_idx + 1}
224
+
225
+ for col_idx, item in enumerate(row_items):
226
+ col_name = f'第{col_idx + 1}列'
227
+ row_dict[col_name] = item['識別文字']
228
 
229
  # 填充空列
230
+ for col_idx in range(len(row_items), max_cols):
231
+ col_name = f'{col_idx + 1}'
232
+ row_dict[col_name] = ""
233
 
234
+ table_data.append(row_dict)
235
 
236
  return pd.DataFrame(table_data)
237
 
238
  except Exception as e:
239
  print(f"重建表格失敗:{e}")
240
+ return pd.DataFrame([{"錯誤": "表格重建失敗"}])
241
 
242
+ def create_error_result(self, message):
243
+ """創建錯誤結果"""
244
+ error_df = pd.DataFrame([{"錯誤信息": message}])
245
+ return error_df, error_df, f"❌ {message}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ def create_empty_result(self, message):
248
+ """創建空結果"""
249
  empty_df = pd.DataFrame()
250
+ return empty_df, empty_df, f"ℹ️ {message}"
251
 
252
+ def main():
253
+ """主函數,創建Gradio應用"""
 
254
 
255
+ # 初始化處理器
256
+ processor = FinalOCRProcessor()
257
+
258
+ def process_image_wrapper(image, min_confidence):
259
+ """包裝處理函數"""
260
  try:
261
+ # 處理圖片
262
+ df, table_df, summary = processor.process_image(image)
263
 
264
  # 應用信心度過濾
265
  if not df.empty and '信心度' in df.columns:
266
+ original_count = len(df)
267
+ df = df[df['信心度'] >= min_confidence].reset_index(drop=True)
268
+ filtered_count = len(df)
269
+
270
+ if filtered_count < original_count:
271
+ summary += f"\n\n🔍 信心度過濾:保留 {filtered_count}/{original_count} 個結果"
272
 
273
  return summary, df, table_df
274
 
275
  except Exception as e:
276
  error_msg = f"處理失敗:{str(e)}"
277
+ error_df = pd.DataFrame([{"錯誤": error_msg}])
278
  return error_msg, error_df, error_df
279
 
280
+ # 創建Gradio界面
281
+ with gr.Blocks(
282
+ title="中文OCR識別系統",
283
+ theme=gr.themes.Default()
284
+ ) as app:
285
+
286
+ # 標題和說明
287
  gr.HTML("""
288
+ <div style="text-align: center; padding: 20px; background: #f0f2f6; border-radius: 10px; margin-bottom: 20px;">
289
+ <h1 style="color: #1f2937; margin-bottom: 10px;">🔍 中文OCR識別系統</h1>
290
+ <p style="color: #6b7280; font-size: 16px;">
291
+ 支持中文、英文、數字識別,自動重建表格結構
292
+ </p>
293
  </div>
294
  """)
295
 
296
+ # 主要操作區域
297
  with gr.Row():
298
+ # 左側:圖片上傳和控制
299
  with gr.Column(scale=1):
300
  image_input = gr.Image(
301
  label="📤 上傳圖片",
302
+ type="pil"
 
303
  )
304
 
305
+ confidence_slider = gr.Slider(
306
  minimum=0.0,
307
  maximum=1.0,
308
  value=0.3,
309
  step=0.1,
310
+ label="🎯 最低信心度閾值"
 
311
  )
312
 
313
+ process_button = gr.Button(
314
  "🚀 開始識別",
315
+ variant="primary"
 
316
  )
317
 
318
+ # 右側:摘要報告
319
  with gr.Column(scale=1):
320
+ summary_output = gr.Textbox(
321
  label="📊 識別報告",
322
+ lines=15,
323
+ max_lines=20
324
  )
325
 
326
+ # 結果展示區域
327
  with gr.Row():
328
  with gr.Column():
329
  gr.Markdown("### 📋 詳細識別結果")
330
+ detail_output = gr.Dataframe(
331
+ label="所有識別的文字內容",
332
+ interactive=True
 
333
  )
334
 
335
  with gr.Column():
336
  gr.Markdown("### 🔄 重建表格")
337
+ table_output = gr.Dataframe(
338
+ label="按表格結構重新組織的數據",
339
+ interactive=True
 
340
  )
341
 
342
+ # 使用說明
343
+ with gr.Accordion("📖 使用說明", open=False):
344
  gr.Markdown("""
345
+ ### 🚀 快速開始
346
+ 1. **上傳圖片**:點擊上傳包含文字的圖片
347
+ 2. **調整參數**:最低信心度(建議0.3-0.7)
348
+ 3. **開始識別**:點擊按鈕或直接上傳圖片自動處理
349
+ 4. **查看結果**:在兩個表格中查看識別結果
350
 
351
+ ### 📊 結果說明
352
+ - **詳細識別結果**:每個文字區塊完整信息
353
+ - **重建表格**:嘗試還原原始表格的行列
354
+ - **識別報告**:統計信息和品質評估
 
355
 
356
+ ### 💡 優化建議
357
+ - 使用清晰、高對比度的圖片
358
+ - 確保文字大小適中(不要太小)
359
+ - 避免圖片傾斜或變形
360
+ - 調整信心度閾值獲得最佳
361
  """)
362
 
363
  # 綁定事件
364
+ process_button.click(
365
+ fn=process_image_wrapper,
366
+ inputs=[image_input, confidence_slider],
367
+ outputs=[summary_output, detail_output, table_output]
368
  )
369
 
370
+ # 圖片上傳時自動處理
371
  image_input.change(
372
+ fn=process_image_wrapper,
373
+ inputs=[image_input, confidence_slider],
374
+ outputs=[summary_output, detail_output, table_output]
375
  )
376
 
377
+ return app
378
 
379
  if __name__ == "__main__":
380
+ app = main()
381
+ app.launch(
382
  server_name="0.0.0.0",
383
  server_port=7860,
384
+ share=True
 
385
  )