├── .env.sample
├── .gitignore
├── README.md
├── calorie_counter.py
├── config
├── README.md
├── calorieapp.service
└── calorieapp_apache.conf
├── requirements.txt
├── server.py
├── static
├── css
│ └── style.css
├── images
│ └── camera.png
└── js
│ └── script.js
├── templates
└── index.html
└── test_images
├── DALL·E 2024-05-23 08.13.42 - A realistic image of a healthy lunch consisting of a grilled chicken salad with mixed greens, cherry tomatoes, sliced cucumbers, and a light vinaigret.webp
├── DALL·E 2024-05-23 08.16.12 - A photo-realistic image depicting a healthy lunch arranged on a plate, as if taken with a smartphone. The lunch consists of a colorful salad with mixe.webp
├── DALL·E 2024-05-23 08.17.46 - A realistic image of a delicious burger meal. The burger is large, with a glossy sesame seed bun, layers of fresh lettuce, sliced tomatoes, juicy beef.webp
└── DALL·E 2024-05-23 08.19.51 - A realistic image of an unhealthy lunch meal consisting of a greasy cheeseburger with melted cheddar oozing out, a large side of golden, crispy french.webp
/.env.sample:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=sk-1234567890abcdef1234567890abcdef
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | __pycache__/
3 | venv/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AI Calorie Counter App
2 |
3 | This GPT-4o powered Flask web app tells you how many calories there are in an image of a meal you upload
4 |
5 | ## Quick Start
6 |
7 | 0. Rename the `.env.sample` file into `.env` and add your OpenAI API key
8 |
9 | 1. Start the server:
10 |
11 | ```sh
12 | $ python3 server.py
13 | ```
14 |
15 | 2. Go to http://localhost:5000
16 |
17 |
18 | ## Terminal usage
19 |
20 | You can also use it from the terminal:
21 |
22 | ```sh
23 | $ python3 calorie_counter.py IMAGE_FILE
24 | ```
25 |
--------------------------------------------------------------------------------
/calorie_counter.py:
--------------------------------------------------------------------------------
1 | from openai import OpenAI
2 | from dotenv import load_dotenv
3 | import base64
4 | import json
5 | import sys
6 |
7 | load_dotenv()
8 | client = OpenAI()
9 |
10 | def get_calories_from_image(image_path):
11 | with open(image_path, "rb") as image:
12 | base64_image = base64.b64encode(image.read()).decode("utf-8")
13 |
14 | response = client.chat.completions.create(
15 | model="gpt-4o",
16 | response_format={"type": "json_object"},
17 | messages=[
18 | {
19 | "role": "system",
20 | "content": """You are a dietitian. A user sends you an image of a meal and you tell them how many calories are in it. Use the following JSON format:
21 |
22 | {
23 | "reasoning": "reasoning for the total calories",
24 | "food_items": [
25 | {
26 | "name": "food item name",
27 | "calories": "calories in the food item"
28 | }
29 | ],
30 | "total": "total calories in the meal"
31 | }"""
32 | },
33 | {
34 | "role": "user",
35 | "content": [
36 | {
37 | "type": "text",
38 | "text": "How many calories is in this meal?"
39 | },
40 | {
41 | "type": "image_url",
42 | "image_url": {
43 | "url": f"data:image/jpeg;base64,{base64_image}"
44 | }
45 | }
46 | ]
47 | },
48 | ],
49 | )
50 |
51 | response_message = response.choices[0].message
52 | content = response_message.content
53 |
54 | return json.loads(content)
55 |
56 | if __name__ == "__main__":
57 | image_path = sys.argv[1]
58 | calories = get_calories_from_image(image_path)
59 | print(json.dumps(calories, indent=4))
60 |
--------------------------------------------------------------------------------
/config/README.md:
--------------------------------------------------------------------------------
1 | ## Deployment instructions
2 |
3 | Here's how you can deploy the app on an AWS EC2 instance (Ubuntu 24.04)
4 |
5 | Install required packages
6 |
7 | ```sh
8 | $ sudo apt update && sudo apt install python3.12-venv python3-pip apache2
9 | ```
10 |
11 | Create a directory for the project and set up permissions.
12 |
13 | ```sh
14 | $ sudo mkdir /srv/calorieapp
15 | $ sudo chown ubuntu:ubuntu /srv/calorieapp/
16 | ```
17 |
18 | Install Python virtual environment and requirements
19 |
20 | ```sh
21 | $ cd /srv/calorieapp/
22 | $ python3 -m venv venv
23 | $ source venv/bin/activate
24 | $ pip install -r requirements.txt
25 | ```
26 |
27 | Add Gunicorn user and set up permissions
28 |
29 | ```sh
30 | $ sudo adduser --system --no-create-home --group gunicorn
31 | $ sudo chown -R gunicorn:gunicorn /srv/calorieapp/
32 | ```
33 |
34 | Install background service
35 |
36 | ```sh
37 | $ sudo cp config/calorieapp.service /etc/systemd/system/
38 | $ sudo systemctl daemon-reload
39 | $ sudo systemctl enable calorieapp
40 | $ sudo service calorieapp start
41 | $ sudo service calorieapp status
42 | ```
43 |
44 | Install Apache configuration
45 |
46 | ```sh
47 | $ sudo cp config/calorieapp_apache.conf /etc/apache2/sites-available/
48 | $ sudo a2enmod proxy proxy_http
49 | $ sudo a2ensite calorieapp_apache.conf
50 | $ sudo service apache2 reload
51 | ```
52 |
53 | Install Let's Encrypt SSL certificate
54 |
55 | ```sh
56 | $ sudo snap install --classic certbot
57 | $ sudo ln -s /snap/bin/certbot /usr/bin/certbot
58 | $ sudo certbot --apache
59 | ```
60 |
61 | Set up cron job for automatic SSL certificate renewal
62 |
63 | ```sh
64 | $ sudo crontab -e
65 | ```
66 |
67 | Add this to crontab:
68 |
69 | ```
70 | 0 4 * * 1 /usr/bin/certbot --renew && /usr/sbin/service apache2 reload
71 | ```
72 |
73 | Crate swapfile (bonus)
74 |
75 | ```sh
76 | $ sudo fallocate -l 1G /swapfile
77 | $ sudo chmod 0600 /swapfile
78 | $ sudo mkswap /swapfile
79 | $ sudo swapon /swapfile
80 | ```
81 |
82 | Add swapfile to `/etc/fstab` to persist over boot
83 |
84 | ```sh
85 | $ echo "/swapfile none swap sw 0 0" > /etc/fstab
86 | ```
87 |
--------------------------------------------------------------------------------
/config/calorieapp.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Calorie App Server
3 | After=network.target
4 |
5 | [Service]
6 | ExecStart=/srv/calorieapp/venv/bin/gunicorn server:app -w 4 -b 127.0.0.1:8000
7 | WorkingDirectory=/srv/calorieapp/
8 | Restart=on-failure
9 | User=gunicorn
10 | Group=gunicorn
11 |
12 | [Install]
13 | WantedBy=multi-user.target
14 |
--------------------------------------------------------------------------------
/config/calorieapp_apache.conf:
--------------------------------------------------------------------------------
1 |
Upload a meal image to calculate its calories
13 | 14 | 15 | 16 |