Spaces:
Sleeping
Sleeping
File size: 5,873 Bytes
d12d2be 4479043 d12d2be 7fb15d2 eb0ade0 7fb15d2 eb0ade0 f8d1ebe eb0ade0 e128900 eb0ade0 e128900 eb0ade0 19e66f7 eb0ade0 f8d1ebe eb0ade0 7fb15d2 eb0ade0 d12d2be 6c93c6b e128900 8fb9fc0 0f3ad7e d12d2be b1d1716 d12d2be 3687356 d12d2be 3687356 b1d1716 d12d2be b1d1716 d12d2be 3687356 b1d1716 d12d2be b1d1716 d12d2be b1d1716 d12d2be 3687356 d12d2be 3687356 d12d2be b2b5513 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | import os, io, base64, json, tempfile, pathlib
import pandas as pd
import gradio as gr
from openai import OpenAI
# ---------- ❶ 基本設定 ----------
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
raise RuntimeError("Please set the OPENAI_API_KEY environment variable.")
client = OpenAI(api_key=OPENAI_API_KEY)
MODEL = "o3"
MAX_TOKENS = 1024
# ---------- ❷ JSON Schema ----------
product_schema = {
"name": "RetailPriceTagSchema",
"description": "Products extracted from retail price-tag photos.",
"strict": True,
"schema": {
"type": "object",
"properties": {
"products": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "完整商品名稱;若無或無法辨識請輸出『無法辨識』"
},
"list_price": {
"type": "string",
"description": "原價 (非促銷);若有標促銷價但沒標原價或無法辨識請輸出『無法辨識』"
},
"promo_price": {
"type": "string",
"description": "促銷/特價;若無或無法辨識請輸出『無法辨識』"
},
"weight": {
"type": "string",
"description": "總重量 (g / kg);若無或無法辨識請輸出『無法辨識』"
},
"volume": {
"type": "string",
"description": "總量(件數/入數/顆,或類似數量單位);若無或無法辨識請輸出『無法辨識』"
},
"barcode": {
"type": "string",
"description": "條碼號 EAN/UPC;;若無或無法辨識請輸出『無法辨識』"
},
"item_code": {
"type": "string",
"description": "通路自用貨號(多為英數混排,類似F0500)/PLU;若無或無法辨識請輸出『無法辨識』"
}
},
"required": [
"name", "list_price", "promo_price",
"weight", "volume", "barcode", "item_code"
],
"additionalProperties": False
}
}
},
"required": ["products"],
"additionalProperties": False
}
}
system_prompt = (
"""你是一個零售標價解析助手,請嚴格根據圖片分析商品標示資訊,商品名稱顯示標示上的原文(若為中文則務必顯示中文):
規則:
# 判斷規則 (務必遵守)
1. 價牌上如果以『促銷價』『special price』『sale』等字樣作為抬頭,或者整張牌為紅/黃底高亮,則此價格一律填入 'promo_price','list_price' 改寫『無法辨識』。
2. 出現『×2』『x3』『3包』等倍數或件數,寫入 'volume';重量只留單包重量 (例如「50g ±3」→ weight=50g,volume=3包)。
3. 僅當價牌明示「原價/建議售價/定價/刪除線價格」才填 list_price,否則填『無法辨識』…
4. 若 weight 含 (10顆),把括號內容移到 volume,weight 只留 g/kg…
"""
)
# ---------- ❸ 小工具 ----------
def encode_image_to_data_url(img_path: str) -> str:
mime = "image/" + pathlib.Path(img_path).suffix.lstrip(".").lower()
with open(img_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
return f"data:{mime};base64,{b64}"
def call_gpt_model(model_name, image_path):
messages = [
{"role": "system", "content": system_prompt},
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": encode_image_to_data_url(image_path)}}
]
}
]
params = {
"model": model_name,
"messages": messages,
"response_format": {
"type": "json_schema",
"json_schema": product_schema
}
}
if model_name == "gpt-4o":
params["temperature"] = 0.0
resp = client.chat.completions.create(**params)
return json.loads(resp.choices[0].message.content)
def process(images, model_name):
all_items = []
for img in images:
payload = call_gpt_model(model_name, img.name)
items = payload.get("products", [])
all_items.extend(items)
json_str = json.dumps(all_items, ensure_ascii=False, indent=2)
df = pd.DataFrame(all_items)
bio = io.BytesIO()
df.to_excel(bio, index=False, engine="openpyxl")
bio.seek(0)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
tmp.write(bio.read())
tmp.flush()
return json_str, tmp.name
# ---------- ❹ Gradio 介面 ----------
with gr.Blocks(title="Price-Tag Parser") as demo:
gr.Markdown("## 🏷️ 零售標價解析\n上傳一張或多張標價照片 → 取得 JSON 與 Excel")
inp = gr.Files(label="上傳圖片 (可多選)", file_types=["image"])
model_selector = gr.Radio(
choices=["gpt-4o", "o3"],
value="gpt-4o",
label="選擇使用的模型"
)
btn = gr.Button("開始解析 🪄")
out_json = gr.JSON(label="辨識結果 (JSON)")
out_file = gr.File(label="下載 Excel", file_types=[".xlsx"])
btn.click(process, inputs=[inp, model_selector], outputs=[out_json, out_file])
if __name__ == "__main__":
demo.launch() |