KRISH09bha commited on
Commit
b46337a
·
verified ·
1 Parent(s): e3110b5

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +267 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,269 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
1
+ import cv2
 
 
2
  import streamlit as st
3
+ import numpy as np
4
+ import time
5
+ import random
6
+ import mediapipe as mp
7
+ import math
8
+
9
+ # Declare all global variables upfront
10
+ global last_spawn_time
11
+ last_spawn_time = 0 # Initialize the global variable before any other usage
12
+
13
+
14
+ @st.cache_data
15
+ def load_image(path):
16
+ img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
17
+ if img is None:
18
+ raise FileNotFoundError(f"Image not found or invalid at path: {path}")
19
+ return img # Keep in BGRA format
20
+
21
+
22
+ # Load images
23
+ apple_img = load_image("apple.png")
24
+ banana_img = load_image("banana.png")
25
+ watermelon_img = load_image("watermelon.png")
26
+ bomb_img = load_image("bomb.png")
27
+
28
+ # Define fruit properties
29
+ fruit_map = {
30
+ "apple": {"img": apple_img, "points": 100, "size": 40, "color": (0, 0, 255)},
31
+ "banana": {"img": banana_img, "points": 150, "size": 50, "color": (0, 255, 255)},
32
+ "watermelon": {"img": watermelon_img, "points": 200, "size": 70, "color": (0, 255, 0)},
33
+ }
34
+
35
+ # Constants and Variables
36
+ SLASH_LENGTH = 15
37
+ LIVES = 3
38
+ SPAWN_INTERVAL = 1.2
39
+
40
+ fruits, splashes, explosions = [], [], []
41
+ score, lives = 0, LIVES
42
+ last_slice_time = 0
43
+ slash_points = []
44
+ slash_color = (255, 255, 255)
45
+ combo_count, combo_multiplier = 0, 1
46
+ combo_time_limit = 2
47
+ combo_message = ""
48
+
49
+ mp_hands = mp.solutions.hands
50
+ hands = mp_hands.Hands(
51
+ static_image_mode=False,
52
+ max_num_hands=1,
53
+ model_complexity=1,
54
+ min_detection_confidence=0.5,
55
+ )
56
+
57
+
58
+ def overlay_image_alpha(background, overlay, pos, overlay_size):
59
+ x, y = pos
60
+ overlay = cv2.resize(overlay, overlay_size)
61
+
62
+ # Ensure the overlay image has an alpha channel
63
+ if overlay.shape[2] != 4:
64
+ raise ValueError(f"Overlay image must have 4 channels but has {overlay.shape[2]}!")
65
+
66
+ b, g, r, a = cv2.split(overlay)
67
+ overlay_rgb = cv2.merge((b, g, r))
68
+ mask = a / 255.0 # Normalize alpha channel
69
+
70
+ h, w = overlay.shape[:2]
71
+
72
+ # Validate dimensions to avoid out-of-bounds errors
73
+ if x < 0 or y < 0 or y + h > background.shape[0] or x + w > background.shape[1]:
74
+ # Do not overlay if it goes out of bounds
75
+ return
76
+
77
+ roi = background[y:y + h, x:x + w]
78
+ for c in range(3):
79
+ roi[:, :, c] = (1.0 - mask) * roi[:, :, c] + mask * overlay_rgb[:, :, c]
80
+
81
+ background[y:y + h, x:x + w] = roi
82
+
83
+
84
+ def distance(a, b):
85
+ return int(math.hypot(a[0] - b[0], a[1] - b[1]))
86
+
87
+
88
+ def spawn_fruit():
89
+ x = random.randint(50, 600)
90
+ is_bomb = random.random() < 0.2
91
+ fruit = {
92
+ "pos": [x, 600],
93
+ "vel": [random.randint(-4, 4), -random.randint(13, 18)],
94
+ "size": 40,
95
+ "bomb": is_bomb,
96
+ "img": bomb_img if is_bomb else None,
97
+ "points": 0,
98
+ "color": (255, 255, 255),
99
+ }
100
+ if not is_bomb:
101
+ kind = random.choice(list(fruit_map.keys()))
102
+ f = fruit_map[kind]
103
+ fruit.update({"img": f["img"], "points": f["points"], "size": f["size"], "color": f["color"], "kind": kind})
104
+ else:
105
+ fruit["kind"] = "bomb"
106
+ fruits.append(fruit)
107
+
108
+
109
+ def move_fruits():
110
+ global lives
111
+ for fruit in fruits[:]:
112
+ fruit["vel"][1] += 0.5
113
+ fruit["pos"][0] += fruit["vel"][0]
114
+ fruit["pos"][1] += fruit["vel"][1]
115
+ if fruit["pos"][1] > 700:
116
+ if not fruit["bomb"]:
117
+ lives -= 1
118
+ fruits.remove(fruit)
119
+
120
+
121
+ def add_splash(pos, color):
122
+ for _ in range(10):
123
+ splashes.append({
124
+ "pos": [pos[0] + random.randint(-10, 10), pos[1] + random.randint(-10, 10)],
125
+ "vel": [random.uniform(-3, 3), random.uniform(-3, 3)],
126
+ "radius": random.randint(3, 6),
127
+ "color": color,
128
+ "life": 15,
129
+ })
130
+
131
+
132
+ def add_explosion(pos):
133
+ for _ in range(30):
134
+ explosions.append({
135
+ "pos": [pos[0], pos[1]],
136
+ "vel": [random.uniform(-5, 5), random.uniform(-5, 5)],
137
+ "radius": random.randint(5, 10),
138
+ "color": (255, 255, 255),
139
+ "life": 20,
140
+ })
141
+
142
+
143
+ def update_effects(img):
144
+ for s in splashes[:]:
145
+ s["pos"][0] += s["vel"][0]
146
+ s["pos"][1] += s["vel"][1]
147
+ s["life"] -= 1
148
+ alpha = s["life"] / 15
149
+ cv2.circle(img, (int(s["pos"][0]), int(s["pos"][1])), s["radius"], tuple(int(c * alpha) for c in s["color"]),
150
+ -1)
151
+ if s["life"] <= 0:
152
+ splashes.remove(s)
153
+
154
+ for e in explosions[:]:
155
+ e["pos"][0] += e["vel"][0]
156
+ e["pos"][1] += e["vel"][1]
157
+ e["life"] -= 1
158
+ alpha = e["life"] / 20
159
+ cv2.circle(img, (int(e["pos"][0]), int(e["pos"][1])), e["radius"], tuple(int(c * alpha) for c in e["color"]),
160
+ -1)
161
+ if e["life"] <= 0:
162
+ explosions.remove(e)
163
+
164
+
165
+ def check_slices(index_pos):
166
+ global score, lives, slash_color, combo_count, combo_multiplier, combo_message, last_slice_time
167
+ combo_scored = False
168
+ for fruit in fruits[:]:
169
+ d = distance(index_pos, fruit["pos"])
170
+ if d < fruit["size"]:
171
+ if fruit["bomb"]:
172
+ lives -= 1
173
+ add_explosion(fruit["pos"])
174
+ fruits.clear()
175
+ splashes.clear()
176
+ return
177
+ else:
178
+ score += fruit["points"] * combo_multiplier
179
+ add_splash(fruit["pos"], fruit["color"])
180
+ fruits.remove(fruit)
181
+ combo_scored = True
182
+ if combo_scored:
183
+ if time.time() - last_slice_time <= combo_time_limit:
184
+ combo_count += 1
185
+ combo_multiplier = 1 + (combo_count // 2)
186
+ combo_message = f"{combo_multiplier}x Combo!"
187
+ else:
188
+ combo_count = 1
189
+ combo_multiplier = 1
190
+ combo_message = ""
191
+ last_slice_time = time.time()
192
+
193
+
194
+ def draw_fruits(img):
195
+ for fruit in fruits:
196
+ if fruit.get("img") is None or fruit.get("size") is None:
197
+ continue # Skip if image or size is invalid
198
+
199
+ x, y = int(fruit["pos"][0]), int(fruit["pos"][1])
200
+ size = fruit["size"] * 2
201
+ overlay_image_alpha(img, fruit["img"], (x - size // 2, y - size // 2), (size, size))
202
+
203
+
204
+ def draw_slash(img, index_pos):
205
+ global slash_points
206
+ slash_points.append(index_pos)
207
+ if len(slash_points) > SLASH_LENGTH:
208
+ slash_points.pop(0)
209
+ if len(slash_points) > 1:
210
+ pts = np.array(slash_points, np.int32).reshape((-1, 1, 2))
211
+ cv2.polylines(img, [pts], False, slash_color, 15)
212
+
213
+
214
+ def draw_ui(img, fps):
215
+ h, w = img.shape[:2]
216
+ cv2.putText(img, f"Score: {score}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2)
217
+ cv2.putText(img, f"Lives: {lives}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
218
+ cv2.putText(img, f"FPS: {int(fps)}", (w - 120, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
219
+ if combo_message:
220
+ cv2.putText(img, combo_message, (w // 3, 90), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3)
221
+
222
+
223
+ # Streamlit UI
224
+ st.title("Fruit Ninja in Streamlit 🍉")
225
+ run_game = st.button("Start Game")
226
+ frame_placeholder = st.empty()
227
+
228
+ if run_game:
229
+ cap = cv2.VideoCapture(0)
230
+ prev_time = time.time()
231
+ last_spawn_time = time.time() # Global variable used here
232
+
233
+ while cap.isOpened() and lives > 0:
234
+ ret, frame = cap.read()
235
+ if not ret:
236
+ break
237
+
238
+ frame = cv2.flip(frame, 1)
239
+ frame = cv2.convertScaleAbs(frame, alpha=1.2, beta=30) # Improve brightness and contrast
240
+
241
+ h, w, _ = frame.shape
242
+ img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
243
+ results = hands.process(img_rgb)
244
+
245
+ index_pos = None
246
+ if results.multi_hand_landmarks:
247
+ lm = results.multi_hand_landmarks[0].landmark[8]
248
+ index_pos = (int(lm.x * w), int(lm.y * h))
249
+ check_slices(index_pos)
250
+ draw_slash(frame, index_pos)
251
+
252
+ # Spawn fruit
253
+ if time.time() - last_spawn_time > SPAWN_INTERVAL:
254
+ spawn_fruit()
255
+ last_spawn_time = time.time()
256
+
257
+ move_fruits()
258
+ draw_fruits(frame)
259
+ update_effects(frame)
260
+
261
+ curr_time = time.time()
262
+ fps = 1 / (curr_time - prev_time)
263
+ prev_time = curr_time
264
+ draw_ui(frame, fps)
265
+
266
+ frame_placeholder.image(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), channels="RGB")
267
 
268
+ cap.release()
269
+ st.write("Game Over! Your score:", score)