youngtsai commited on
Commit
6914802
·
1 Parent(s): 5710160

with gr.Tab("計算紙分析"):

Browse files
Files changed (1) hide show
  1. app.py +99 -12
app.py CHANGED
@@ -11,6 +11,8 @@ from storage_service import GoogleCloudStorage
11
  import csv
12
  import io
13
  import fitz # PyMuPDF
 
 
14
 
15
 
16
 
@@ -46,18 +48,22 @@ sheets_client = gspread.service_account_from_dict(GSHEET_KEY_DICT)
46
  CSV_DATA = []
47
 
48
  # 函数定义
49
- def upload_image_to_gcs(image_path, bucket):
50
- # Extract the file name from the path and create a unique filename
51
- original_filename = os.path.basename(image_path)
52
- unique_filename = f"{int(time.time())}_{original_filename}"
53
  blob = bucket.blob(unique_filename)
54
 
55
- # Open the file and upload its contents to GCS
56
- with open(image_path, "rb") as image_file:
57
- blob.upload_from_file(image_file)
 
 
 
 
 
58
  blob.make_public()
59
  print("======upload_image_to_gcs=====")
60
- print(f"File {original_filename} uploaded to {unique_filename} in GCS.")
61
  return blob.public_url
62
 
63
  def process_image(image_url):
@@ -226,7 +232,7 @@ def create_csv(processed_data):
226
  # 设定一个可写的目录路径
227
  writable_directory = "/tmp/csv_files"
228
  if not os.path.exists(writable_directory):
229
- os.makedirs(writable_directory) # 如果目录不存在,创建它
230
 
231
  timestamp = int(time.time())
232
  file_name = f"csv_{timestamp}.csv"
@@ -285,7 +291,7 @@ def process_image_to_data(password, images):
285
  print("image_url:", image_url)
286
 
287
  question_count = len(processed_data)
288
- result = f"圖片處理完成,總共完成 {question_count} 道題目"
289
  csv_file_path = create_csv(processed_data)
290
 
291
  return processed_data, result, csv_file_path
@@ -521,11 +527,85 @@ def process_qid_to_data(password, q_id):
521
 
522
  return processed_data, result, csv_file_path
523
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  # Gradio界面
525
  with gr.Blocks() as demo:
526
  with gr.Row():
527
  password_input = gr.Textbox(label="密碼", type="password", elem_id="password_input")
528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
  with gr.Tab("Junyi_Q_ID", elem_id="junyi_q_id_tab"):
530
  with gr.Row():
531
  gr.Markdown("## Junyi Q_ID")
@@ -599,12 +679,13 @@ with gr.Blocks() as demo:
599
  with gr.Accordion(open=False):
600
  with gr.Row():
601
  pdf_result_table = gr.Dataframe(
602
- headers=["圖片URL", "文字", "題號", "題目", "選項1", "選項2", "選項3", "選項4", "答案", "提示1", "提示2", "提示3", "提示4", "提示5", "Perseus JSON"],
603
  column_widths=[10, 10, 5, 20, 4, 4, 4, 4, 4,4,4,4,4,4, 10],
604
  wrap=True
605
  )
606
 
607
 
 
608
  submit_button.click(
609
  fn=process_image_to_data,
610
  inputs=[password_input, image_input],
@@ -653,6 +734,12 @@ with gr.Blocks() as demo:
653
  outputs=[junyi_q_id_question_markdown]
654
  )
655
 
656
- demo.launch()
 
 
 
 
 
 
657
 
658
 
 
11
  import csv
12
  import io
13
  import fitz # PyMuPDF
14
+ import base64
15
+ from PIL import Image
16
 
17
 
18
 
 
48
  CSV_DATA = []
49
 
50
  # 函数定义
51
+ def upload_image_to_gcs(image_data, bucket):
52
+ # Generate a unique filename
53
+ unique_filename = f"{int(time.time())}_image.jpg"
 
54
  blob = bucket.blob(unique_filename)
55
 
56
+ # If image_data is a BytesIO object, upload directly
57
+ if isinstance(image_data, io.BytesIO):
58
+ blob.upload_from_file(image_data, content_type='image/jpeg')
59
+ else:
60
+ # If it's a file path, open and upload
61
+ with open(image_data, "rb") as image_file:
62
+ blob.upload_from_file(image_file)
63
+
64
  blob.make_public()
65
  print("======upload_image_to_gcs=====")
66
+ print(f"File uploaded to {unique_filename} in GCS.")
67
  return blob.public_url
68
 
69
  def process_image(image_url):
 
232
  # 设定一个可写的目录路径
233
  writable_directory = "/tmp/csv_files"
234
  if not os.path.exists(writable_directory):
235
+ os.makedirs(writable_directory) # 如果目录不存在,创它
236
 
237
  timestamp = int(time.time())
238
  file_name = f"csv_{timestamp}.csv"
 
291
  print("image_url:", image_url)
292
 
293
  question_count = len(processed_data)
294
+ result = f"圖片處理完成總共完成 {question_count} 道題目"
295
  csv_file_path = create_csv(processed_data)
296
 
297
  return processed_data, result, csv_file_path
 
527
 
528
  return processed_data, result, csv_file_path
529
 
530
+ # 新增函數來處理計算紙截圖
531
+ def process_calculation_image(image_input, base64_input):
532
+ if base64_input:
533
+ # 處理 base64 編碼的圖片
534
+ image_data = base64.b64decode(base64_input.split(',')[1])
535
+ image = Image.open(io.BytesIO(image_data))
536
+ elif image_input:
537
+ if isinstance(image_input, str):
538
+ # 處理圖片文件路徑
539
+ image = Image.open(image_input)
540
+ else:
541
+ # 處理上傳的圖片文件
542
+ image = Image.open(image_input.name)
543
+ else:
544
+ return None, "請上傳圖片或輸入 base64 圖片字串"
545
+
546
+ # 將圖片轉換為 base64 字串
547
+ buffered = io.BytesIO()
548
+ image.save(buffered, format="PNG")
549
+ img_str = base64.b64encode(buffered.getvalue()).decode()
550
+
551
+ # 直接使用 OpenAI API 分析圖片
552
+ analysis = analyze_calculation_image(img_str)
553
+
554
+ return image, analysis
555
+
556
+ def analyze_calculation_image(image_base64):
557
+ user_prompt = """
558
+ 請分析這張學生計算紙的截圖:
559
+ 1. 如果有計算式,請解釋學生的解題步驟,並指出可能的錯誤或改進點。使用 LaTeX 格式表示數學公式。
560
+ 2. 如果沒有計算式,請提供這道題目的標準教學步驟,但不要直接給出答案。同樣使用 LaTeX 格式表示數學公式。
561
+ 3. 無論哪種情況,都請給出鼓勵性的回饋。
562
+ 4. 請使用 Markdown 格式輸出,數學公式請用 $...$ 包裹。
563
+ 5. 請使用繁體中文輸出 ZH-TW
564
+ """
565
+
566
+ response = OPEN_AI_CLIENT.chat.completions.create(
567
+ model="gpt-4o",
568
+ messages=[
569
+ {
570
+ "role": "user",
571
+ "content": [
572
+ {
573
+ "type": "text",
574
+ "text": user_prompt
575
+ },
576
+ {
577
+ "type": "image_url",
578
+ "image_url": {
579
+ "url": f"data:image/png;base64,{image_base64}",
580
+ },
581
+ },
582
+ ],
583
+ }
584
+ ],
585
+ max_tokens=1000,
586
+ )
587
+ return response.choices[0].message.content
588
+
589
  # Gradio界面
590
  with gr.Blocks() as demo:
591
  with gr.Row():
592
  password_input = gr.Textbox(label="密碼", type="password", elem_id="password_input")
593
 
594
+ with gr.Tab("計算紙分析"):
595
+ with gr.Row():
596
+ gr.Markdown("## 學生計算紙分析")
597
+ with gr.Accordion(open=False):
598
+ with gr.Row():
599
+ calculation_image_input = gr.Image(label="上傳計算紙截圖", type="filepath")
600
+ with gr.Row():
601
+ calculation_base64_input = gr.Textbox(label="或輸入 base64 圖片字串", lines=3, elem_id="calculation_base64_input")
602
+ with gr.Row():
603
+ calculation_submit_button = gr.Button("分析計算紙", elem_id="calculation_submit_button")
604
+ with gr.Row():
605
+ calculation_image_display = gr.Image(label="上傳的圖片")
606
+ with gr.Row():
607
+ calculation_result = gr.Markdown(label="分析結果", latex_delimiters=[{"left": "$", "right": "$", "display": False}])
608
+
609
  with gr.Tab("Junyi_Q_ID", elem_id="junyi_q_id_tab"):
610
  with gr.Row():
611
  gr.Markdown("## Junyi Q_ID")
 
679
  with gr.Accordion(open=False):
680
  with gr.Row():
681
  pdf_result_table = gr.Dataframe(
682
+ headers=["圖片URL", "文字", "題號", "題目", "選項1", "選���2", "選項3", "選項4", "答案", "提示1", "提示2", "提示3", "提示4", "提示5", "Perseus JSON"],
683
  column_widths=[10, 10, 5, 20, 4, 4, 4, 4, 4,4,4,4,4,4, 10],
684
  wrap=True
685
  )
686
 
687
 
688
+
689
  submit_button.click(
690
  fn=process_image_to_data,
691
  inputs=[password_input, image_input],
 
734
  outputs=[junyi_q_id_question_markdown]
735
  )
736
 
737
+ calculation_submit_button.click(
738
+ fn=process_calculation_image,
739
+ inputs=[calculation_image_input, calculation_base64_input],
740
+ outputs=[calculation_image_display, calculation_result]
741
+ )
742
+
743
+ demo.launch(share=True)
744
 
745