Untitled
unknown
plain_text
10 months ago
6.8 kB
18
Indexable
# File: nutrisnap/app.py
import os
from flask import Flask, render_template, request, jsonify
from dotenv import load_dotenv
from main import estimate_nutrition
load_dotenv()
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def upload():
if 'image' not in request.files:
return jsonify({'error': 'No image uploaded'}), 400
file = request.files['image']
if file.filename == '':
return jsonify({'error': 'No selected file'}), 400
image_path = 'temp_image.jpg'
file.save(image_path)
try:
result = estimate_nutrition(image_path)
os.remove(image_path)
return jsonify(result)
except Exception as e:
os.remove(image_path)
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(debug=True)
# File: nutrisnap/main.py
import os
import base64
import json
from openai import OpenAI
from PIL import Image, ImageOps
from io import BytesIO
MODEL = "gpt-4o"
def resize_image(image_path, target_width=1000):
with Image.open(image_path) as img:
img = ImageOps.exif_transpose(img)
original_width, original_height = img.size
target_height = int((target_width / original_width) * original_height)
resized_img = img.resize((target_width, target_height), Image.Resampling.LANCZOS)
return resized_img
def pil_image_to_base64(img):
buffered = BytesIO()
img.save(buffered, format="JPEG")
base64_str = base64.b64encode(buffered.getvalue()).decode('utf-8')
return base64_str
def estimate_nutrition(image_path):
resized_image = resize_image(image_path, 1000)
base64_image = pil_image_to_base64(resized_image)
system_prompt = "You are a nutrition expert. Analyze the meal photo and estimate calories and nutrients."
user_prompt = """
The photo shows a meal. Determine the items and return ONLY as JSON:
{
"items": [
{
"title": "item name",
"weight_grams": estimated weight,
"calories_per_100g": calories per 100g,
"proteins_per_100g": proteins per 100g,
"fats_per_100g": fats per 100g,
"carbs_per_100g": carbs per 100g,
"fiber_per_100g": fiber per 100g
}
],
"total_calories": total calories for the meal,
"suggestions": "Brief health tips or swaps"
}
"""
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
completion = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": [
{"type": "text", "text": user_prompt},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
]}
],
response_format={"type": "json_object"}
)
response_content = completion.choices[0].message.content
return json.loads(response_content)
# File: nutrisnap/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NutriSnap - AI Meal Analyzer</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div class="container">
<h1>NutriSnap</h1>
<p>Snap a photo of your meal to get instant nutrition info!</p>
<button id="upload-btn">Upload Meal Photo</button>
<div id="result" style="display: none;">
<h2>Analysis:</h2>
<pre id="output"></pre>
</div>
<div id="loading" style="display: none;">Analyzing... <div class="spinner"></div></div>
</div>
<script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>
# File: nutrisnap/static/style.css
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
#upload-btn {
background: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
font-size: 16px;
}
#upload-btn:hover {
background: #45a049;
}
#result {
margin-top: 20px;
}
pre {
text-align: left;
background: #eee;
padding: 10px;
border-radius: 4px;
}
#loading {
margin-top: 10px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
# File: nutrisnap/static/script.js
document.getElementById('upload-btn').addEventListener('click', function() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = function(event) {
const file = event.target.files[0];
if (file) {
const formData = new FormData();
formData.append('image', file);
document.getElementById('loading').style.display = 'block';
document.getElementById('upload-btn').style.display = 'none';
fetch('/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
document.getElementById('loading').style.display = 'none';
document.getElementById('result').style.display = 'block';
document.getElementById('output').textContent = JSON.stringify(data, null, 2);
document.getElementById('upload-btn').style.display = 'block';
})
.catch(error => {
console.error('Error:', error);
document.getElementById('loading').style.display = 'none';
alert('Error analyzing image');
document.getElementById('upload-btn').style.display = 'block';
});
}
};
input.click();
});
# File: nutrisnap/requirements.txt
flask==3.0.3
openai==1.40.0
pillow==10.4.0
python-dotenv==1.0.1
# File: nutrisnap/.env (create manually, add your API key)
OPENAI_API_KEY=your_api_key_here
Editor is loading...
Leave a Comment