Spaces:
Sleeping
Sleeping
| import os | |
| from dotenv import load_dotenv | |
| from transformers import pipeline | |
| from PIL import Image | |
| import gradio as gr | |
| from openai import OpenAI | |
| import torch | |
| import scipy | |
| import numpy | |
| # πΉ νκ²½ λ³μ λ‘λ | |
| load_dotenv() | |
| client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) | |
| # πΉ κ°μ²΄ νμ§ λͺ¨λΈ λ‘λ© | |
| detector = pipeline( | |
| task="zero-shot-object-detection", | |
| model="google/owlv2-base-patch16-ensemble" | |
| ) | |
| # πΉ μ¬λ£ λΌλ²¨ λ° λ²μ | |
| candidate_labels = [ | |
| "lettuce","cheese","bread","tomato","ham" | |
| ] | |
| label_ko = { | |
| "cheese": "μΉμ¦", "bread":"λΉ΅", "tomato":"ν λ§ν ","ham":"ν","lettuce":"μμμΆ"} | |
| # πΉ μ μ λ³μ μ μ₯μ© | |
| last_ingredients = "" | |
| # πΉ HTML νμ μΆλ ₯ | |
| def format_result_html(result_text): | |
| html = "<div id='recipe-box' style='font-family: Pretendard, sans-serif; font-size: 17px; line-height: 1.9; background-color: #fefefe; padding: 20px; border-radius: 12px; border: 1px solid #ddd; box-shadow: 0 2px 8px rgba(0,0,0,0.05);'>" | |
| lines = result_text.strip().splitlines() | |
| for line in lines: | |
| if line.startswith("μ리 μ΄λ¦:"): | |
| content = line.replace("μ리 μ΄λ¦:", "").strip() | |
| html += f"<h3 style='color:#d84315;'>π½οΈ {content}</h3><br>" | |
| elif line.startswith("μ€λͺ :"): | |
| html += f"<p><strong>μ€λͺ :</strong> {line.replace('μ€λͺ :', '').strip()}</p>" | |
| elif line.startswith("μ¬λ£:"): | |
| html += f"<p><strong>μ¬λ£:</strong> {line.replace('μ¬λ£:', '').strip()}</p>" | |
| elif line.startswith("λ§λλ λ²:"): | |
| html += f"<p><strong>λ§λλ λ²:</strong> {line.replace('λ§λλ λ²:', '').strip()}</p>" | |
| elif line.startswith("쑰리 μκ°:"): | |
| html += f"<p><strong>Ⱡ쑰리 μκ°:</strong> {line.replace('쑰리 μκ°:', '').strip()}</p>" | |
| elif line.startswith("λμ΄λ:"): | |
| html += f"<p><strong>π― λμ΄λ:</strong> {line.replace('λμ΄λ:', '').strip()}</p>" | |
| else: | |
| html += f"<p>{line}</p>" | |
| html += "</div>" | |
| return html | |
| # πΉ μ리 μμ± ν¨μ | |
| def generate_recipe(ingredient_text): | |
| prompt = f""" | |
| λμ₯κ³ μ μλ μ¬λ£: {ingredient_text} | |
| μ΄ μ¬λ£λ€λ§ μ¬μ©ν΄μ λ§λ€ μ μλ μ리 **νλ**λ₯Ό μΆμ²ν΄μ€. | |
| - **κ³ κΈ°λ μμ μ²λΌ λΉμΈκ³ μ£Όμ¬λ£ κ°μ μ¬λ£λ μ λ λ£μ§ λ§** | |
| - μκΈ, νμΆ, κ°μ₯, μμ©μ , μ€ν λ±μ κΈ°λ³Έ μλ μ μ¬μ©ν΄λ λΌ | |
| - μκΈ, νμΆ, κ°μ₯, μμ©μ , μ€ν λ±μ κΈ°λ³Έ μλ μ κΌ μ¨μΌλλκ±°λ μλκ³ μ리μ νμνλ©΄ μ¬μ©νλ©΄ λΌ | |
| - κ°λ₯νλ©΄ μ§μ μμ λ²ν μ¬λ£λ‘ ꡬμ±ν΄μ€ | |
| - **μ¬λ£λ λ°λμ μ λ ₯λ μ¬λ£λ§ μ¬μ©νκ³ , μΌνλ‘ λμ΄λ ν μ€ νμμΌλ‘ μ¨μ€** | |
| - μ€λͺ μ μμλλ‘ λ²νΈ λΆμ¬μ μ¨μ€ | |
| - μΆλ ₯μ μλ νμμ κΌ μ§μΌμ€: | |
| μ리 μ΄λ¦: | |
| μ€λͺ : | |
| μ¬λ£: | |
| λ§λλ λ²: | |
| 쑰리 μκ°: | |
| λμ΄λ: | |
| """ | |
| response = client.chat.completions.create( | |
| model="gpt-4o", | |
| messages=[ | |
| {"role": "system", "content": "λΉμ μ μ리 μ λ¬Έκ°μ λλ€."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| temperature=0.9, | |
| max_tokens=600 | |
| ) | |
| return format_result_html(response.choices[0].message.content) | |
| # πΉ μ¬λ£ νμ§ ν μ리 μΆμ² | |
| def detect_and_recommend(image): | |
| global last_ingredients | |
| if image is None: | |
| return "<p style='color:red;'>μ΄λ―Έμ§λ₯Ό μ λ‘λν΄μ£ΌμΈμ.</p>" | |
| outputs = detector(image, candidate_labels=candidate_labels, threshold=0.2) | |
| detected_labels = list(set([o["label"] for o in outputs])) | |
| translated_labels = [label_ko.get(label, label) for label in detected_labels] | |
| if not translated_labels: | |
| return "<p style='color:red;'>μ¬λ£λ₯Ό μΈμνμ§ λͺ»νμ΅λλ€.</p>" | |
| last_ingredients = ", ".join(translated_labels) | |
| return generate_recipe(last_ingredients) | |
| # πΉ κ°μ μ¬λ£λ‘ λ€μ μΆμ² | |
| def reroll_recipe(): | |
| if not last_ingredients: | |
| return "<p style='color:red;'>λ¨Όμ μ΄λ―Έμ§λ₯Ό μ λ‘λν΄μ£ΌμΈμ.</p>" | |
| return generate_recipe(last_ingredients) | |
| # πΉ Gradio UI | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## π§ λμ₯κ³ μ΄λ―Έμ§ κΈ°λ° μ리 μΆμ² μμ€ν ") | |
| gr.Markdown("λμ₯κ³ λ΄λΆ μ¬μ§μ μ λ‘λν ν λ²νΌμ λλ¬ μΆμ² μ리λ₯Ό νμΈνμΈμ.") | |
| with gr.Row(): | |
| with gr.Column(scale=5): | |
| image_input = gr.Image(type="pil", label="λμ₯κ³ μ΄λ―Έμ§ μ λ‘λ", height=300) | |
| recommend_btn = gr.Button("λ μνΌ μΆμ²λ°κΈ°", size="lg") | |
| with gr.Column(scale=7): | |
| recipe_output = gr.HTML(label="π½οΈμΆμ² μ리 κ²°κ³Ό") | |
| # μ¨κ²¨μ§ Gradio λ²νΌ (μ€μ reroll νΈλ¦¬κ±°μ©) | |
| reroll_hidden_btn = gr.Button("", visible=False) | |
| # HTML λ²νΌλ€ (λ³΅μ¬ + 리둀) | |
| gr.HTML(""" | |
| <div style='display: flex; flex-direction: row; gap: 12px; margin-top: 12px;'> | |
| <button onclick=" | |
| const el = document.getElementById('recipe-box'); | |
| if (el) { | |
| const range = document.createRange(); | |
| range.selectNodeContents(el); | |
| const sel = window.getSelection(); | |
| sel.removeAllRanges(); | |
| sel.addRange(range); | |
| document.execCommand('copy'); | |
| alert('λ μνΌκ° 볡μ¬λμμ΅λλ€!'); | |
| } | |
| " style='padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 6px; font-size: 15px; cursor: pointer;'> | |
| π λ μνΌ λ³΅μ¬νκΈ° | |
| </button> | |
| <button onclick=" | |
| const buttons = document.getElementsByTagName('button'); | |
| for (let i = 0; i < buttons.length; i++) { | |
| if (buttons[i].innerText === '' && buttons[i].style.display === 'none') { | |
| buttons[i].click(); | |
| break; | |
| } | |
| } | |
| " style='padding: 10px 20px; background-color: #607D8B; color: white; border: none; border-radius: 6px; font-size: 15px; cursor: pointer;'> | |
| π² λ€λ₯Έ μ리 μΆμ²λ°κΈ° | |
| </button> | |
| </div> | |
| """) | |
| # μ΄λ²€νΈ μ°κ²° | |
| recommend_btn.click(fn=detect_and_recommend, inputs=image_input, outputs=recipe_output) | |
| reroll_hidden_btn.click(fn=reroll_recipe, inputs=None, outputs=recipe_output) | |
| if __name__ == "__main__": | |
| demo.launch() | |