jerrychen428 commited on
Commit
74e1f38
·
verified ·
1 Parent(s): be3d292

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +5 -5
  2. app.py +427 -0
  3. gitattributes +35 -0
  4. requirements.txt +8 -0
README.md CHANGED
@@ -1,13 +1,13 @@
1
  ---
2
- title: Linebot RobertClass
3
- emoji: 🐨
4
  colorFrom: red
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 5.44.1
8
  app_file: app.py
9
  pinned: false
10
- short_description: Linebot_robertClass
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Webcam Groq LineBot Application
3
+ emoji: 📉
4
  colorFrom: red
5
+ colorTo: yellow
6
  sdk: gradio
7
+ sdk_version: 5.43.1
8
  app_file: app.py
9
  pinned: false
10
+ license: other
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ from PIL import Image
5
+ from groq import Groq
6
+ import base64
7
+ import io
8
+ import csv
9
+ import pandas as pd
10
+ from docx import Document
11
+ import tempfile
12
+ import os
13
+ from datetime import datetime
14
+ from linebot import LineBotApi
15
+ from linebot.models import TextSendMessage
16
+
17
+ # LINE Bot 設定
18
+ LINE_CHANNEL_ACCESS_TOKEN = 'pt02snLmY1OFd0GhtGZD1DjGG4fB5a423PiDORgBIPkLvkNWjeUt5ZtN4rlwv6Bgne3zxCZrVJOK7Wa5th93YpkFhJmmX5cFX3wHjtdlKZQIfIwtjp16tEPN+2aSUWYEnmIrO9nG5L6H5veI80umZAdB04t89/1O/w1cDnyilFU='
19
+
20
+ def send_line_message(user_id, message, line_token=None):
21
+ """
22
+ Send a message to a specific LINE user
23
+
24
+ :param user_id: LINE user ID to send message to
25
+ :param message: Text message to send
26
+ :param line_token: Optional custom LINE token
27
+ """
28
+ try:
29
+ # 使用自訂 token 或預設 token
30
+ token = line_token if line_token else LINE_CHANNEL_ACCESS_TOKEN
31
+
32
+ if not token.strip():
33
+ return False, "LINE Token 不能為空"
34
+
35
+ if not user_id.strip():
36
+ return False, "User ID 不能為空"
37
+
38
+ # Initialize LineBotApi with the Channel Access Token
39
+ line_bot_api = LineBotApi(token)
40
+
41
+ # Create a TextSendMessage
42
+ text_message = TextSendMessage(text=message)
43
+
44
+ # Send the message
45
+ line_bot_api.push_message(user_id, text_message)
46
+
47
+ print(f"Message successfully sent to {user_id}")
48
+ return True, "訊息發送成功!"
49
+ except Exception as e:
50
+ error_msg = f"發送訊息時發生錯誤: {str(e)}"
51
+ print(error_msg)
52
+ return False, error_msg
53
+
54
+ def preview_image(image, flip_horizontal=False):
55
+ """預覽圖片,顯示翻轉效果"""
56
+ if image is None:
57
+ return None
58
+
59
+ # 直接顯示原圖,不進行任何翻轉
60
+ # 因為攝像頭拍照後的圖片已經是正確方向
61
+ if flip_horizontal:
62
+ # 只有用戶勾選時才翻轉
63
+ return image.transpose(Image.FLIP_LEFT_RIGHT)
64
+ else:
65
+ # 預設顯示原圖(正確方向)
66
+ return image
67
+
68
+ def encode_image(image):
69
+ """將 PIL Image 轉換為 base64 編碼"""
70
+ buffered = io.BytesIO()
71
+ image.save(buffered, format="JPEG")
72
+ return base64.b64encode(buffered.getvalue()).decode("utf-8")
73
+
74
+ def process_ocr_and_send_line(image, api_key, prompt, flip_horizontal,
75
+ send_to_line, line_token, line_user_id):
76
+ """使用 Groq API 進行 OCR 文字辨識並可選擇發送到 LINE"""
77
+ if image is None:
78
+ return "請先拍照或上傳圖片", None, None, "未執行"
79
+
80
+ if not api_key.strip():
81
+ return "請輸入有效的 Groq API Key", None, None, "未執行"
82
+
83
+ if not prompt.strip():
84
+ prompt = "請幫我辨識拍照的文字和內容進行OCR辨識"
85
+
86
+ try:
87
+ # 只有在用戶明確要求時才翻轉圖片
88
+ processed_image = image
89
+ if flip_horizontal:
90
+ processed_image = image.transpose(Image.FLIP_LEFT_RIGHT)
91
+ print("已手動翻轉圖片")
92
+ else:
93
+ print("使用原始圖片方向")
94
+
95
+ # 將圖片轉換為 base64
96
+ base64_image = encode_image(processed_image)
97
+ image_content = {
98
+ "type": "image_url",
99
+ "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
100
+ }
101
+
102
+ # 初始化 Groq 客戶端
103
+ client = Groq(api_key=api_key)
104
+
105
+ # 發送 API 請求
106
+ completion = client.chat.completions.create(
107
+ model="meta-llama/llama-4-scout-17b-16e-instruct",
108
+ messages=[{
109
+ "role": "user",
110
+ "content": [
111
+ {"type": "text", "text": prompt},
112
+ image_content
113
+ ]
114
+ }],
115
+ temperature=1,
116
+ max_completion_tokens=512,
117
+ top_p=1,
118
+ stream=False,
119
+ stop=None,
120
+ )
121
+
122
+ # 獲取辨識結果
123
+ content = completion.choices[0].message.content
124
+
125
+ # 生成輸出檔案
126
+ csv_file = create_csv_output(content)
127
+ docx_file = create_docx_output(content)
128
+
129
+ # 準備 LINE 發送狀態
130
+ line_status = "未執行"
131
+
132
+ # 如果用戶選擇發送到 LINE
133
+ if send_to_line:
134
+ if not line_user_id.strip():
135
+ line_status = "錯誤:請輸入 LINE User ID"
136
+ else:
137
+ # 準備要發送的訊息
138
+ timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
139
+ line_message = f"""📸 OCR 辨識結果
140
+
141
+ 🕒 辨識時間: {timestamp}
142
+
143
+ 📝 辨識內容:
144
+ {content}
145
+
146
+ ---
147
+ 由 OCR 文字辨識系統自動發送"""
148
+
149
+ # 發送到 LINE
150
+ success, message = send_line_message(line_user_id, line_message, line_token)
151
+ if success:
152
+ line_status = f"✅ {message}"
153
+ else:
154
+ line_status = f"❌ {message}"
155
+
156
+ return content, csv_file, docx_file, line_status
157
+
158
+ except Exception as e:
159
+ error_msg = f"OCR 辨識發生錯誤: {str(e)}"
160
+ return error_msg, None, None, "未執行"
161
+
162
+ def create_csv_output(content):
163
+ """創建 CSV 輸出檔案"""
164
+ try:
165
+ # 創建臨時檔案
166
+ temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv', encoding='utf-8-sig')
167
+
168
+ # 將辨識結果寫入 CSV
169
+ writer = csv.writer(temp_file)
170
+ writer.writerow(['時間戳記', 'OCR辨識結果'])
171
+ writer.writerow([datetime.now().strftime('%Y-%m-%d %H:%M:%S'), content])
172
+
173
+ # 如果內容包含多行,每行作為一個記錄
174
+ lines = content.split('\n')
175
+ if len(lines) > 1:
176
+ writer.writerow([]) # 空行分隔
177
+ writer.writerow(['行號', '內容'])
178
+ for i, line in enumerate(lines, 1):
179
+ if line.strip():
180
+ writer.writerow([i, line.strip()])
181
+
182
+ temp_file.close()
183
+ return temp_file.name
184
+
185
+ except Exception as e:
186
+ print(f"創建 CSV 檔案時發生錯誤: {e}")
187
+ return None
188
+
189
+ def create_docx_output(content):
190
+ """創建 DOCX 輸出檔案"""
191
+ try:
192
+ # 創建臨時檔案
193
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.docx')
194
+ temp_file.close()
195
+
196
+ # 創建 Word 文件
197
+ doc = Document()
198
+ doc.add_heading('OCR 辨識結果', 0)
199
+
200
+ # 添加時間戳記
201
+ doc.add_heading('辨識時間', level=1)
202
+ doc.add_paragraph(datetime.now().strftime('%Y年%m月%d日 %H:%M:%S'))
203
+
204
+ # 添加辨識內容
205
+ doc.add_heading('辨識內容', level=1)
206
+ doc.add_paragraph(content)
207
+
208
+ # 如果內容包含多行,分段顯示
209
+ lines = content.split('\n')
210
+ if len(lines) > 3:
211
+ doc.add_heading('分行內容', level=1)
212
+ for i, line in enumerate(lines, 1):
213
+ if line.strip():
214
+ doc.add_paragraph(f"{i}. {line.strip()}")
215
+
216
+ doc.save(temp_file.name)
217
+ return temp_file.name
218
+
219
+ except Exception as e:
220
+ print(f"創建 DOCX 檔案時發生錯誤: {e}")
221
+ return None
222
+
223
+ def clear_inputs():
224
+ """清除輸入內容"""
225
+ return None, "", "", "", "", False, "未執行"
226
+
227
+ def test_line_message(line_token, line_user_id):
228
+ """測試 LINE 訊息發送"""
229
+ if not line_user_id.strip():
230
+ return "❌ 請輸入 LINE User ID"
231
+
232
+ test_message = f"""🔔 測試訊息
233
+
234
+ 這是來自 OCR 文字辨識系統的測試訊息。
235
+
236
+ 🕒 測試時間: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
237
+
238
+ 如果您收到此訊息,表示 LINE Bot 設定成功!"""
239
+
240
+ success, message = send_line_message(line_user_id, test_message, line_token)
241
+ if success:
242
+ return f"✅ {message}"
243
+ else:
244
+ return f"❌ {message}"
245
+
246
+ # 創建 Gradio 介面
247
+ with gr.Blocks(title="OCR + LINE Bot 整合系統", theme=gr.themes.Soft()) as demo:
248
+ gr.Markdown("# 📸📱 OCR + LINE Bot 整合系統")
249
+ gr.Markdown("使用攝像頭拍照或上傳圖片,透過 AI 進行文字辨識,並可選擇將結果發送到 LINE")
250
+
251
+ with gr.Row():
252
+ with gr.Column(scale=1):
253
+ # 輸入區域
254
+ gr.Markdown("### 📝 輸入設定")
255
+
256
+ # 圖片輸入
257
+ image_input = gr.Image(
258
+ sources=['webcam', 'upload'],
259
+ type='pil',
260
+ label="拍照或上傳圖片"
261
+ )
262
+
263
+ # API Key 輸入
264
+ api_key_input = gr.Textbox(
265
+ label="Groq API Key",
266
+ placeholder="請輸入您的 Groq API Key",
267
+ type="password",
268
+ value=""
269
+ )
270
+
271
+ # 提示詞輸入
272
+ prompt_input = gr.Textbox(
273
+ label="辨識提示詞",
274
+ placeholder="輸入您想要的辨識提示...",
275
+ value="請幫我辨識拍照的文字和內容進行OCR辨識,請盡可能詳細和準確地提取所有可見的文字內容。",
276
+ lines=3
277
+ )
278
+
279
+ # 鏡像修正選項
280
+ flip_checkbox = gr.Checkbox(
281
+ label="🔄 手動翻轉圖片(如果文字方向不對才勾選)",
282
+ value=False,
283
+ info="通常攝像頭拍照後圖片方向是正確的,只在文字顛倒時才勾選此選項"
284
+ )
285
+
286
+ # 圖片預覽
287
+ preview_image_output = gr.Image(
288
+ label="圖片預覽(將要辨識的圖片)",
289
+ type='pil',
290
+ interactive=False
291
+ )
292
+
293
+ # LINE Bot 設定
294
+ gr.Markdown("### 📱 LINE Bot 設定")
295
+
296
+ send_to_line_checkbox = gr.Checkbox(
297
+ label="📤 將辨識結果發送到 LINE",
298
+ value=False,
299
+ info="勾選後會將 OCR 結果自動發送到指定的 LINE 用戶"
300
+ )
301
+
302
+ line_token_input = gr.Textbox(
303
+ label="LINE Channel Access Token(選填)",
304
+ placeholder="留空則使用預設 Token",
305
+ type="password",
306
+ value=""
307
+ )
308
+
309
+ line_user_id_input = gr.Textbox(
310
+ label="LINE User ID",
311
+ placeholder="請輸入要發送訊息的 LINE User ID",
312
+ value="U377f923cd08097b0a01116f8e942650b"
313
+ )
314
+
315
+ # 操作按鈕
316
+ with gr.Row():
317
+ process_btn = gr.Button("🔍 開始辨識", variant="primary")
318
+ test_line_btn = gr.Button("📱 測試 LINE", variant="secondary")
319
+ clear_btn = gr.Button("🗑️ 清除", variant="secondary")
320
+
321
+ with gr.Column(scale=1):
322
+ # 輸出區域
323
+ gr.Markdown("### 📊 辨識結果")
324
+
325
+ # 文字結果輸出
326
+ text_output = gr.Textbox(
327
+ label="辨識結果",
328
+ placeholder="辨識結果將顯示在這裡...",
329
+ lines=8,
330
+ max_lines=12
331
+ )
332
+
333
+ # LINE 發送狀態
334
+ line_status_output = gr.Textbox(
335
+ label="LINE 發送狀態",
336
+ placeholder="LINE 發送狀態將顯示在這裡...",
337
+ lines=2,
338
+ interactive=False
339
+ )
340
+
341
+ # 檔案下載
342
+ gr.Markdown("### 📁 下載檔案")
343
+ with gr.Row():
344
+ csv_download = gr.File(
345
+ label="下載 CSV 檔案",
346
+ visible=True
347
+ )
348
+ docx_download = gr.File(
349
+ label="下載 DOCX 檔案",
350
+ visible=True
351
+ )
352
+
353
+ # 使用說明
354
+ with gr.Accordion("📖 使用說明", open=False):
355
+ gr.Markdown("""
356
+ ### 使用步驟:
357
+ 1. **獲取 Groq API Key**:前往 [Groq官網](https://groq.com) 註冊並取得 API Key
358
+ 2. **設定 LINE Bot**(選用):
359
+ - 輸入您的 LINE User ID
360
+ - 可選擇輸入自訂的 LINE Channel Access Token
361
+ - 點擊「測試 LINE」確認設定正確
362
+ 3. **拍照或上傳**:使用攝像頭拍照或上傳包含文字的圖片
363
+ 4. **檢查方向**:查看圖片預覽中的文字方向是否正確
364
+ 5. **必要時翻轉**:只有當文字看起來是顛倒的時候才勾選「手動翻轉圖片」
365
+ 6. **輸入 API Key**:在上方欄位輸入您的 Groq API Key
366
+ 7. **選擇發送方式**:勾選「將辨識結果發送到 LINE」如果您想要自動發送結果
367
+ 8. **開始辨識**:點擊「開始辨識」按鈕
368
+ 9. **下載結果**:辨識完成後可下載 CSV 和 DOCX 格式的結果檔案
369
+
370
+ ### 支援功能:
371
+ - 📷 即時攝像頭拍照
372
+ - 🔄 攝像頭鏡像修正(解決左右相反問題)
373
+ - 📤 圖片檔案上傳
374
+ - 🤖 AI 文字辨識
375
+ - 📱 LINE Bot 訊息發送
376
+ - 📄 CSV 格式輸出
377
+ - 📝 Word 文檔輸出
378
+ - 🔧 自訂提示詞
379
+
380
+ ### LINE User ID 取得方式:
381
+ 1. 在 LINE 中加入您的 LINE Bot 為好友
382
+ 2. 傳送任意訊息給 Bot
383
+ 3. 在 Bot 後台查看 User ID
384
+ 4. 或使用 LINE 開發者工具取得
385
+ """)
386
+
387
+ # 事件綁定
388
+ process_btn.click(
389
+ fn=process_ocr_and_send_line,
390
+ inputs=[image_input, api_key_input, prompt_input, flip_checkbox,
391
+ send_to_line_checkbox, line_token_input, line_user_id_input],
392
+ outputs=[text_output, csv_download, docx_download, line_status_output]
393
+ )
394
+
395
+ test_line_btn.click(
396
+ fn=test_line_message,
397
+ inputs=[line_token_input, line_user_id_input],
398
+ outputs=line_status_output
399
+ )
400
+
401
+ clear_btn.click(
402
+ fn=clear_inputs,
403
+ outputs=[image_input, text_output, api_key_input, prompt_input,
404
+ line_token_input, send_to_line_checkbox, line_status_output]
405
+ )
406
+
407
+ # 圖片預覽更新事件
408
+ image_input.change(
409
+ fn=preview_image,
410
+ inputs=[image_input, flip_checkbox],
411
+ outputs=preview_image_output
412
+ )
413
+
414
+ flip_checkbox.change(
415
+ fn=preview_image,
416
+ inputs=[image_input, flip_checkbox],
417
+ outputs=preview_image_output
418
+ )
419
+
420
+ # 啟動應用程式
421
+ if __name__ == "__main__":
422
+ demo.launch(
423
+ share=True,
424
+ server_name="0.0.0.0",
425
+ server_port=None, # 讓 Gradio 自動尋找可用端口
426
+ show_error=True
427
+ )
gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ opencv-python>=4.8.0
3
+ pillow>=10.0.0
4
+ groq>=0.4.0
5
+ pandas>=2.0.0
6
+ python-docx>=1.1.0
7
+ line-bot-sdk>=3.5.0
8
+ numpy>=1.24.0