Spaces:
Sleeping
Sleeping
import streamlit as st | |
import torch | |
import cv2 | |
import numpy as np | |
from ultralytics import YOLO | |
from PIL import Image | |
import easyocr | |
import requests | |
def load_model(): | |
# Load the pre-trained YOLOv8 model for object detection | |
model = YOLO("yolov8n.pt") # You can use a custom-trained model for food detection | |
return model | |
def detect_ingredients(image, model): | |
results = model(image) | |
detected_items = set() | |
for result in results: | |
for box in result.boxes: | |
cls = result.names[int(box.cls[0])] | |
detected_items.add(cls) | |
return list(detected_items) | |
def extract_text(image): | |
# Use EasyOCR to extract text from the image | |
reader = easyocr.Reader(['en']) # Specify language | |
results = reader.readtext(np.array(image)) # Convert PIL image to NumPy | |
extracted_text = [text[1] for text in results] # Extract detected text | |
return extracted_text | |
MEALDB_API_URL = "https://www.themealdb.com/api/json/v1/1" | |
def fetch_ingredients(): | |
"""Fetches a list of available ingredients from TheMealDB API.""" | |
url = f"{MEALDB_API_URL}/list.php?i=list" | |
response = requests.get(url) | |
if response.status_code == 200: | |
data = response.json() | |
return [item["strIngredient"].lower() for item in data["meals"]] | |
return [] | |
def filter_valid_ingredients(detected_ingredients): | |
"""Filters detected ingredients against TheMealDB ingredient list.""" | |
valid_ingredients = fetch_ingredients() | |
return [ing for ing in detected_ingredients if ing.lower() in valid_ingredients] | |
def get_recipes(ingredients): | |
"""Fetch recipes from TheMealDB based on detected ingredients.""" | |
recipe_list = set() | |
for ingredient in ingredients: | |
url = f"{MEALDB_API_URL}/filter.php?i={ingredient}" | |
response = requests.get(url) | |
if response.status_code == 200: | |
data = response.json() | |
if data["meals"]: | |
for meal in data["meals"]: | |
recipe_list.add(meal["strMeal"]) | |
return list(recipe_list) if recipe_list else ["No matching recipes found."] | |
def main(): | |
st.title("VQA Recipe Generator") | |
st.write("Upload an image of ingredients, and we'll suggest recipes you can make!") | |
with st.expander("How It Works"): | |
st.write(""" | |
1. **Upload an Image**: Upload a photo of ingredients you have. | |
2. **Ingredient Detection**: The app uses a YOLOv8 model to detect visible ingredients. | |
3. **Text Extraction**: Any text in the image (e.g., labels) is extracted using EasyOCR. | |
4. **Ingredient Validation**: The detected ingredients are cross-checked with TheMealDB database. | |
5. **Recipe Suggestions**: The app fetches recipes that match the available ingredients. | |
P.S This is a work in progress so it can't cater all ingredients and can't really detect if ingredients don't have labels. | |
""") | |
model = load_model() | |
uploaded_file = st.file_uploader("Upload an image", type=["jpg", "png", "jpeg"]) | |
if uploaded_file: | |
image = Image.open(uploaded_file) | |
st.image(image, caption="Uploaded Image", use_column_width=True) | |
# Convert image for processing | |
img_array = np.array(image) | |
img_array = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) | |
with st.spinner("Detecting ingredients..."): | |
detected_ingredients = detect_ingredients(img_array, model) | |
extracted_text = extract_text(image) | |
# Merge OCR text with detected objects | |
detected_ingredients.extend(extracted_text) | |
# Filter only valid ingredients from TheMealDB | |
valid_ingredients = filter_valid_ingredients(detected_ingredients) | |
st.subheader("Detected Ingredients:") | |
st.write(", ".join(valid_ingredients)) | |
recipes = get_recipes(valid_ingredients) | |
st.subheader("Suggested Recipes:") | |
for recipe in recipes: | |
st.write(f"- {recipe}") | |
if __name__ == "__main__": | |
main() | |