Upload 3 files
Browse files- app.py +31 -40
- kaloriedata.csv +5 -10
- requirements.txt +2 -3
app.py
CHANGED
|
@@ -1,54 +1,45 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
from PIL import Image
|
| 3 |
-
import
|
| 4 |
-
from transformers import AutoFeatureExtractor, AutoModelForImageClassification
|
| 5 |
import pandas as pd
|
| 6 |
-
from utils.matcher import fuzzy_match
|
| 7 |
|
| 8 |
-
|
| 9 |
-
def load_model():
|
| 10 |
-
extractor = AutoFeatureExtractor.from_pretrained("gabrielganan/efficientnet_b1-food101")
|
| 11 |
-
model = AutoModelForImageClassification.from_pretrained("gabrielganan/efficientnet_b1-food101")
|
| 12 |
-
return extractor, model
|
| 13 |
-
|
| 14 |
-
# Load model and data
|
| 15 |
-
extractor, model = load_model()
|
| 16 |
df = pd.read_csv("kaloriedata.csv")
|
| 17 |
food_list = df["navn"].tolist()
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
image = Image.open(uploaded_file).convert("RGB")
|
| 24 |
-
st.image(image, caption="Uploadet billede", use_column_width=True)
|
| 25 |
-
|
| 26 |
-
with st.spinner("Analyserer billede..."):
|
| 27 |
-
inputs = extractor(images=image, return_tensors="pt")
|
| 28 |
-
with torch.no_grad():
|
| 29 |
-
outputs = model(**inputs)
|
| 30 |
-
logits = outputs.logits
|
| 31 |
-
probs = torch.nn.functional.softmax(logits, dim=1)[0]
|
| 32 |
-
confidence, idx = torch.max(probs, dim=0)
|
| 33 |
-
label = model.config.id2label[idx.item()]
|
| 34 |
-
conf_score = confidence.item()
|
| 35 |
-
|
| 36 |
-
st.markdown(f"**Modelgæt:** `{label}` med **{conf_score*100:.1f}%** sikkerhed")
|
| 37 |
|
| 38 |
-
|
| 39 |
-
selected = st.selectbox("Model usikker – vælg fødevare manuelt:", food_list)
|
| 40 |
-
else:
|
| 41 |
-
selected = fuzzy_match(label, food_list)
|
| 42 |
|
| 43 |
-
|
| 44 |
-
kcal_per_100g = float(df.loc[df["navn"] == selected, "kcal_pr_100g"].iloc[0])
|
| 45 |
-
total_kcal = gram * kcal_per_100g / 100
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
st.subheader("🔍 Analyse af måltid")
|
| 48 |
-
st.write(f"- {gram} g {
|
| 49 |
-
|
| 50 |
-
feedback = st.text_input("Feedback eller
|
| 51 |
if st.button("Send feedback"):
|
| 52 |
with open("feedback_log.csv", "a") as f:
|
| 53 |
-
f.write(f"{label},{
|
| 54 |
st.success("Tak for din feedback!")
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
from PIL import Image
|
| 3 |
+
from transformers import pipeline
|
|
|
|
| 4 |
import pandas as pd
|
|
|
|
| 5 |
|
| 6 |
+
# Load calorie data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
df = pd.read_csv("kaloriedata.csv")
|
| 8 |
food_list = df["navn"].tolist()
|
| 9 |
|
| 10 |
+
# Load zero-shot pipeline for image classification
|
| 11 |
+
@st.cache_resource
|
| 12 |
+
def get_classifier():
|
| 13 |
+
return pipeline("zero-shot-image-classification", model="openai/clip-vit-base-patch32")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
+
classifier = get_classifier()
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
st.title("🍽️ WebKalorier – Kalorieestimering via CLIP")
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
uploaded = st.file_uploader("Upload billede af mad", type=["jpg", "jpeg", "png"])
|
| 20 |
+
if uploaded:
|
| 21 |
+
image = Image.open(uploaded).convert("RGB")
|
| 22 |
+
st.image(image, caption="Uploadet billede", use_column_width=True)
|
| 23 |
+
with st.spinner("Analyserer..."):
|
| 24 |
+
outputs = classifier(image, candidate_labels=food_list)
|
| 25 |
+
# outputs: list of dicts with labels and scores
|
| 26 |
+
best = outputs[0]
|
| 27 |
+
label = best['labels'][0]
|
| 28 |
+
score = best['scores'][0]
|
| 29 |
+
st.markdown(f"**Modelgæt:** {label} ({score:.1%} sikkerhed)")
|
| 30 |
+
# fallback
|
| 31 |
+
if score < 0.7:
|
| 32 |
+
label = st.selectbox("Modellen er usikker – vælg manuelt:", food_list, index=food_list.index(label))
|
| 33 |
+
# grams input
|
| 34 |
+
gram = st.number_input(f"Angiv mængde af {label} i gram:", 1, 2000, 100)
|
| 35 |
+
# lookup calories
|
| 36 |
+
kcal_per_100g = df.loc[df["navn"] == label, "kcal_pr_100g"].iloc[0]
|
| 37 |
+
kcal = gram * kcal_per_100g / 100
|
| 38 |
st.subheader("🔍 Analyse af måltid")
|
| 39 |
+
st.write(f"- **{gram} g {label}** → **{kcal:.1f} kcal**")
|
| 40 |
+
# feedback
|
| 41 |
+
feedback = st.text_input("Feedback eller korrektion (valgfrit)")
|
| 42 |
if st.button("Send feedback"):
|
| 43 |
with open("feedback_log.csv", "a") as f:
|
| 44 |
+
f.write(f"{label},{score:.2f},{feedback}\n")
|
| 45 |
st.success("Tak for din feedback!")
|
kaloriedata.csv
CHANGED
|
@@ -1,16 +1,11 @@
|
|
| 1 |
navn,kcal_pr_100g
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
salat,15
|
| 3 |
-
tomat,18
|
| 4 |
ris,130
|
| 5 |
pasta,131
|
| 6 |
kylling,239
|
| 7 |
-
|
| 8 |
-
smør,717
|
| 9 |
-
kartoffel,77
|
| 10 |
-
broccoli,35
|
| 11 |
-
laks,210
|
| 12 |
oksekød,250
|
| 13 |
-
gulerod,41
|
| 14 |
-
pizza,270
|
| 15 |
-
burger,280
|
| 16 |
-
brød,260
|
|
|
|
| 1 |
navn,kcal_pr_100g
|
| 2 |
+
æg,155
|
| 3 |
+
kartofler,77
|
| 4 |
+
smør,717
|
| 5 |
+
broccoli,35
|
| 6 |
salat,15
|
|
|
|
| 7 |
ris,130
|
| 8 |
pasta,131
|
| 9 |
kylling,239
|
| 10 |
+
fisk,206
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
oksekød,250
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
streamlit
|
| 2 |
-
torch
|
| 3 |
transformers
|
| 4 |
-
|
|
|
|
| 5 |
pandas
|
| 6 |
-
rapidfuzz
|
|
|
|
| 1 |
streamlit
|
|
|
|
| 2 |
transformers
|
| 3 |
+
torch
|
| 4 |
+
Pillow
|
| 5 |
pandas
|
|
|