Muhammad Nabeel
commited on
Upload 5 files
Browse filesThis app allows users to upload food images or search for recipes by name. It uses a pre-trained Gen AI model to classify the food and fetch recipe suggestions through an external API. Users can view the recipe details, including ingredients, servings, and instructions, and even download the recipe as a PDF.
Key Features:-
1) Food Image Classification: Classify food images using a machine learning model.
2) Recipe Search: Fetch recipe suggestions based on food names.
3) PDF Download: Download recipe details as a PDF with ingredients and instructions.
- app.py +81 -0
- image_classifier.py +8 -0
- pdf_generator.py +41 -0
- recipe_fetcher.py +13 -0
- requirements.txt +6 -0
app.py
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
5 |
+
from pdf_generator import generate_pdf
|
6 |
+
|
7 |
+
# Function to display recipes in a readable format
|
8 |
+
def display_recipes(recipes):
|
9 |
+
recipe_text = ""
|
10 |
+
if recipes:
|
11 |
+
for recipe in recipes:
|
12 |
+
recipe_text += f"**Title**: {recipe['title']}\n"
|
13 |
+
recipe_text += "**Ingredients**:\n"
|
14 |
+
for ingredient in recipe['ingredients'].split('|'):
|
15 |
+
recipe_text += f"- {ingredient}\n"
|
16 |
+
recipe_text += f"**Servings**: {recipe['servings']}\n"
|
17 |
+
recipe_text += "**Instructions**:\n"
|
18 |
+
recipe_text += f"{recipe['instructions'][:300]}...\n" # Truncate for brevity
|
19 |
+
recipe_text += "-" * 40 + "\n"
|
20 |
+
else:
|
21 |
+
recipe_text = "No recipes found."
|
22 |
+
|
23 |
+
return recipe_text
|
24 |
+
|
25 |
+
# Main Streamlit app UI
|
26 |
+
def main():
|
27 |
+
st.title("Food Classifier and Recipe Finder")
|
28 |
+
st.write("Choose an option to get food recipes.")
|
29 |
+
|
30 |
+
# Radio buttons to select the mode (search or upload)
|
31 |
+
option = st.radio("Choose an option", ("Search Food Recipe", "Upload Image to Predict Food"))
|
32 |
+
|
33 |
+
# If 'Search Food Recipe' is selected
|
34 |
+
if option == "Search Food Recipe":
|
35 |
+
query = st.text_input("Enter a food name", "")
|
36 |
+
if query:
|
37 |
+
recipes = fetch_recipe(query)
|
38 |
+
recipe_text = display_recipes(recipes)
|
39 |
+
st.text_area("Recipe Details", recipe_text, height=300)
|
40 |
+
|
41 |
+
# Only show PDF download button if recipes are found
|
42 |
+
if "No recipes found." not in recipe_text:
|
43 |
+
pdf_file = generate_pdf(recipe_text, query)
|
44 |
+
with open(pdf_file, "rb") as f:
|
45 |
+
st.download_button("Download Recipe as PDF", f, file_name=pdf_file)
|
46 |
+
|
47 |
+
# If 'Upload Image to Predict Food' is selected
|
48 |
+
elif option == "Upload Image to Predict Food":
|
49 |
+
st.write("Upload an image to predict the food item and get the recipe.")
|
50 |
+
|
51 |
+
image_file = st.file_uploader("Upload an image", type=["jpg", "jpeg", "png"])
|
52 |
+
|
53 |
+
if image_file is not None:
|
54 |
+
# Open the uploaded image
|
55 |
+
image = Image.open(image_file).convert("RGB")
|
56 |
+
|
57 |
+
# Display the image
|
58 |
+
st.image(image, caption="Uploaded Image", use_container_width=True)
|
59 |
+
|
60 |
+
# Classify the food using the pipeline
|
61 |
+
label_with_pipeline = classify_food_with_pipeline(image)
|
62 |
+
st.write(f"**Predicted Food**: {label_with_pipeline}")
|
63 |
+
|
64 |
+
# Fetch the recipe based on the predicted label
|
65 |
+
recipes = fetch_recipe(label_with_pipeline)
|
66 |
+
|
67 |
+
# Display the fetched recipe(s)
|
68 |
+
recipe_text = display_recipes(recipes)
|
69 |
+
st.text_area("Recipe Details", recipe_text, height=300)
|
70 |
+
|
71 |
+
# Only show PDF download button if recipes are found
|
72 |
+
if "No recipes found." not in recipe_text:
|
73 |
+
pdf_file = generate_pdf(recipe_text, label_with_pipeline)
|
74 |
+
with open(pdf_file, "rb") as f:
|
75 |
+
st.download_button("Download Recipe as PDF", f, file_name=pdf_file)
|
76 |
+
# Add monogram at the bottom of the Streamlit app
|
77 |
+
st.markdown("<br><br><h5 style='text-align: center;'>Developed by M.Nabeel</h5>", unsafe_allow_html=True)
|
78 |
+
|
79 |
+
# Run the app
|
80 |
+
if __name__ == "__main__":
|
81 |
+
main()
|
image_classifier.py
ADDED
@@ -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"]
|
pdf_generator.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fpdf import FPDF
|
2 |
+
|
3 |
+
|
4 |
+
def capitalize_first_letter(sentence):
|
5 |
+
words = sentence.split() # Split the sentence into words
|
6 |
+
capitalized_words = [word.capitalize() for word in words] # Capitalize each word's first letter
|
7 |
+
return ' '.join(capitalized_words) # Join the words back into a sentence
|
8 |
+
|
9 |
+
# Example usage:
|
10 |
+
# Function to generate PDF from recipe details
|
11 |
+
def generate_pdf(recipe_text: str, label_with_pipeline: str):
|
12 |
+
pdf = FPDF()
|
13 |
+
pdf.set_auto_page_break(auto=True, margin=15)
|
14 |
+
pdf.add_page()
|
15 |
+
|
16 |
+
sentence = label_with_pipeline
|
17 |
+
result = capitalize_first_letter(sentence)
|
18 |
+
|
19 |
+
# Set font for title
|
20 |
+
pdf.set_font("Arial", size=16, style='B')
|
21 |
+
pdf.cell(200, 10, txt=f"{result} Recipe ", ln=True, align="C")
|
22 |
+
|
23 |
+
# Add recipe details to PDF
|
24 |
+
pdf.ln(10) # Add some space
|
25 |
+
pdf.set_font("Arial", size=12)
|
26 |
+
|
27 |
+
# Add the recipe text
|
28 |
+
pdf.multi_cell(0, 10, txt=recipe_text)
|
29 |
+
|
30 |
+
# Add monogram
|
31 |
+
pdf.ln(10)
|
32 |
+
pdf.set_font("Arial", size=10)
|
33 |
+
pdf.cell(200, 10, txt="Developed by M. Nabeel", ln=True, align="C")
|
34 |
+
|
35 |
+
# Save PDF to a file
|
36 |
+
|
37 |
+
|
38 |
+
pdf_output = f"{result} Recipe.pdf"
|
39 |
+
pdf.output(pdf_output)
|
40 |
+
|
41 |
+
return pdf_output
|
recipe_fetcher.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
|
3 |
+
# Function to fetch recipe based on the classified label
|
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 |
+
else:
|
13 |
+
return None
|
requirements.txt
ADDED
@@ -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
|