diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d2435f56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +venv/ +train/ +__pycache__ +test/ +database/database.py +database/normalize.txt +weight/ \ No newline at end of file diff --git a/README.md b/README.md index b58f55ca..9140cca2 100644 --- a/README.md +++ b/README.md @@ -1,194 +1,73 @@ -# FoodSnap AI - Hackathon Project +# ๐Ÿฑ Food Classification & Nutrition Prediction +A deep learning model that classifies food from images using EfficientNetB0 and returns nutritional facts using a SQLite3 database. -๐ŸŽ‰ **Welcome to the FoodSnap AI Hackathon!** ๐ŸŽ‰ +## ๐Ÿš€ Features +- Image classification with **EfficientNetB0** pretrained on **ImageNet** -Get ready to combine your coding skills with the fascinating world of artificial intelligence and nutrition! We're excited to see what innovative solutions you come up with. +- **Transfer learning with fine-tuning** for improved accuracy -**The Challenge:** -Your mission, should you choose to accept it, is to develop a solution that can estimate the caloric content (and ideally, macronutrient breakdown โ€“ protein, carbs, fats) of a food item from an input image. +- Nutrition info retrieval from a **SQLite3 database** -## Table of Contents +- Exported model in both **`.keras`** and **`.tflite`** formats -1. [Project Overview](#project-overview) -2. [Why This Project?](#why-this-project) -3. [Scope & Implementation Freedom](#project-scope--implementation-freedom) -4. [Key Objectives / Potential Features](#key-objectives--potential-features) -5. [Helpful Resources & APIs](#helpful-resources--apis) - * [Food Image Datasets](#food-image-datasets) - * [Nutrition Information Databases & APIs](#nutrition-information-databases--apis) - * [Machine Learning / AI Tools](#machine-learning--ai-tools) -6. [Getting Started: Git & Submission Workflow](#getting-started-git--submission-workflow) - * [1. Fork the Repository](#1-fork-the-repository) - * [2. Clone Your Forked Repository](#2-clone-your-forked-repository) - * [3. Create a New Branch](#3-create-a-new-branch) - * [4. Develop Your Project](#4-develop-your-project) - * [5. Push Your Branch to Your Fork](#5-push-your-branch-to-your-fork) - * [6. Submit Your Project (Create a Pull Request)](#6-submit-your-project-create-a-pull-request) -7. [What to Include in Your Submission](#what-to-include-in-your-submission) -8. [Judging Criteria (General Pointers)](#judging-criteria-general-pointers) +## ๐Ÿ–ผ๏ธ Dataset +- 12 custom food categories +- 1200+ images ---- +## ๐Ÿง  Model Architecture +- Base model: EfficientNetB0 (ImageNet weights) -## Project Overview +- Layers: Base model, GlobalAveragePooling, Dense, Softmax -**Project Name:** FoodSnap AI +- Loss: sparse_categorical_crossentropy -**The Challenge:** -Develop a solution that can estimate the caloric content (and ideally, macronutrient breakdown โ€“ protein, carbs, fats) of a food item from an input image. +- Optimizer: Adam -## Why This Project? - -Understanding calorie intake is crucial for health and fitness. Manually logging food can be tedious. FoodSnap AI aims to simplify this process, making nutritional awareness more accessible to everyone. Imagine snapping a photo of your meal and instantly getting its nutritional information! +## ๐Ÿงช Results +- Training Accuracy: ~97% + +- Validation Accuracy: ~98% + +## ๐Ÿ’พ Model Export + +- `.tflite` + +## ๐Ÿ› ๏ธ Tools & Frameworks +- `Python` | `TensorFlow` | `Keras` | `EfficientNet` | `SQLite3` | `Google Colab` | `NumPy` + +## ๐Ÿ“ฆ Nutrition Database (Example) +| Food | Calories | Protein | Fat | +| ------ | -------- | ------- | ---- | +| Apple | 52 | 0.3g | 0.2g | +| Samosa | 262 | 4g | 17g | +| Banana | 96 | 1.3g | 0.3g | + +## Set Up and Run Locally + +Clone repository +```bash +git clone https://github.com/Adeelp1/foodsnap-ai.git +cd foodsnap-ai +``` +Checkout branch +```bash +git checkout feature/adeelp1 +``` + +Install dependencies: +```bash +pip install -r requirements.txt +``` +Make sure you have all dependencies installed, then run the program using: +```bash +python main.py +``` + +## ๐Ÿ“Œ Future Improvements +- Add more food categories + +- Integrate speech/text query input + +- Deploy on mobile/web app -## Project Scope & Implementation Freedom - -You have complete freedom in how you bring FoodSnap AI to life! - -* **Platform:** Mobile App (iOS, Android, cross-platform), Web App, Website, or even a Command Line Interface (CLI) tool. -* **Technology Stack:** Use any programming languages, frameworks, libraries, or APIs you prefer. Python, JavaScript, Java, Swift, Kotlin, Ruby, Go โ€“ the choice is yours! -* **Approach:** You can use pre-trained machine learning models, train your own, leverage existing food recognition APIs, or come up with a completely novel approach. - -## Key Objectives / Potential Features - -1. **Image Input:** The system must accept an image of food as input. -2. **Food Identification (Implicit or Explicit):** The system needs to identify the food item(s) in the image. This could be a direct output or an internal step. -3. **Calorie Estimation:** Based on the identified food, estimate its total calories. -4. **Macronutrient Breakdown (Bonus):** If possible, also estimate protein, carbohydrates, and fats. -5. **Portion Size Consideration (Advanced Bonus):** Accurately estimating portion size from an image is challenging but would be a significant enhancement. -6. **User Interface (for non-CLI):** If you're building an app or website, make it user-friendly and intuitive. -7. **Multiple Food Items (Advanced Bonus):** Can your solution handle an image with multiple food items on a plate? - -## Helpful Resources & APIs - -To get you started, here are some resources that might be useful. You are **not limited** to these and are encouraged to explore! - -### Food Image Datasets -*(For training/inspiration if you go the custom ML route)* - -* **Food-101:** [https://data.vision.ee.ethz.ch/cvl/datasets_extra/food-101/](https://data.vision.ee.ethz.ch/cvl/datasets_extra/food-101/) (101 food categories, 101,000 images) -* **UECFood-100 / UECFood-256:** [http://foodcam.mobi/dataset.html](http://foodcam.mobi/dataset.html) (Japanese food primarily, good for object detection) -* **Recipe1M+:** [http://pic2recipe.csail.mit.edu/](http://pic2recipe.csail.mit.edu/) (Images and recipes) -* **Google Images / Flickr:** Can be used for scraping specific food images (be mindful of terms of service). - -### Nutrition Information Databases & APIs -*(For calorie/macro lookup)* - -* **USDA FoodData Central API:** [https://fdc.nal.usda.gov/api-guide.html](https://fdc.nal.usda.gov/api-guide.html) (Comprehensive US food composition database) -* **Edamam Food Database API:** [https://developer.edamam.com/food-database-api](https://developer.edamam.com/food-database-api) (Offers free tier for recipe analysis and food database lookup) -* **Spoonacular API:** [https://spoonacular.com/food-api](https://spoonacular.com/food-api) (Nutrition, recipes, food products, free tier available) -* **MyFitnessPal / CalorieKing / FatSecret:** While direct API access might be limited or paid, these websites are excellent sources for manual data collection or understanding how nutritional information is presented. Web scraping *could* be an option, but always respect `robots.txt` and terms of service. -* **Open Food Facts:** [https://world.openfoodfacts.org/](https://world.openfoodfacts.org/) (A collaborative, free, and open database of food products from around the world. They have an API.) - -### Machine Learning / AI Tools - -* **TensorFlow / Keras:** For building and training custom models. -* **PyTorch:** Another popular deep learning framework. -* **OpenCV:** For image processing tasks. -* **Pre-trained Image Recognition Models:** (e.g., MobileNet, ResNet, InceptionV3 available via TensorFlow Hub, PyTorch Hub, etc.) These can often be fine-tuned for food recognition. -* **Cloud AI Services:** Google Cloud Vision AI, AWS Rekognition, Azure Computer Vision (these often have free tiers for experimentation and can perform object/food recognition out-of-the-box). - -## Getting Started: Git & Submission Workflow - -We will be using GitHub for version control and submission. Please follow these steps carefully. - -**This Repository (Main Project):** `https://github.com/WeCode-Community-Dev/foodsnap-ai` - -### 1. Fork the Repository -* Go to the main project repository: `https://github.com/WeCode-Community-Dev/foodsnap-ai` -* In the top-right corner of the page, click the "**Fork**" button. -* This will create a copy of the repository under your own GitHub account (e.g., `https://github.com/YOUR_USERNAME/foodsnap-ai`). This is *your* personal remote copy. - -### 2. Clone Your Forked Repository -* On your GitHub page for *your forked repository* (`https://github.com/YOUR_USERNAME/foodsnap-ai`), click the green "**Code**" button. -* Copy the HTTPS or SSH URL. -* Open your terminal or Git client and run: - ```bash - git clone https://github.com/YOUR_USERNAME/foodsnap-ai.git - cd foodsnap-ai - ``` - (Replace `YOUR_USERNAME` with your actual GitHub username.) - -### 3. Create a New Branch -* It's crucial to work on a new branch rather than directly on `main` or `master`. -* Choose a descriptive branch name, for example, `feature/your-team-name` or `solution-john-doe`. -* In your terminal, inside the `foodsnap-ai` directory, run: - ```bash - git checkout -b feature/your-team-name - ``` - (e.g., `git checkout -b feature/awesome-coders` or `git checkout -b solution-jane-doe`) -* You are now on your new branch. Verify by running `git branch`. - -### 4. Develop Your Project -* Start coding! Add your files, write your logic, and build your FoodSnap AI solution. -* Commit your changes frequently with clear commit messages: - ```bash - # Stage all new and modified files - git add . - # Or stage specific files - # git add path/to/your/file.py path/to/another/file.js - - # Commit your changes - git commit -m "feat: Implement image upload functionality" - # Example commit types: feat, fix, docs, style, refactor, test, chore - ``` - -### 5. Push Your Branch to Your Fork -* When you're ready to save your progress to *your remote fork on GitHub*, push your branch: - ```bash - git push origin feature/your-team-name - ``` - (Replace `feature/your-team-name` with your actual branch name.) -* If it's the first time pushing this branch, Git might suggest a command like `git push --set-upstream origin feature/your-team-name`. Use that command. - -### 6. Submit Your Project (Create a Pull Request) -* Once your project is complete (or at a submittable stage), go to *your* forked repository on GitHub (`https://github.com/YOUR_USERNAME/foodsnap-ai`). -* You should see a prompt saying "`feature/your-team-name` had recent pushes". Click the "**Compare & pull request**" button. -* If you don't see the prompt, go to the "**Pull requests**" tab and click "**New pull request**". -* **Crucially, ensure the settings are:** - * **Base repository:** `WeCode-Community-Dev/foodsnap-ai` - * **Base branch:** `main` (or `master`, whichever is the default for this repository) - * **Head repository:** `YOUR_USERNAME/foodsnap-ai` - * **Compare branch:** `feature/your-team-name` (your development branch) -* Write a clear title and a detailed description for your Pull Request (PR). Include: - * A brief overview of your solution. - * Technologies used. - * How to run/test your project (setup, commands, etc.). - * Any known issues or limitations. - * A link to a live demo if applicable (e.g., Heroku, Netlify, GitHub Pages). - * Screenshots or a short video showcasing your project can be very helpful! -* Click "**Create pull request**". - -## What to Include in Your Submission -*(In your project's directory, pushed to your branch and included in the PR)* - -* **Source Code:** All the code for your project. -* **`README.md` (Your Project's README):** This is very important! Your project's `README.md` (different from this main hackathon `README.md`) should include: - * Project Title & Team Name/Members (if applicable). - * A brief description of your FoodSnap AI solution. - * What features you implemented. - * Tech stack used. - * **Clear instructions on how to set up and run *your specific project* locally.** This includes dependencies, environment variables, build steps, and run commands. - * Any API keys or environment variables needed (explain how to get them, but **DO NOT COMMIT ACTUAL KEYS** to the repository). Use a `.env.example` file to show what's needed. - * Link to a live demo (if any). -* **(Optional but Recommended)** A short demo video or presentation slides (you can link these in your PR description or in your project's README). - -## Judging Criteria (General Pointers) - -While specific criteria might be announced, generally projects are evaluated on: - -* **Functionality:** Does it work as intended? Does it achieve the core goal of calorie estimation from an image? -* **Accuracy:** How close are the calorie/macro estimations? (We understand this is complex!) -* **Innovation & Creativity:** Did you come up with a unique approach or an interesting feature? -* **Technical Implementation:** Quality of code, choice of technology, and complexity handled. -* **User Experience (UX/UI):** If applicable, is the application easy and pleasant to use? -* **Presentation/Demo:** How well you explain and showcase your project. -* **Adherence to Submission Guidelines:** Including a good project `README.md` and following the Git workflow. - - ---- - -Good luck, innovators! We can't wait to see your FoodSnap AI creations. Remember to have fun, learn, and collaborate! - -**Happy Hacking!** -The WeCode Community Dev Team diff --git a/config.py b/config.py new file mode 100644 index 00000000..2720231b --- /dev/null +++ b/config.py @@ -0,0 +1,10 @@ +import os + +BASE_DIR = os.path.dirname(__file__) + +# model path +MODEL_FILE_PATH = os.path.join(BASE_DIR, "model", "model12(32).tflite") +CLASS_NAME_FILE_PATH = os.path.join(BASE_DIR, "model", "class_name.pkl") + +# database path +DB_PATH = os.path.join(BASE_DIR, "database", "nutrition.db") \ No newline at end of file diff --git a/database/nutrition.db b/database/nutrition.db new file mode 100644 index 00000000..671f7b24 Binary files /dev/null and b/database/nutrition.db differ diff --git a/food.py b/food.py new file mode 100644 index 00000000..8a18c7da --- /dev/null +++ b/food.py @@ -0,0 +1,63 @@ +import pickle +import numpy as np +import os +import warnings +from config import (MODEL_FILE_PATH, CLASS_NAME_FILE_PATH) + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' +os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0" +warnings.filterwarnings("ignore") + +import tensorflow as tf # type: ignore + +class PredictFood: + + interpreter = tf.lite.Interpreter(model_path=MODEL_FILE_PATH) + with open(CLASS_NAME_FILE_PATH, "rb") as f: + class_names = pickle.load(f) + + def __init__(self) -> None: + self.classify_lite = self.interpreter.get_signature_runner('serving_default') + self.img_height = 224 + self.img_width = 224 + + def convert_image_to_array(self, img: str) -> tf.Tensor: + """ + Generate an array of an image + + Args: + img (str): Path of the image + + Returns: + tf.Tensor: The image as a 4D tensor (1, height, width, channels) + """ + img = tf.keras.utils.load_img(img, target_size=(self.img_height, self.img_width)) + image_array = tf.keras.utils.img_to_array(img) + image_array = tf.expand_dims(image_array, 0) + + return image_array + + def predict(self, image_array: tf.Tensor) -> tuple[str, float]: + """ + Predict the food item from the given image tensor. + + Args: + image_array (tf.Tensor): A 4D image tensor (1, height, width, channels) + + Returns: + tupe[str, float]: A tuple containing: + -Predicted food name as string + -Confidence score as a float (percentage) + """ + predictions = self.classify_lite(keras_tensor_990=image_array)['output_0'] + score = tf.nn.softmax(predictions) + + return self.class_names[np.argmax(score)], 100 * np.max(score) + + +def find_food(img: str) -> str: + pf = PredictFood() + img_array = pf.convert_image_to_array(img) + img_name, score = pf.predict(img_array) + + return img_name \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 00000000..424ac1eb --- /dev/null +++ b/main.py @@ -0,0 +1,21 @@ +import os +import food +from nutrition import Nutrition + + +if __name__ == "__main__": + print("type exit for exit") + while True: + path = input("Enter image path: ") + if path == "exit": break + if not os.path.isfile(path): + print("Enter a valid path") + continue + weight = float(input("Please enter the weight of the food (in grams): ")) + + name = food.find_food(path) + ntn = Nutrition() + nutri = ntn.get_nutrition(name, weight) + cal, fat, carb, protien = nutri + + print(f"\nfood is a {name}\nNutrients : calories = {cal}, fat = {fat}g, carbs = {carb}g, protien = {protien}g\n") \ No newline at end of file diff --git a/model/class_name.pkl b/model/class_name.pkl new file mode 100644 index 00000000..ffffd424 Binary files /dev/null and b/model/class_name.pkl differ diff --git a/model/model12(32).tflite b/model/model12(32).tflite new file mode 100644 index 00000000..528067c8 Binary files /dev/null and b/model/model12(32).tflite differ diff --git a/nutrition.py b/nutrition.py new file mode 100644 index 00000000..7b218057 --- /dev/null +++ b/nutrition.py @@ -0,0 +1,55 @@ +import sqlite3 +from config import DB_PATH + +class Nutrition: + def __init__(self) -> None: + """ + Establish a connection to the SQLite database. + """ + self.conn = sqlite3.connect(DB_PATH) + self.cursor = self.conn.cursor() + + def __del__(self) -> None: + """ + Close the established database connection. + """ + self.cursor.close() + + def get_nutrition(self, name: str, weight: float) -> list[float]: + """ + Retrieve nutrition information from the database. + + Args: + name (str): Name of the food item. + weight (float): Weight of the food in grams. + + Returns: + list[float]: A list containing nutritional values: + [calories, fat, carbs, protein] + """ + self.cursor.execute("SELECT calories, fat, carbs, protien FROM nutrition_info WHERE food_name = ?", (name,)) + nutri = self.cursor.fetchall() + nutrition_info = self.calculate_calories(nutri[0], weight) + + return nutrition_info + + def calculate_calories(self, nutri_info: tuple[float, float, float, float], weight: float) -> list[float]: + """ + Calculate the nutritional values based on the given weight. + + Args: + nutri_info (tuple[float, float, float, float]): Nutritional values per 100g retrieved from the database, in the order: + (calories, fat, carbs, protein). + weight (float): Weight of the food in grams. + + Returns: + list[float]: Scaled nutritional values based on the given weight: + [calories, fat, carbs, protein] + """ + factor = weight/100 + nutrition_result = [] + + for i in range(len(nutri_info)): + nutrition_result.append(nutri_info[i] * factor) + + return nutrition_result \ No newline at end of file diff --git a/requirement.txt b/requirement.txt new file mode 100644 index 00000000..b7476989 --- /dev/null +++ b/requirement.txt @@ -0,0 +1,4 @@ +pickle==4.0 +numpy==2.1.3 +pillow==11.2.1 +tensorflow==2.19.0 \ No newline at end of file