File size: 5,934 Bytes
8d67bdf cacf836 65ddbdd 72254f9 cacf836 72254f9 52468e0 2e84422 f2f5f5c 2e84422 72254f9 e231d9b f2f5f5c 72254f9 6599a57 8bf75c7 72254f9 8bf75c7 8d67bdf 72254f9 cacf836 72254f9 e231d9b 72254f9 fbe9d58 72254f9 9943408 72254f9 65ddbdd cf46432 72254f9 cf46432 8d67bdf 72254f9 52468e0 72254f9 52468e0 72254f9 52468e0 72254f9 52468e0 72254f9 52468e0 72254f9 938215a 72254f9 938215a 72254f9 938215a 72254f9 938215a 72254f9 c28e6e8 72254f9 8d67bdf | 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 | # =====================================================================
# 🛸 HUGGING FACE ZERO-GPU INITIALIZATION (MUST BE FIRST)
# =====================================================================
import sys
# This forces spaces to load right away if it's installed in the HF container
try:
import spaces
except ImportError:
pass
import torch
import pandas as pd
import gradio as gr
import cv2
import numpy as np
from PIL import Image
from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor, BitsAndBytesConfig
from peft import PeftModel
from qwen_vl_utils import process_vision_info
# =====================================================================
# ⚡ ENGINE INITIALIZATION
# =====================================================================
device = "cuda" if torch.cuda.is_available() else "cpu"
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
base_model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
"Qwen/Qwen2.5-VL-7B-Instruct",
quantization_config=quantization_config,
device_map="auto",
torch_dtype=torch.float16
)
model = PeftModel.from_pretrained(base_model, "uttarasawant/qwen2.5-vl-fridge-adapters")
processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct")
def sample_frames_from_video(video_path, num_frames=4):
cap = cv2.VideoCapture(video_path)
frames = []
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# Handle empty videos
if total_frames <= 0: return []
indices = np.linspace(0, total_frames - 1, num_frames, dtype=int)
for i in range(total_frames):
ret, frame = cap.read()
if not ret: break
if i in indices:
frames.append(Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)))
cap.release()
return frames
# =====================================================================
# 🧠 CHEF LOGIC ENGINE
# =====================================================================
@spaces.GPU(duration=120)
def process_kitchen_operations(media_input, budget, days):
# GUARD: Stop if no input provided
if media_input is None:
return None, None, pd.DataFrame(columns=["Ingredient Asset", "Qty", "Status", "Value"]), "### ⚠️ System Idle\nPlease upload an image or video."
if isinstance(media_input, str):
images = sample_frames_from_video(media_input)
if not images: return None, None, pd.DataFrame(), "### ❌ Error\nCould not extract frames from video."
else:
images = [media_input]
chef_prompt = f"Act as a professional chef. Identify ingredients. Create a {days}-day meal plan (budget ${budget}). Output as Markdown Table (Day|Breakfast|Lunch|Dinner). Provide inventory list first."
content = [{"type": "image", "image": img} for img in images]
content.append({"type": "text", "text": chef_prompt})
messages = [{"role": "system", "content": "You are a professional chef. Only use visible ingredients."}, {"role": "user", "content": content}]
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
image_inputs, _ = process_vision_info(messages)
inputs = processor(text=[text], images=image_inputs, padding=True, return_tensors="pt").to(device)
generated_ids = model.generate(**inputs, max_new_tokens=400)
generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
generated_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True)[0]
food_keywords = ['salmon', 'chicken', 'broccoli', 'lettuce', 'tomato', 'pepper', 'mushroom']
found_items = [f for f in food_keywords if f in generated_text.lower()]
df_rows = [[item.title(), "1 Unit", "Fresh", f"${2.50 + (idx*0.5):.2f}"] for idx, item in enumerate(found_items)]
df = pd.DataFrame(df_rows or [["None", "-", "-", "$0"]], columns=["Ingredient Asset", "Qty", "Status", "Value"])
return images[0], images[0], df, f"### 👨🍳 Chef's Culinary Blueprint\n{generated_text}"
# =====================================================================
# 🎨 GRADIO INTERFACE
# =====================================================================
with gr.Blocks() as demo:
gr.Markdown("# 🛰️ Parallel Plate: Digital Twin Chef Engine")
with gr.Tabs():
with gr.TabItem("Upload Image"):
img_input = gr.Image(type="pil")
with gr.TabItem("Upload Video"):
vid_input = gr.Video()
# Clear other tab when one is used
img_input.change(fn=lambda: None, outputs=vid_input)
vid_input.change(fn=lambda: None, outputs=img_input)
budget_slider = gr.Slider(5, 100, 25, label="Budget ($)")
days_slider = gr.Slider(1, 7, 3, label="Days of Supply")
with gr.Row():
scan_btn = gr.Button("🚀 Initialize Scan & Recipe Plan", variant="primary")
clear_btn = gr.Button("🧹 Clear")
with gr.Row():
orig_display = gr.Image(label="Input Source")
processed_display = gr.Image(label="Digital Twin Output")
inventory_df = gr.Dataframe(label="Asset Manifest")
output_text = gr.Markdown()
def clear_interface():
empty_df = pd.DataFrame(columns=["Ingredient Asset", "Qty", "Status", "Value"])
return [None, None, None, None, empty_df, ""]
clear_btn.click(fn=clear_interface, inputs=[], outputs=[img_input, vid_input, orig_display, processed_display, inventory_df, output_text])
# Helper to pick the active input
def choose_input(img, vid): return vid if vid else img
scan_btn.click(
fn=lambda img, vid, b, d: process_kitchen_operations(choose_input(img, vid), b, d),
inputs=[img_input, vid_input, budget_slider, days_slider],
outputs=[orig_display, processed_display, inventory_df, output_text]
)
if __name__ == "__main__":
demo.launch(theme=gr.themes.Monochrome()) |