forked from unconv/calorieapp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e72eaba
Showing
14 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
OPENAI_API_KEY=sk-1234567890abcdef1234567890abcdef |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.env | ||
__pycache__/ | ||
venv/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
openai | ||
flask | ||
python-dotenv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 added
BIN
+394 KB
...en salad with mixed greens, cherry tomatoes, sliced cucumbers, and a light vinaigret.webp
Binary file not shown.
Binary file added
BIN
+279 KB
...ate, as if taken with a smartphone. The lunch consists of a colorful salad with mixe.webp
Binary file not shown.
Binary file added
BIN
+298 KB
... with a glossy sesame seed bun, layers of fresh lettuce, sliced tomatoes, juicy beef.webp
Binary file not shown.
Binary file added
BIN
+308 KB
...y cheeseburger with melted cheddar oozing out, a large side of golden, crispy french.webp
Binary file not shown.