Zaious commited on
Commit
b2b5513
·
verified ·
1 Parent(s): 95039fe

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -0
app.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, io, base64, json, tempfile, pathlib
2
+ import pandas as pd
3
+ import gradio as gr
4
+ from openai import OpenAI
5
+
6
+ # --- Configuration ----------------------------------------------------------
7
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
8
+ if not OPENAI_API_KEY:
9
+ raise RuntimeError("Please set the OPENAI_API_KEY environment variable.")
10
+ client = OpenAI(api_key=OPENAI_API_KEY)
11
+
12
+ MODEL = "gpt-4o-mini" # works fine; change to "gpt-4o" if you have access
13
+ MAX_TOKENS = 1024 # plenty for JSON output
14
+
15
+ SYSTEM_PROMPT = """
16
+ 你是一個零售標價解析助手。請依照以下 JSON Schema 輸出結果:
17
+ [
18
+ {
19
+ "品名": string,
20
+ "原價": string | null,
21
+ "促銷價": string | null,
22
+ "總重": string | null,
23
+ "總量": string | null,
24
+ "條碼號": string | null,
25
+ "物品編號": string | null
26
+ },
27
+ ...
28
+ ]
29
+ ⚠️ 只回傳 JSON,不要講述。
30
+ 若圖片中有多個商品,請依序列出。
31
+ """
32
+
33
+ # --- Core logic -------------------------------------------------------------
34
+ def encode_image_to_data_url(img_path: str) -> str:
35
+ mime = "image/" + pathlib.Path(img_path).suffix.lstrip(".").lower()
36
+ with open(img_path, "rb") as f:
37
+ b64 = base64.b64encode(f.read()).decode()
38
+ return f"data:{mime};base64,{b64}"
39
+
40
+ def call_gpt4o(image_paths):
41
+ messages = [
42
+ {"role": "system", "content": SYSTEM_PROMPT}
43
+ ]
44
+ for p in image_paths:
45
+ messages.append(
46
+ {
47
+ "role": "user",
48
+ "content": [
49
+ {
50
+ "type": "image_url",
51
+ "image_url": {"url": encode_image_to_data_url(p)}
52
+ }
53
+ ],
54
+ }
55
+ )
56
+ resp = client.chat.completions.create(
57
+ model=MODEL,
58
+ messages=messages,
59
+ max_tokens=MAX_TOKENS,
60
+ temperature=0.0,
61
+ )
62
+ # Expect pure JSON
63
+ text = resp.choices[0].message.content.strip()
64
+ try:
65
+ return json.loads(text)
66
+ except Exception as e:
67
+ raise ValueError(f"模型輸出不是有效 JSON:\n{text}\n{e}")
68
+
69
+ def process(images):
70
+ """
71
+ images: list[TemporaryUploadedFile]
72
+ Returns (json_str, xlsx_file_path)
73
+ """
74
+ paths = [img.name for img in images] # TemporaryUploadedFile has .name path
75
+ all_items = call_gpt4o(paths)
76
+
77
+ # 1) JSON pretty-print
78
+ json_str = json.dumps(all_items, ensure_ascii=False, indent=2)
79
+
80
+ # 2) Excel
81
+ df = pd.DataFrame(all_items)
82
+ bio = io.BytesIO()
83
+ df.to_excel(bio, index=False, engine="openpyxl")
84
+ bio.seek(0)
85
+ # Save to a real temp file so Gradio can return it
86
+ tmp_xlsx = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
87
+ tmp_xlsx.write(bio.read())
88
+ tmp_xlsx.flush()
89
+ return json_str, tmp_xlsx.name
90
+
91
+ # --- Gradio UI --------------------------------------------------------------
92
+ with gr.Blocks(title="Price-Tag Parser") as demo:
93
+ gr.Markdown("## 🏷️ 零售標價解析\n上傳一張或多張標價照片 → 取得 JSON 與 Excel")
94
+ with gr.Row():
95
+ inp = gr.Files(label="上傳圖片 (可多選)", file_types=["image"])
96
+ btn = gr.Button("開始解析 🪄")
97
+ out_json = gr.JSON(label="辨識結果 (JSON)")
98
+ out_file = gr.File(label="下載 Excel", file_types=[".xlsx"])
99
+
100
+ btn.click(process, inputs=inp, outputs=[out_json, out_file])
101
+
102
+ if __name__ == "__main__":
103
+ demo.launch()