Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
unconv committed May 23, 2024
0 parents commit e72eaba
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OPENAI_API_KEY=sk-1234567890abcdef1234567890abcdef
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
__pycache__/
venv/
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# AI Calorie Counter App

This GPT-4o powered Flask web app tells you how many calories there are in an image of a meal you upload

## Quick Start

1. Start the server:

```sh
$ python3 server.py
```

2. Go to http://localhost:5000


## Terminal usage

You can also use it from the terminal:

```sh
$ python3 calorie_counter.py IMAGE_FILE
```
59 changes: 59 additions & 0 deletions calorie_counter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from openai import OpenAI
from dotenv import load_dotenv
import base64
import json
import sys

load_dotenv()
client = OpenAI()

def get_calories_from_image(image_path):
with open(image_path, "rb") as image:
base64_image = base64.b64encode(image.read()).decode("utf-8")

response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"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:
{
"reasoning": "reasoning for the total calories",
"food_items": [
{
"name": "food item name",
"calories": "calories in the food item"
}
],
"total": "total calories in the meal"
}"""
},
{
"role": "user",
"content": [
{
"type": "text",
"text": "How many calories is in this meal?"
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
]
},
],
)

response_message = response.choices[0].message
content = response_message.content

return json.loads(content)

if __name__ == "__main__":
image_path = sys.argv[1]
calories = get_calories_from_image(image_path)
print(json.dumps(calories, indent=4))
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
openai
flask
python-dotenv
32 changes: 32 additions & 0 deletions server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from flask import Flask, render_template, request
import tempfile

from calorie_counter import get_calories_from_image

app = Flask(__name__)

@app.route("/")
def index():
return render_template("index.html")

@app.route("/upload", methods=["POST"])
def upload():
image = request.files["image"]

if image.filename == "":
return {
"error": "No image uploaded",
}, 400

temp_file = tempfile.NamedTemporaryFile()
image.save(temp_file.name)

calories = get_calories_from_image(temp_file.name)
temp_file.close()

return {
"calories": calories,
}

if __name__ == "__main__":
app.run(debug=True)
60 changes: 60 additions & 0 deletions static/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
font-family: Arial, sans-serif;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
justify-content: center;
background-color: white;
border-radius: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 200px;
height: 200px;
}
#calorie-count {
width: 60%;
height: 20px;
border: 1px solid #ccc;
border-radius: 5px;
text-align: center;
margin-top: 20px;
}
#upload {
border: none;
outline: none;
padding: 0;
cursor: pointer;
background: none;
}
#upload img {
width: 90px;
}

#spinner {
display: none;
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Binary file added static/images/camera.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions static/js/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const upload_button = document.querySelector("#upload");
const calorie_count = document.querySelector("#calorie-count");

upload_button.addEventListener("click", () => {
// ask to upload a file or take an image
const file_input = document.createElement("input");
file_input.type = "file";
file_input.accept = "image/*";
file_input.click();

file_input.addEventListener("change", () => {
loading();

const file = file_input.files[0];

const fd = new FormData();
fd.append("image", file);

fetch("/upload", {
method: "POST",
body: fd
}).then(response => response.json())
.then(data => {
stop_loading();
calorie_count.textContent = data.calories.total
});
});
});


function loading() {
document.querySelector("#upload").style.display = "none";
document.querySelector("#calorie-count").style.display = "none";
document.querySelector("#spinner").style.display = "block";
}

function stop_loading() {
document.querySelector("#spinner").style.display = "none";
document.querySelector("#upload").style.display = "block";
document.querySelector("#calorie-count").style.display = "block";
}
17 changes: 17 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Calorie Counter</title>
<link rel="stylesheet" href="/static/css/style.css" />
</head>
<body>
<div class="container">
<button id="upload"><img src="/static/images/camera.png" /></button>
<div id="calorie-count"></div>
<div id="spinner"></div>
</div>
<script src="/static/js/script.js"></script>
</body>
</html>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit e72eaba

Please sign in to comment.