Spaces:
Sleeping
Upload 6 files
Browse filesThis app helps you identify foods and find recipes easily. It can classify foods from images, provide detailed recipes, and give you nutrition information. You can also download recipes in PDF format.
Key Features:
• mage Upload: Upload food images and the app will predict what food it is.
• Recipe Search: Enter a food name and get detailed recipes including ingredients and instructions.
• Nutrition Info: Get a table of nutrition details for the food you search.
• PDF Export: Download your favorite recipes as PDFs.
• User-Friendly: Simple and clean interface for easy use.
How It Works:
• Upload an Image: Upload a food image and the app will predict the food.
• Search for Recipes: Enter a food name to find recipes and detailed cooking instructions.
• Nutrition Information: View nutrition details like fat, carbs, sugar, and sodium for the food.
• Download Recipes: Save recipes as PDF files for easy access later.
Tech Stack:
• Machine Learning: Uses image classification to predict food.
• APIs: Fetch recipes and nutrition data from external sources.
• Streamlit: Beautiful web interface for a smooth user experience.
- app.py +78 -0
- image_classifier.py +8 -0
- nutrition_fetcher.py +17 -0
- pdf_generator.py +34 -0
- recipe_fetcher.py +29 -0
- requirements.txt +6 -0
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from PIL import Image
|
3 |
+
from image_classifier import classify_food_with_pipeline
|
4 |
+
from recipe_fetcher import fetch_recipe, display_recipes
|
5 |
+
from nutrition_fetcher import fetch_nutrition
|
6 |
+
from pdf_generator import generate_pdf
|
7 |
+
|
8 |
+
def main():
|
9 |
+
st.title("Food Classifier and Recipe Finder")
|
10 |
+
st.write("Choose an option to get food recipes and nutrition details.")
|
11 |
+
|
12 |
+
# Option selection
|
13 |
+
option = st.radio("Choose an option", ("Search Food Recipe", "Upload Image to Predict Food"))
|
14 |
+
|
15 |
+
# Search Food Recipe Option
|
16 |
+
if option == "Search Food Recipe":
|
17 |
+
query = st.text_input("Enter a food name", "")
|
18 |
+
if query:
|
19 |
+
try:
|
20 |
+
# Fetch and display recipes
|
21 |
+
recipes = fetch_recipe(query)
|
22 |
+
recipe_text = display_recipes(recipes)
|
23 |
+
st.text_area("Recipe Details", recipe_text, height=300)
|
24 |
+
|
25 |
+
# Fetch and display nutrition details
|
26 |
+
st.write("### Nutrition Details")
|
27 |
+
nutrition_df = fetch_nutrition(query)
|
28 |
+
if nutrition_df is not None:
|
29 |
+
st.dataframe(nutrition_df)
|
30 |
+
else:
|
31 |
+
st.write("No nutrition details found.")
|
32 |
+
|
33 |
+
# Generate PDF
|
34 |
+
if "No recipes found." not in recipe_text:
|
35 |
+
pdf_file = generate_pdf(recipe_text, query)
|
36 |
+
with open(pdf_file, "rb") as f:
|
37 |
+
st.download_button("Download Recipe as PDF", f, file_name=pdf_file)
|
38 |
+
except Exception as e:
|
39 |
+
st.error(f"An error occurred while fetching data: {e}")
|
40 |
+
|
41 |
+
# Upload Image Option
|
42 |
+
elif option == "Upload Image to Predict Food":
|
43 |
+
image_file = st.file_uploader("Upload an image", type=["jpg", "jpeg", "png"])
|
44 |
+
if image_file is not None:
|
45 |
+
try:
|
46 |
+
# Display and process image
|
47 |
+
image = Image.open(image_file).convert("RGB")
|
48 |
+
st.image(image, caption="Uploaded Image", use_container_width=True) # Updated parameter
|
49 |
+
|
50 |
+
# Predict Food
|
51 |
+
label = classify_food_with_pipeline(image)
|
52 |
+
st.write(f"**Predicted Food**: {label}")
|
53 |
+
|
54 |
+
# Fetch and display recipes
|
55 |
+
recipes = fetch_recipe(label)
|
56 |
+
recipe_text = display_recipes(recipes)
|
57 |
+
st.text_area("Recipe Details", recipe_text, height=300)
|
58 |
+
|
59 |
+
# Fetch and display nutrition details
|
60 |
+
st.write("### Nutrition Details")
|
61 |
+
nutrition_df = fetch_nutrition(label)
|
62 |
+
if nutrition_df is not None:
|
63 |
+
st.dataframe(nutrition_df)
|
64 |
+
else:
|
65 |
+
st.write("No nutrition details found.")
|
66 |
+
|
67 |
+
# Generate PDF
|
68 |
+
if "No recipes found." not in recipe_text:
|
69 |
+
pdf_file = generate_pdf(recipe_text, label)
|
70 |
+
with open(pdf_file, "rb") as f:
|
71 |
+
st.download_button("Download Recipe as PDF", f, file_name=pdf_file)
|
72 |
+
except Exception as e:
|
73 |
+
st.error(f"An error occurred while processing the image: {e}")
|
74 |
+
|
75 |
+
st.markdown("<br><br><h5 style='text-align: center;'>Developed by M.Nabeel</h5>", unsafe_allow_html=True)
|
76 |
+
|
77 |
+
if __name__ == "__main__":
|
78 |
+
main()
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import pipeline
|
2 |
+
from PIL import Image
|
3 |
+
|
4 |
+
# Function to classify food using the pipeline
|
5 |
+
def classify_food_with_pipeline(image: Image):
|
6 |
+
pipe = pipeline("image-classification", model="nateraw/food")
|
7 |
+
result = pipe(image)
|
8 |
+
return result[0]["label"]
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
import pandas as pd
|
3 |
+
|
4 |
+
# Function to fetch nutrition details
|
5 |
+
def fetch_nutrition(query: str):
|
6 |
+
api_url = f'https://api.api-ninjas.com/v1/nutrition?query={query}'
|
7 |
+
headers = {'X-Api-Key': 'Hu4DkNyaIFT+E/FfCPRaYw==EUSFTIrXpCdQXrjH'}
|
8 |
+
response = requests.get(api_url, headers=headers)
|
9 |
+
|
10 |
+
if response.status_code == requests.codes.ok:
|
11 |
+
data = response.json()
|
12 |
+
if data:
|
13 |
+
df = pd.DataFrame(data)
|
14 |
+
# Drop unwanted columns
|
15 |
+
df = df.drop(columns=['calories', 'serving_size_g', 'protein_g'], errors='ignore')
|
16 |
+
return df
|
17 |
+
return None
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fpdf import FPDF
|
2 |
+
|
3 |
+
# Function to capitalize the first letter of each word
|
4 |
+
def capitalize_first_letter(sentence):
|
5 |
+
words = sentence.split()
|
6 |
+
return ' '.join([word.capitalize() for word in words])
|
7 |
+
|
8 |
+
# Function to generate PDF from recipe details
|
9 |
+
def generate_pdf(recipe_text: str, label_with_pipeline: str):
|
10 |
+
pdf = FPDF()
|
11 |
+
pdf.set_auto_page_break(auto=True, margin=15)
|
12 |
+
pdf.add_page()
|
13 |
+
|
14 |
+
# Capitalize label for the title
|
15 |
+
result = capitalize_first_letter(label_with_pipeline)
|
16 |
+
|
17 |
+
# Add Title
|
18 |
+
pdf.set_font("Arial", size=16, style='B')
|
19 |
+
pdf.cell(200, 10, txt=f"{result} Recipe", ln=True, align="C")
|
20 |
+
|
21 |
+
# Add Recipe Text
|
22 |
+
pdf.ln(10)
|
23 |
+
pdf.set_font("Arial", size=12)
|
24 |
+
pdf.multi_cell(0, 10, txt=recipe_text)
|
25 |
+
|
26 |
+
# Add Monogram
|
27 |
+
pdf.ln(10)
|
28 |
+
pdf.set_font("Arial", size=10)
|
29 |
+
pdf.cell(200, 10, txt="Developed by M.Nabeel", ln=True, align="C")
|
30 |
+
|
31 |
+
# Save PDF
|
32 |
+
pdf_output = f"{result} Recipe.pdf"
|
33 |
+
pdf.output(pdf_output)
|
34 |
+
return pdf_output
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
|
3 |
+
# Function to fetch recipe based on the food query
|
4 |
+
def fetch_recipe(query: str):
|
5 |
+
api_url = f'https://api.api-ninjas.com/v1/recipe?query={query}'
|
6 |
+
headers = {'X-Api-Key': 'Hu4DkNyaIFT+E/FfCPRaYw==EUSFTIrXpCdQXrjH'}
|
7 |
+
response = requests.get(api_url, headers=headers)
|
8 |
+
|
9 |
+
if response.status_code == requests.codes.ok:
|
10 |
+
recipes = response.json() # Convert the response to JSON
|
11 |
+
return recipes
|
12 |
+
return None
|
13 |
+
|
14 |
+
# Function to display recipes in a readable format
|
15 |
+
def display_recipes(recipes):
|
16 |
+
recipe_text = ""
|
17 |
+
if recipes:
|
18 |
+
for recipe in recipes:
|
19 |
+
recipe_text += f"**Title**: {recipe['title']}\n"
|
20 |
+
recipe_text += "**Ingredients**:\n"
|
21 |
+
for ingredient in recipe['ingredients'].split('|'):
|
22 |
+
recipe_text += f"- {ingredient}\n"
|
23 |
+
recipe_text += f"**Servings**: {recipe['servings']}\n"
|
24 |
+
recipe_text += "**Instructions**:\n"
|
25 |
+
recipe_text += f"{recipe['instructions'][:300]}...\n"
|
26 |
+
recipe_text += "-" * 40 + "\n"
|
27 |
+
else:
|
28 |
+
recipe_text = "No recipes found."
|
29 |
+
return recipe_text
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
transformers==4.47.0
|
2 |
+
Pillow==11.0.0
|
3 |
+
requests==2.32.3
|
4 |
+
streamlit==1.41.0
|
5 |
+
torch==2.5.1
|
6 |
+
fpdf==1.7.2
|