Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -4,168 +4,96 @@ import os
|
|
4 |
import io
|
5 |
import base64
|
6 |
import boto3
|
7 |
-
import time
|
8 |
from PIL import Image
|
9 |
-
import
|
10 |
-
from firebase_admin import credentials, auth, firestore
|
11 |
|
12 |
-
#
|
13 |
-
|
14 |
-
|
15 |
-
AWS_ACCESS_KEY = os.getenv("AWS_ACCESS_KEY_ID")
|
16 |
-
AWS_SECRET_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
|
17 |
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
|
18 |
S3_BUCKET_NAME = os.getenv("S3_BUCKET_NAME", "food-image-crowdsourcing")
|
19 |
-
DYNAMODB_TABLE = os.getenv("DYNAMODB_TABLE", "image_metadata")
|
20 |
|
21 |
# Load Firebase credentials
|
22 |
-
FIREBASE_CONFIG = os.getenv("FIREBASE_CONFIG")
|
23 |
-
|
24 |
-
# β
Initialize Firebase Admin SDK
|
25 |
-
if FIREBASE_CONFIG:
|
26 |
-
try:
|
27 |
-
firebase_config_dict = json.loads(FIREBASE_CONFIG)
|
28 |
-
if not firebase_admin._apps:
|
29 |
-
cred = credentials.Certificate(firebase_config_dict)
|
30 |
-
firebase_admin.initialize_app(cred)
|
31 |
-
except Exception as e:
|
32 |
-
st.error(f"β οΈ Firebase initialization error: {e}")
|
33 |
-
else:
|
34 |
-
st.error("β οΈ Firebase configuration is missing! Please check HF Secrets.")
|
35 |
-
|
36 |
-
# β
Initialize AWS Services (S3 & DynamoDB)
|
37 |
-
try:
|
38 |
-
s3 = boto3.client(
|
39 |
-
"s3",
|
40 |
-
aws_access_key_id=AWS_ACCESS_KEY,
|
41 |
-
aws_secret_access_key=AWS_SECRET_KEY,
|
42 |
-
region_name=AWS_REGION
|
43 |
-
)
|
44 |
-
|
45 |
-
dynamodb = boto3.resource(
|
46 |
-
"dynamodb",
|
47 |
-
region_name=AWS_REGION,
|
48 |
-
aws_access_key_id=AWS_ACCESS_KEY,
|
49 |
-
aws_secret_access_key=AWS_SECRET_KEY,
|
50 |
-
)
|
51 |
-
metadata_table = dynamodb.Table(DYNAMODB_TABLE) # β
Corrected Table Name
|
52 |
-
except Exception as e:
|
53 |
-
st.error(f"β οΈ AWS initialization error: {e}")
|
54 |
-
|
55 |
-
# πΉ Initialize Session State for Tokens
|
56 |
-
if "tokens" not in st.session_state:
|
57 |
-
st.session_state.tokens = 0
|
58 |
-
if "user_email" not in st.session_state:
|
59 |
-
st.session_state.user_email = None
|
60 |
-
|
61 |
-
# πΉ UI - Page Title
|
62 |
-
st.title("π· Food Crowdsourcing Research App")
|
63 |
-
st.write("Help our research by uploading food images! Earn tokens for future app use.")
|
64 |
-
|
65 |
-
# β
**User Authentication Section**
|
66 |
-
st.sidebar.header("π User Authentication")
|
67 |
-
|
68 |
-
auth_choice = st.sidebar.radio("Select an option", ["Login", "Sign Up", "Logout"])
|
69 |
-
|
70 |
-
if auth_choice == "Sign Up":
|
71 |
-
with st.sidebar.form("signup_form"):
|
72 |
-
email = st.text_input("Email")
|
73 |
-
password = st.text_input("Password", type="password")
|
74 |
-
submit_button = st.form_submit_button("Sign Up")
|
75 |
-
|
76 |
-
if submit_button:
|
77 |
-
try:
|
78 |
-
user = auth.create_user(email=email, password=password)
|
79 |
-
st.success(f"β
Account created! Now login with {email}.")
|
80 |
-
except Exception as e:
|
81 |
-
st.error(f"β οΈ {str(e)}")
|
82 |
-
|
83 |
-
if auth_choice == "Login":
|
84 |
-
with st.sidebar.form("login_form"):
|
85 |
-
email = st.text_input("Email")
|
86 |
-
password = st.text_input("Password", type="password")
|
87 |
-
submit_button = st.form_submit_button("Login")
|
88 |
-
|
89 |
-
if submit_button:
|
90 |
-
try:
|
91 |
-
user = auth.get_user_by_email(email)
|
92 |
-
st.session_state.user_email = email
|
93 |
-
st.success(f"β
Welcome back, {email}!")
|
94 |
-
except Exception as e:
|
95 |
-
st.error(f"β οΈ {str(e)}")
|
96 |
-
|
97 |
-
if auth_choice == "Logout":
|
98 |
-
if st.session_state.user_email:
|
99 |
-
st.session_state.user_email = None
|
100 |
-
st.success("β
Logged out successfully!")
|
101 |
|
102 |
-
#
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
# β
**Image Upload Section**
|
108 |
-
uploaded_file = st.file_uploader("πΈ Upload a food image", type=["jpg", "png", "jpeg"])
|
109 |
-
|
110 |
-
# β
**Terms & Conditions**
|
111 |
-
st.markdown("### **Terms & Conditions**")
|
112 |
-
st.write(
|
113 |
-
"By uploading an image, you agree to transfer full copyright to the research team for AI training purposes. "
|
114 |
-
"You are responsible for ensuring you own the image and it does not violate any copyright laws. "
|
115 |
-
"We do not guarantee when tokens will be redeemable. Keep track of your user ID."
|
116 |
)
|
117 |
-
st.checkbox("I agree to the terms and conditions", key="terms_accepted")
|
118 |
-
|
119 |
-
# β
**Process and Upload Image**
|
120 |
-
if uploaded_file and st.session_state.terms_accepted:
|
121 |
-
image = Image.open(uploaded_file)
|
122 |
-
st.image(image, caption="Uploaded Image", use_column_width=True)
|
123 |
-
|
124 |
-
# Show Original Size
|
125 |
-
st.write(f"π Original size: {image.size}")
|
126 |
-
|
127 |
-
# Convert PNG to JPEG if needed
|
128 |
-
if image.format == "PNG":
|
129 |
-
image = image.convert("RGB")
|
130 |
-
|
131 |
-
# Resize image to max 1024x1024 px
|
132 |
-
MAX_SIZE = (1024, 1024)
|
133 |
-
image.thumbnail(MAX_SIZE)
|
134 |
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
Bucket=S3_BUCKET_NAME,
|
147 |
-
Key=file_name,
|
148 |
-
Body=img_bytes.getvalue(),
|
149 |
-
ContentType="image/jpeg",
|
150 |
-
)
|
151 |
-
st.success("β
Image uploaded successfully to S3!")
|
152 |
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
)
|
161 |
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
4 |
import io
|
5 |
import base64
|
6 |
import boto3
|
|
|
7 |
from PIL import Image
|
8 |
+
from streamlit_tags import st_tags
|
|
|
9 |
|
10 |
+
# Load AWS credentials using correct HF Secrets
|
11 |
+
AWS_ACCESS_KEY = os.getenv("AWS_ACCESS_KEY")
|
12 |
+
AWS_SECRET_KEY = os.getenv("AWS_SECRET_KEY")
|
|
|
|
|
13 |
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
|
14 |
S3_BUCKET_NAME = os.getenv("S3_BUCKET_NAME", "food-image-crowdsourcing")
|
15 |
+
DYNAMODB_TABLE = os.getenv("DYNAMODB_TABLE", "image_metadata")
|
16 |
|
17 |
# Load Firebase credentials
|
18 |
+
FIREBASE_CONFIG = json.loads(os.getenv("FIREBASE_CONFIG", "{}"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
# Initialize AWS Services (S3 & DynamoDB)
|
21 |
+
s3 = boto3.client(
|
22 |
+
"s3",
|
23 |
+
aws_access_key_id=AWS_ACCESS_KEY,
|
24 |
+
aws_secret_access_key=AWS_SECRET_KEY,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
+
dynamodb = boto3.resource(
|
28 |
+
"dynamodb",
|
29 |
+
region_name=AWS_REGION,
|
30 |
+
aws_access_key_id=AWS_ACCESS_KEY,
|
31 |
+
aws_secret_access_key=AWS_SECRET_KEY,
|
32 |
+
)
|
33 |
+
metadata_table = dynamodb.Table(DYNAMODB_TABLE)
|
34 |
|
35 |
+
# Streamlit Layout - Three Panel Design
|
36 |
+
st.title("π½οΈ Food Image Review & Annotation")
|
37 |
+
col1, col2, col3 = st.columns([1, 1, 1]) # Equal size columns
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
+
# Upload Image
|
40 |
+
uploaded_file = st.file_uploader("Upload an image of your food", type=["jpg", "png", "jpeg"])
|
41 |
+
if uploaded_file:
|
42 |
+
image = Image.open(uploaded_file)
|
43 |
+
st.session_state["original_image"] = image
|
44 |
+
|
45 |
+
# Resize image in Streamlit before sending to S3
|
46 |
+
def resize_image(image):
|
47 |
+
aspect_ratio = image.width / image.height
|
48 |
+
new_width = 512
|
49 |
+
new_height = int(new_width / aspect_ratio)
|
50 |
+
resized_image = image.resize((new_width, new_height))
|
51 |
+
return resized_image
|
52 |
+
|
53 |
+
if "original_image" in st.session_state:
|
54 |
+
original_img = st.session_state["original_image"]
|
55 |
+
processed_img = resize_image(original_img)
|
56 |
+
|
57 |
+
# πΌοΈ Panel 1: Original Image
|
58 |
+
with col1:
|
59 |
+
st.subheader("π· Original Image")
|
60 |
+
st.image(original_img, caption="Uploaded", use_container_width=True)
|
61 |
+
|
62 |
+
# πΌοΈ Panel 2: Resized Image (512x512 Maintaining Aspect Ratio)
|
63 |
+
with col2:
|
64 |
+
st.subheader("πΌοΈ Processed Image")
|
65 |
+
st.image(processed_img, caption="512x512 Maintained Aspect", use_container_width=True)
|
66 |
+
|
67 |
+
# βοΈ Panel 3: Food Annotations with Intellisense
|
68 |
+
with col3:
|
69 |
+
st.subheader("π Add Annotations")
|
70 |
+
|
71 |
+
# Expanded food suggestions
|
72 |
+
suggested_foods = [
|
73 |
+
"Pizza", "Pasta", "Tacos", "Sushi", "Ramen", "Kimchi", "Bratwurst", "Jambalaya",
|
74 |
+
"Chicken", "Rice", "Steak", "Bread", "Cheese", "Salmon", "Avocado", "Eggs",
|
75 |
+
"Carrots", "Tomatoes", "Cucumber", "Yogurt", "Peanuts", "Lettuce", "Pierogies", "Mongolian Beef", "Spaghetti Bolognese",
|
76 |
+
"Bigos", "Stuffed Cabbage", "Zurek", "Schnitzel", "Tomato Soup", "Potato Pancakes", "Blintzes", "Broccoli Chicken"
|
77 |
+
]
|
78 |
+
|
79 |
+
# User selects food items from suggestions
|
80 |
+
food_items = st_tags(
|
81 |
+
label="Enter food items",
|
82 |
+
text="Add items...",
|
83 |
+
value=[],
|
84 |
+
suggestions=suggested_foods,
|
85 |
+
maxtags=10,
|
86 |
)
|
87 |
|
88 |
+
if st.button("Save Annotations"):
|
89 |
+
metadata_table.update_item(
|
90 |
+
Key={"image_id": uploaded_file.name},
|
91 |
+
UpdateExpression="SET annotations = :a, processing_status = :p, s3_url = :s, tokens_earned = :t",
|
92 |
+
ExpressionAttributeValues={
|
93 |
+
":a": food_items,
|
94 |
+
":p": "processed",
|
95 |
+
":s": f"s3://{S3_BUCKET_NAME}/raw-uploads/{uploaded_file.name}",
|
96 |
+
":t": 1 if food_items else 0
|
97 |
+
},
|
98 |
+
)
|
99 |
+
st.success("β
Annotations saved successfully!")
|