diff --git a/result.py b/analytics/generate_academic_latency_report.py similarity index 66% rename from result.py rename to analytics/generate_academic_latency_report.py index 5341759..6ddcd49 100644 --- a/result.py +++ b/analytics/generate_academic_latency_report.py @@ -2,60 +2,51 @@ import matplotlib.pyplot as plt import os -# --- 1. Setup --- -csv_path = os.path.join("logs", "test_results.csv") +# --- 1. Dynamic Path Setup --- +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT_DIR = os.path.dirname(SCRIPT_DIR) + +csv_path = os.path.join(ROOT_DIR, "logs", "latency_results.csv") +output_dir = os.path.join(ROOT_DIR, "stats", "Objective 1 Performance") + +if not os.path.exists(output_dir): + os.makedirs(output_dir) def generate_consolidated_objective_1_report(): - """ - Generates a single comprehensive graph validating Objective 1: - - Latency < 100ms - - Frame Rate >= 30 FPS - - Markerless/Standard Hardware efficiency - """ if not os.path.exists(csv_path): print(f"Error: {csv_path} not found. Run the game to generate logs first!") return df = pd.read_csv(csv_path) - - # Derive FPS from recorded processing time to prove 'Measurable' criteria df['RealTime_FPS'] = 1 / df['Proc_Time'] - # Create figure with twin axes for a single-report focus fig, ax1 = plt.subplots(figsize=(12, 8)) - # --- PRIMARY AXIS: Latency (Seconds) --- - color_lat = '#1f77b4' # Tech Blue + color_lat = '#1f77b4' ax1.set_xlabel('Punch Sample Sequence (Time)', fontsize=12) ax1.set_ylabel('Processing Latency (Seconds)', color=color_lat, fontsize=12, fontweight='bold') ax1.plot(df.index, df['Proc_Time'], color=color_lat, linewidth=2.5, label='Measured Latency') - # CRITICAL: Target Threshold Line (100ms) per Objective 1 ax1.axhline(y=0.1, color='#d62728', linestyle='--', linewidth=2, label='Max Target (100ms)') ax1.tick_params(axis='y', labelcolor=color_lat) - ax1.set_ylim(0, 0.15) # Focused view around the 100ms threshold + ax1.set_ylim(0, 0.15) ax1.grid(True, linestyle=':', alpha=0.5) - # --- SECONDARY AXIS: Frame Rate (FPS) --- ax2 = ax1.twinx() - color_fps = '#2ca02c' # Success Green + color_fps = '#2ca02c' ax2.set_ylabel('Frame Rate (FPS)', color=color_fps, fontsize=12, fontweight='bold') ax2.plot(df.index, df['RealTime_FPS'], color=color_fps, linestyle='-', alpha=0.4, label='Real-time FPS') - # Target 30 FPS Line per SMART Criteria ax2.axhline(y=30, color='#1b5e20', linestyle=':', linewidth=2, label='Target 30 FPS') ax2.tick_params(axis='y', labelcolor=color_fps) ax2.set_ylim(0, 60) - # --- Annotations & Styling --- plt.title('Objective 1 Validation: Vision Pipeline Efficiency\n(Python/OpenCV Markerless Tracking)', pad=20, fontsize=14) - # Combined Legend lines1, labels1 = ax1.get_legend_handles_labels() lines2, labels2 = ax2.get_legend_handles_labels() ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left', frameon=True, shadow=True) - # Objective Summary Text Box avg_lat = df['Proc_Time'].mean() * 1000 avg_fps = df['RealTime_FPS'].mean() summary_text = f"Avg Latency: {avg_lat:.1f}ms\nAvg FPS: {avg_fps:.1f}" @@ -63,9 +54,10 @@ def generate_consolidated_objective_1_report(): bbox=dict(facecolor='white', alpha=0.8), ha='right', fontsize=10) fig.tight_layout() - plt.savefig('objective_1_performance_validation.png') - print("Success: objective_1_performance_validation.png generated.") + output_file = os.path.join(output_dir, 'academic_latency_graph.png') + plt.savefig(output_file) + print(f"Success: Academic graph saved securely to {output_file}") if __name__ == "__main__": - print("--- Generating Objective 1 Audit ---") + print("--- Generating Academic Objective 1 Audit ---") generate_consolidated_objective_1_report() \ No newline at end of file diff --git a/analytics/generate_color_mapping.py b/analytics/generate_color_mapping.py new file mode 100644 index 0000000..81db927 --- /dev/null +++ b/analytics/generate_color_mapping.py @@ -0,0 +1,153 @@ +import cv2 as cv +import numpy as np +import os +import pandas as pd +import matplotlib.pyplot as plt + +# ========================= +# OUTPUT FOLDER +# ========================= +base_folder = "stats" +output_folder = os.path.join(base_folder, "Pseudocolor Mapping Analysis") + +if not os.path.exists(output_folder): + os.makedirs(output_folder) + +# ========================= +# MAIN FUNCTION +# ========================= +def analyze_pseudocolor_mapping(video_source=0): + + cap = cv.VideoCapture(video_source) + + if not cap.isOpened(): + print("Error: Cannot open video source.") + return + + prev_gray = None + results = [] + + frame_count = 0 + max_frames = 300 # limit for testing + + while frame_count < max_frames: + ret, frame = cap.read() + if not ret: + break + + # ========================= + # PREPROCESSING + # ========================= + gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) + gray = cv.GaussianBlur(gray, (5, 5), 0) + + # ========================= + # MOTION DETECTION + # ========================= + if prev_gray is None: + prev_gray = gray + continue + + diff = cv.absdiff(prev_gray, gray) + _, mask = cv.threshold(diff, 25, 255, cv.THRESH_BINARY) + + # Clean noise + kernel = np.ones((3,3), np.uint8) + mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel) + + prev_gray = gray + + # ========================= + # PSEUDOCOLOR (HEATMAP) + # ========================= + heatmap = cv.applyColorMap(mask, cv.COLORMAP_JET) + + # ========================= + # ANALYSIS + # ========================= + motion = cv.countNonZero(mask) + + hsv = cv.cvtColor(heatmap, cv.COLOR_BGR2HSV) + avg_hue = hsv[:, :, 0].mean() + + results.append((motion, avg_hue)) + + frame_count += 1 + + # Optional display + cv.imshow("Mask", mask) + cv.imshow("Heatmap", heatmap) + + if cv.waitKey(1) & 0xFF == 27: + break + + cap.release() + cv.destroyAllWindows() + + # ========================= + # SAVE RESULTS + # ========================= + df = pd.DataFrame(results, columns=["Motion", "Hue"]) + + csv_path = os.path.join(output_folder, "mapping_results.csv") + df.to_csv(csv_path, index=False) + + # ========================= + # SIMPLE SUMMARY + # ========================= + corr = df.corr().iloc[0,1] + + summary_path = os.path.join(output_folder, "mapping_summary.txt") + with open(summary_path, "w") as f: + f.write("PSEUDOCOLOR MAPPING ANALYSIS\n\n") + f.write(f"Total Frames: {len(df)}\n") + f.write(f"Correlation (Motion vs Hue): {corr:.4f}\n\n") + + if corr < -0.3: + f.write("Strong inverse relationship (Correct mapping)\n") + elif corr < -0.1: + f.write("Moderate relationship\n") + else: + f.write("Weak relationship (Needs improvement)\n") + + print("✅ Analysis completed") + print(f"CSV saved at: {csv_path}") + print(f"Summary saved at: {summary_path}") + + # ========================= + # GENERATE SCATTER PLOT + # ========================= + # Optional: remove zero motion for clearer visualization + df_plot = df[df["Motion"] > 0] + + plt.figure(figsize=(10, 6)) + + # Scatter plot + plt.scatter(df_plot["Motion"], df_plot["Hue"], alpha=0.6) + + # Trend line + if len(df_plot) > 1: + z = np.polyfit(df_plot["Motion"], df_plot["Hue"], 1) + p = np.poly1d(z) + plt.plot(df_plot["Motion"], p(df_plot["Motion"])) + + # Labels + plt.xlabel("Motion Intensity") + plt.ylabel("Hue Value") + plt.title("Pseudocolor Mapping: Motion vs Hue") + plt.grid() + + # Save chart + chart_path = os.path.join(output_folder, "mapping_scatter.png") + plt.savefig(chart_path) + + plt.close() + + print(f"✅ Chart saved at: {chart_path}") + + +# ========================= +# RUN +# ========================= +if __name__ == "__main__": + analyze_pseudocolor_mapping() \ No newline at end of file diff --git a/analytics/generate_latency_report.py b/analytics/generate_latency_report.py new file mode 100644 index 0000000..51cf666 --- /dev/null +++ b/analytics/generate_latency_report.py @@ -0,0 +1,111 @@ +import pandas as pd +import matplotlib.pyplot as plt +import os +import numpy as np + +# ========================= +# PATH SETUP (FIXED) +# ========================= +csv_path = os.path.join("logs", "test_results.csv") + +base_folder = "stats" +stats_folder = os.path.join(base_folder, "Real-Time Pipeline Performance") + +if not os.path.exists(stats_folder): + os.makedirs(stats_folder) + +# ========================= +# MAIN FUNCTION +# ========================= +def generate_consolidated_objective_1_report(): + + if not os.path.exists(csv_path): + print(f"Error: {csv_path} not found.") + return + + df = pd.read_csv(csv_path) + + # === Convert === + df['Latency_ms'] = df['Proc_Time'] * 1000 + df['FPS'] = 1 / df['Proc_Time'] + + # === Statistics === + mean = df['Latency_ms'].mean() + std = df['Latency_ms'].std() + p99 = np.percentile(df['Latency_ms'], 99) + + print("\n=== REAL-TIME PIPELINE PERFORMANCE ===") + print(f"Mean Latency: {mean:.2f} ms") + print(f"Std Dev: {std:.2f}") + print(f"99th Percentile: {p99:.2f} ms") + + # ========================= + # SAVE STATS + # ========================= + stats_file = os.path.join(stats_folder, "latency_statistics.txt") + + with open(stats_file, "w", encoding="utf-8") as f: + f.write("REAL-TIME PIPELINE PERFORMANCE\n\n") + f.write(f"Mean Latency: {mean:.2f} ms\n") + f.write(f"Standard Deviation: {std:.2f}\n") + f.write(f"99th Percentile: {p99:.2f} ms\n") + + # ========================= + # PLOT GRAPH + # ========================= + fig, ax1 = plt.subplots(figsize=(12, 8)) + + # Latency + ax1.plot(df.index, df['Latency_ms'], linewidth=2, label='Latency (ms)') + ax1.set_xlabel("Frame Number") + ax1.set_ylabel("Latency (ms)") + + # Thresholds + ax1.axhline(33.3, linestyle='--', label='30 FPS Threshold (33.3 ms)') + ax1.axhline(100, linestyle='--', label='Maximum Limit (100 ms)') + + ax1.set_ylim(0, max(df['Latency_ms']) * 1.2) + + # FPS axis + ax2 = ax1.twinx() + ax2.plot(df.index, df['FPS'], alpha=0.3, label='FPS') + ax2.axhline(30, linestyle=':', label='30 FPS Target') + ax2.set_ylabel("FPS") + + # Legend + lines1, labels1 = ax1.get_legend_handles_labels() + lines2, labels2 = ax2.get_legend_handles_labels() + ax1.legend(lines1 + lines2, labels1 + labels2) + + # Summary box + summary = ( + f"Mean: {mean:.2f} ms\n" + f"Std: {std:.2f}\n" + f"99%: {p99:.2f} ms" + ) + + plt.gca().text(0.98, 0.02, summary, + transform=ax1.transAxes, + bbox=dict(facecolor='white', alpha=0.8), + ha='right') + + plt.title("Real-Time Pipeline Performance") + plt.tight_layout() + + # ========================= + # SAVE GRAPH + # ========================= + output_path = os.path.join(stats_folder, "latency_performance_graph.png") + plt.savefig(output_path) + + plt.show() + + print(f"✅ Graph saved at: {output_path}") + print(f"✅ Stats saved at: {stats_file}") + + +# ========================= +# RUN +# ========================= +if __name__ == "__main__": + generate_consolidated_objective_1_report() \ No newline at end of file diff --git a/analytics/generate_lut_performance.py b/analytics/generate_lut_performance.py new file mode 100644 index 0000000..ad34f65 --- /dev/null +++ b/analytics/generate_lut_performance.py @@ -0,0 +1,106 @@ +import cv2 as cv +import numpy as np +import time +import os +import matplotlib.pyplot as plt + +# ========================= +# CREATE MAIN STATS FOLDER +# ========================= +base_folder = "stats" +analysis_folder = os.path.join(base_folder, "Pseudocolour Transformation Performance Analysis") + +if not os.path.exists(analysis_folder): + os.makedirs(analysis_folder) + +# ========================= +# PART 1 — LUT DETERMINISTIC VALIDATION +# ========================= + +zero_matrix = np.zeros((10, 10), dtype=np.uint8) +full_matrix = np.full((10, 10), 255, dtype=np.uint8) + +zero_color = cv.applyColorMap(zero_matrix, cv.COLORMAP_JET) +full_color = cv.applyColorMap(full_matrix, cv.COLORMAP_JET) + +zero_bgr = zero_color[0][0].tolist() +full_bgr = full_color[0][0].tolist() + +# Save validation results +validation_path = os.path.join(analysis_folder, "pseudocolour_validation.txt") + +with open(validation_path, "w", encoding="utf-8") as f: + f.write("=== PSEUDOCOLOUR LUT VALIDATION ===\n\n") + f.write(f"Input (0) -> Output BGR: {zero_bgr}\n") + f.write(f"Input (255) -> Output BGR: {full_bgr}\n") + +print(f"✅ LUT validation saved at: {validation_path}") + +# ========================= +# PART 2 — PERFORMANCE TIMING +# ========================= + +runs = 1000 + +# Motion extraction +start = time.perf_counter() +for _ in range(runs): + a = np.random.randint(0, 256, (480, 640), dtype=np.uint8) + b = np.random.randint(0, 256, (480, 640), dtype=np.uint8) + cv.absdiff(a, b) +end = time.perf_counter() + +absdiff_time = (end - start) / runs * 1000 + +# Pseudocolour +start = time.perf_counter() +for _ in range(runs): + img = np.random.randint(0, 256, (480, 640), dtype=np.uint8) + cv.applyColorMap(img, cv.COLORMAP_JET) +end = time.perf_counter() + +colormap_time = (end - start) / runs * 1000 + +print("\n=== PERFORMANCE RESULTS ===") +print(f"Motion Extraction (absdiff): {absdiff_time:.3f} ms") +print(f"Pseudocolour (applyColorMap): {colormap_time:.3f} ms") + +# Save performance stats +performance_path = os.path.join(analysis_folder, "pseudocolour_performance.txt") + +with open(performance_path, "w", encoding="utf-8") as f: + f.write("=== PERFORMANCE COMPARISON ===\n\n") + f.write(f"Motion Extraction (absdiff): {absdiff_time:.3f} ms\n") + f.write(f"Pseudocolour (applyColorMap): {colormap_time:.3f} ms\n") + +print(f"✅ Performance stats saved at: {performance_path}") + +# ========================= +# PART 3 — BAR CHART +# ========================= + +labels = [ + "Motion Extraction\n(cv.absdiff)", + "Pseudocolour\n(cv.applyColorMap)" +] + +values = [absdiff_time, colormap_time] + +plt.figure(figsize=(7, 5)) +plt.bar(labels, values) + +plt.ylabel("Execution Time (ms)") +plt.title("Bar Chart: Processing Time Comparison") + +# Threshold line +plt.axhline(33.3, linestyle='--') +plt.text(0, 34, "30 FPS Threshold (33.3 ms)") + +plt.tight_layout() + +# Save chart +chart_path = os.path.join(analysis_folder, "pseudocolour_bar_chart.png") +plt.savefig(chart_path) +plt.show() + +print(f"✅ Bar chart saved at: {chart_path}") \ No newline at end of file diff --git a/assets/sfx/deflector.mp3 b/assets/sfx/deflector.mp3 new file mode 100644 index 0000000..313c237 Binary files /dev/null and b/assets/sfx/deflector.mp3 differ diff --git a/assets/sfx/deflector_boss.mp3 b/assets/sfx/deflector_boss.mp3 new file mode 100644 index 0000000..037e706 Binary files /dev/null and b/assets/sfx/deflector_boss.mp3 differ diff --git a/assets/sfx/deflector_explode.mp3 b/assets/sfx/deflector_explode.mp3 new file mode 100644 index 0000000..4f82ed2 Binary files /dev/null and b/assets/sfx/deflector_explode.mp3 differ diff --git a/assets/sfx/freeze.mp3 b/assets/sfx/freeze.mp3 new file mode 100644 index 0000000..f98001f Binary files /dev/null and b/assets/sfx/freeze.mp3 differ diff --git a/assets/sfx/laser.mp3 b/assets/sfx/laser.mp3 new file mode 100644 index 0000000..c8cdf6e Binary files /dev/null and b/assets/sfx/laser.mp3 differ diff --git a/assets/sfx/laser_boss.mp3 b/assets/sfx/laser_boss.mp3 new file mode 100644 index 0000000..95297e9 Binary files /dev/null and b/assets/sfx/laser_boss.mp3 differ diff --git a/assets/sfx/scanner_boss.mp3 b/assets/sfx/scanner_boss.mp3 new file mode 100644 index 0000000..1b528ca Binary files /dev/null and b/assets/sfx/scanner_boss.mp3 differ diff --git a/config.py b/config.py index 091f0ea..ef5e47a 100644 --- a/config.py +++ b/config.py @@ -26,7 +26,7 @@ #== BOSS SETTINGS ==# BASE_BOSS_HEALTH = 300 LASER_BOSS_HEALTH = 300 # Can directly overwrite specific boss health, easier to tweak individual bosses for balancing -SCANNER_BOSS_HEALTH = 400 +SCANNER_BOSS_HEALTH = 300 DEFLECTOR_BOSS_HEALTH = 500 LASER_BOSS_DAMAGE = 15 SCANNER_BOSS_DAMAGE = 20 diff --git a/debugger.py b/debugger.py index bfc85b8..2c608c8 100644 --- a/debugger.py +++ b/debugger.py @@ -106,4 +106,20 @@ def draw_thermal_debug(self, main_frame, visual_motion): # Add Legend Labels cv.putText(main_frame, "HIGH MOTION", (35, leg_y + 10), cv.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) - cv.putText(main_frame, "STILL", (35, leg_y + 95), cv.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) \ No newline at end of file + cv.putText(main_frame, "STILL", (35, leg_y + 95), cv.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) + + def log_latency(self, frame_count, proc_time): + """Saves frame latency data for Objective 1 analysis""" + if not config.DEBUG_MODE: return + + latency_file = os.path.join(self.log_dir, "latency_results.csv") + + # Write header if file doesn't exist + if not os.path.exists(latency_file): + with open(latency_file, mode='w', newline='') as file: + writer = csv.writer(file) + writer.writerow(["Frame", "Proc_Time"]) + # Append data + with open(latency_file, mode='a', newline='') as file: + writer = csv.writer(file) + writer.writerow([frame_count, proc_time]) \ No newline at end of file diff --git a/logs/old_test_results.csv b/logs/old_test_results.csv deleted file mode 100644 index 70ced51..0000000 --- a/logs/old_test_results.csv +++ /dev/null @@ -1,133 +0,0 @@ -Timestamp,Target_Dir,Up_Votes,Left_Votes,Right_Votes,Total_Strong,Dominant,Results -23:07:49,UP,340,466,171,977,LEFT,WRONG DIRECTION! -23:07:50,UP,0,81,590,671,RIGHT,WRONG DIRECTION! -23:07:51,UP,0,102,221,323,RIGHT,WRONG DIRECTION! -23:07:52,UP,1270,1340,24,2634,LEFT,WRONG DIRECTION! -23:07:52,UP,0,115,143,258,RIGHT,WRONG DIRECTION! -23:07:53,UP,0,2138,0,2138,LEFT,WRONG DIRECTION! -23:07:53,LEFT,59,0,0,59,UP,WRONG DIRECTION! -23:07:55,UP,0,2138,0,2138,LEFT,WEAK PUNCH! -23:07:55,UP,0,0,646,646,RIGHT,WRONG DIRECTION! -23:07:58,LEFT,391,17,59,467,UP,WRONG DIRECTION! -23:07:59,LEFT,2481,14,602,3097,UP,WRONG DIRECTION! -23:08:00,LEFT,1905,207,64,2176,UP,WRONG DIRECTION! -23:08:00,UP,2,84,131,217,RIGHT,WRONG DIRECTION! -23:08:01,UP,0,77,81,158,RIGHT,WRONG DIRECTION! -23:08:05,UP,0,77,81,158,RIGHT,WEAK PUNCH! -23:08:06,LEFT,284,0,32,316,UP,WRONG DIRECTION! -23:08:07,LEFT,2034,406,42,2482,UP,WRONG DIRECTION! -23:08:07,UP,0,0,14,14,RIGHT,WRONG DIRECTION! -23:08:08,UP,14,0,64,78,RIGHT,WRONG DIRECTION! -23:08:10,UP,1421,0,740,2161,UP,VALID -23:08:11,RIGHT,1421,0,740,2161,UP,WEAK PUNCH! -23:08:13,RIGHT,0,83,22,105,LEFT,WRONG DIRECTION! -23:08:14,RIGHT,17,0,312,329,RIGHT,VALID -23:08:15,RIGHT,17,0,312,329,RIGHT,WEAK PUNCH! -23:08:18,RIGHT,0,25,0,25,LEFT,WRONG DIRECTION! -23:08:30,RIGHT,152,94,76,322,UP,WRONG DIRECTION! -23:08:32,RIGHT,0,0,219,219,RIGHT,VALID -23:08:36,UP,262,16,21,299,UP,VALID -23:08:37,RIGHT,0,98,50,148,LEFT,WRONG DIRECTION! -23:08:37,LEFT,91,410,175,676,LEFT,VALID -23:08:38,LEFT,869,131,356,1356,UP,WRONG DIRECTION! -23:08:39,LEFT,3541,371,0,3912,UP,WRONG DIRECTION! -23:08:41,LEFT,92,4081,0,4173,LEFT,VALID -23:08:42,RIGHT,476,40,804,1320,RIGHT,VALID -23:08:42,LEFT,3819,169,0,3988,UP,WRONG DIRECTION! -23:08:43,LEFT,876,979,357,2212,LEFT,VALID -23:08:44,RIGHT,1280,1115,135,2530,UP,WRONG DIRECTION! -23:08:45,RIGHT,40,7,2582,2629,RIGHT,VALID -23:08:48,RIGHT,0,0,142,142,RIGHT,VALID -23:08:50,UP,0,0,142,142,RIGHT,WEAK PUNCH! -23:08:59,UP,0,0,283,283,RIGHT,WRONG DIRECTION! -23:09:01,UP,99,526,128,753,LEFT,WRONG DIRECTION! -23:09:04,UP,400,205,3,608,UP,VALID -23:09:06,RIGHT,1217,353,780,2350,UP,WRONG DIRECTION! -23:09:07,RIGHT,691,272,77,1040,UP,WRONG DIRECTION! -23:09:08,RIGHT,189,1145,110,1444,LEFT,WRONG DIRECTION! -23:09:09,RIGHT,254,0,90,344,UP,WRONG DIRECTION! -23:09:11,RIGHT,400,205,3,608,UP,WEAK PUNCH! -23:09:12,RIGHT,0,92,95,187,RIGHT,VALID -23:09:13,UP,0,92,95,187,RIGHT,WEAK PUNCH! -23:09:14,UP,1293,0,0,1293,UP,VALID -23:15:33,LEFT,31,541,93,665,LEFT,VALID -23:15:34,RIGHT,2,8,948,958,RIGHT,VALID -23:15:36,RIGHT,29,44,304,377,RIGHT,VALID -23:15:38,LEFT,31,75,1913,2019,RIGHT,WRONG DIRECTION! -23:15:39,LEFT,388,183,1347,1918,RIGHT,WRONG DIRECTION! -23:15:41,LEFT,423,2251,484,3158,LEFT,VALID -23:15:42,RIGHT,0,279,79,358,LEFT,WRONG DIRECTION! -23:15:43,RIGHT,0,0,441,441,RIGHT,VALID -23:15:48,RIGHT,0,0,24,24,RIGHT,VALID -23:15:59,RIGHT,0,104,531,635,RIGHT,VALID -23:16:01,UP,85,92,133,310,RIGHT,WRONG DIRECTION! -23:16:02,UP,157,429,75,661,LEFT,WRONG DIRECTION! -23:16:03,UP,4,2222,0,2226,LEFT,WRONG DIRECTION! -23:16:05,UP,110,0,0,110,UP,VALID -23:16:08,RIGHT,132,413,9,554,LEFT,WRONG DIRECTION! -23:16:09,RIGHT,148,1,0,149,UP,WRONG DIRECTION! -23:16:10,RIGHT,162,47,4,213,UP,WRONG DIRECTION! -23:16:12,RIGHT,26,0,716,742,RIGHT,VALID -23:16:14,UP,7,0,456,463,RIGHT,WRONG DIRECTION! -23:16:14,UP,777,692,175,1644,UP,VALID -23:16:16,RIGHT,791,0,172,963,UP,WRONG DIRECTION! -23:16:18,RIGHT,0,139,0,139,LEFT,WRONG DIRECTION! -23:16:19,RIGHT,0,33,98,131,RIGHT,VALID -23:16:21,LEFT,152,0,474,626,RIGHT,WRONG DIRECTION! -23:16:21,LEFT,945,1125,0,2070,LEFT,VALID -23:16:22,UP,367,0,98,465,UP,VALID -23:16:24,LEFT,112,747,0,859,LEFT,VALID -23:16:27,RIGHT,0,573,93,666,LEFT,WRONG DIRECTION! -23:16:28,RIGHT,0,0,54,54,RIGHT,VALID -23:16:29,UP,253,1410,382,2045,LEFT,WRONG DIRECTION! -23:16:30,UP,607,681,0,1288,LEFT,WRONG DIRECTION! -23:16:30,UP,54,0,2156,2210,RIGHT,WRONG DIRECTION! -23:16:31,UP,1158,965,36,2159,UP,VALID -23:24:41,UP,31,0,602,633,RIGHT,WRONG DIRECTION! -23:24:41,UP,0,131,0,131,LEFT,WRONG DIRECTION! -23:24:42,UP,252,23,52,327,UP,VALID -23:24:43,LEFT,0,87,317,404,RIGHT,WRONG DIRECTION! -23:24:44,LEFT,0,366,6,372,LEFT,VALID -23:24:47,RIGHT,0,45,0,45,LEFT,WRONG DIRECTION! -23:24:51,RIGHT,0,9,232,241,RIGHT,VALID -23:24:54,RIGHT,598,4,129,731,UP,WRONG DIRECTION! -23:24:57,RIGHT,0,0,424,424,RIGHT,VALID -23:46:35,UP,99,210,460,769,RIGHT,WRONG DIRECTION! -23:46:37,UP,227,0,0,227,UP,VALID -23:46:38,UP,777,0,11,788,UP,VALID -23:46:42,RIGHT,5,0,693,698,RIGHT,VALID -23:46:47,RIGHT,0,0,250,250,RIGHT,VALID -23:46:52,RIGHT,0,0,10,10,RIGHT,VALID -23:46:53,RIGHT,14,0,1392,1406,RIGHT,VALID -23:46:55,RIGHT,20,0,254,274,RIGHT,VALID -00:48:42,RIGHT,437,67,1305,1809,RIGHT,VALID -01:10:30,RIGHT,260,0,0,260,UP,WRONG DIRECTION! -01:10:34,RIGHT,8,381,0,389,LEFT,WRONG DIRECTION! -01:10:35,RIGHT,128,267,36,431,LEFT,WRONG DIRECTION! -01:10:36,RIGHT,0,116,0,116,LEFT,WRONG DIRECTION! -01:10:39,RIGHT,3,175,0,178,LEFT,WRONG DIRECTION! -01:10:40,RIGHT,131,0,12,143,UP,WRONG DIRECTION! -01:10:42,RIGHT,551,213,0,764,UP,WRONG DIRECTION! -01:10:43,RIGHT,9,218,0,227,LEFT,WRONG DIRECTION! -01:12:10,RIGHT,256,538,1564,2358,RIGHT,VALID -01:12:11,LEFT,0,13,81,94,RIGHT,WRONG DIRECTION! -01:12:12,LEFT,124,1094,384,1602,LEFT,VALID -01:12:13,UP,366,116,1690,2172,RIGHT,WRONG DIRECTION! -01:12:14,UP,10,149,256,415,RIGHT,WRONG DIRECTION! -01:12:14,UP,60,210,447,717,RIGHT,WRONG DIRECTION! -01:12:14,LEFT,153,0,0,153,UP,WRONG DIRECTION! -01:12:21,UP,784,374,170,1328,UP,VALID -01:12:32,LEFT,403,351,68,822,UP,WRONG DIRECTION! -01:12:33,LEFT,88,135,668,891,RIGHT,WRONG DIRECTION! -01:12:34,LEFT,6,1108,708,1822,LEFT,VALID -01:12:36,UP,80,7,102,189,RIGHT,WRONG DIRECTION! -01:12:37,UP,168,0,24,192,UP,VALID -01:12:38,RIGHT,0,150,0,150,LEFT,WRONG DIRECTION! -01:12:39,RIGHT,0,0,279,279,RIGHT,VALID -01:46:16,UP,268,250,274,792,RIGHT,NO CLEAR DIRECTION! -01:46:20,UP,5,0,818,823,RIGHT,WRONG DIRECTION! -01:46:22,UP,25,0,0,25,UP,VALID -01:46:37,LEFT,345,70,110,525,UP,WRONG DIRECTION! -01:46:38,LEFT,1,848,0,849,LEFT,VALID -01:46:41,RIGHT,48,0,256,304,RIGHT,VALID -01:46:42,RIGHT,0,25,164,189,RIGHT,VALID diff --git a/logs/old_test_results_2.csv b/logs/old_test_results_2.csv deleted file mode 100644 index d14eef3..0000000 --- a/logs/old_test_results_2.csv +++ /dev/null @@ -1,29 +0,0 @@ -Timestamp,Target_Dir,Up_Votes,Left_Votes,Right_Votes,Total_Strong,Dominant,Results,Max_Int,Avg_Int,MOG_Density,Proc_Time -02:24:55,LEFT,1317,740,432,2489,UP,WRONG DIRECTION!,105.0,42.7,11.095,0.0047 -02:24:57,LEFT,114,361,359,834,LEFT,VALID,113.0,40.0,8.265,0.0058 -02:24:59,LEFT,0,0,38,38,RIGHT,WRONG DIRECTION!,121.0,20.7,7.584,0.0062 -02:25:00,LEFT,0,0,36,36,RIGHT,WRONG DIRECTION!,119.0,53.5,6.707,0.0075 -02:25:02,LEFT,0,2067,0,2067,LEFT,VALID,109.0,19.6,6.622,0.0076 -02:25:02,LEFT,0,14,0,14,LEFT,VALID,85.0,10.6,6.84,0.0031 -02:25:05,RIGHT,0,619,0,619,LEFT,WRONG DIRECTION!,115.0,17.2,3.96,0.0044 -02:25:06,RIGHT,32,0,60,92,RIGHT,VALID,115.0,21.8,5.196,0.0062 -02:25:07,RIGHT,658,858,189,1705,LEFT,WRONG DIRECTION!,130.0,45.1,1.646,0.0051 -02:25:08,RIGHT,0,2633,0,2633,LEFT,WRONG DIRECTION!,133.0,25.5,1.602,0.0042 -02:25:09,RIGHT,0,895,0,895,LEFT,WRONG DIRECTION!,126.0,37.0,2.295,0.0048 -02:25:10,RIGHT,0,0,1117,1117,RIGHT,VALID,127.0,51.8,2.782,0.0058 -02:25:12,RIGHT,354,0,138,492,UP,WRONG DIRECTION!,93.0,16.6,5.335,0.0052 -02:25:12,RIGHT,0,357,0,357,LEFT,WRONG DIRECTION!,96.0,34.1,7.497,0.0038 -02:25:13,RIGHT,42,0,95,137,RIGHT,VALID,86.0,10.1,6.349,0.0032 -02:25:16,LEFT,16,910,0,926,LEFT,VALID,124.0,12.0,3.544,0.0044 -02:25:17,UP,400,299,44,743,UP,VALID,102.0,37.0,4.931,0.0035 -02:25:34,LEFT,249,0,818,1067,RIGHT,WRONG DIRECTION!,59.0,10.5,5.709,0.0052 -02:25:35,LEFT,17,0,459,476,RIGHT,WRONG DIRECTION!,76.0,14.0,8.598,0.0031 -02:25:36,LEFT,104,0,398,502,RIGHT,WRONG DIRECTION!,60.0,9.2,7.198,0.0034 -02:25:36,LEFT,2,69,162,233,RIGHT,WRONG DIRECTION!,57.0,6.5,5.985,0.0059 -02:25:37,LEFT,483,0,168,651,UP,WRONG DIRECTION!,64.0,7.6,5.222,0.0057 -02:25:37,LEFT,0,0,203,203,RIGHT,WRONG DIRECTION!,87.0,15.8,6.621,0.0065 -02:25:38,LEFT,220,768,900,1888,RIGHT,WRONG DIRECTION!,78.0,14.9,4.811,0.0051 -02:25:42,LEFT,46,14,122,182,RIGHT,WRONG DIRECTION!,75.0,30.8,9.733,0.0052 -02:25:44,LEFT,0,54,0,54,LEFT,VALID,68.0,13.8,7.756,0.006 -02:25:46,UP,310,235,1172,1717,RIGHT,WRONG DIRECTION!,116.0,14.1,10.381,0.006 -02:25:47,UP,694,0,69,763,UP,VALID,85.0,13.9,8.47,0.0043 diff --git a/logs/test_results.csv b/logs/test_results.csv index 118f050..fccd53d 100644 --- a/logs/test_results.csv +++ b/logs/test_results.csv @@ -684,3 +684,91 @@ Timestamp,Mode,Player,Target_Dir,Up_Votes,Left_Votes,Right_Votes,Total_Strong,Do 21:45:42,LIVE,PLAYER 1,LEFT,122,1804,0,1926,LEFT,VALID,168.0,0.5,5.0,0.01 21:47:08,LIVE,PLAYER 1,LEFT,515,9,6,530,UP,WRONG DIRECTION!,219.0,0.3,5.0,0.01 21:47:09,LIVE,PLAYER 1,LEFT,644,8569,0,9213,LEFT,VALID,224.0,1.6,5.0,0.01 +01:41:10,MULTI,PLAYER 2,LEFT,435,353,2748,3536,RIGHT,WRONG DIRECTION!,106.0,2.7,66.819,0.0061 +01:41:12,MULTI,PLAYER 2,LEFT,1,0,1202,1203,RIGHT,WRONG DIRECTION!,92.0,2.9,1.992,0.0057 +01:41:12,MULTI,PLAYER 2,LEFT,26,1593,0,1619,LEFT,VALID,137.0,4.3,3.641,0.0069 +01:41:14,MULTI,PLAYER 2,RIGHT,1059,614,0,1673,UP,WRONG DIRECTION!,101.0,17.3,7.587,0.0053 +01:41:14,MULTI,PLAYER 1,LEFT,1146,19,1191,2356,RIGHT,WRONG DIRECTION!,111.0,12.8,9.062,0.0056 +01:41:14,MULTI,PLAYER 2,RIGHT,133,0,4776,4909,RIGHT,VALID,100.0,19.3,11.295,0.0054 +01:41:15,MULTI,PLAYER 1,LEFT,31,646,29,706,LEFT,VALID,121.0,13.5,15.395,0.006 +01:41:16,MULTI,PLAYER 2,LEFT,0,0,824,824,RIGHT,WRONG DIRECTION!,154.0,3.2,12.317,0.0058 +01:41:16,MULTI,PLAYER 1,UP,3614,0,110,3724,UP,VALID,107.0,12.7,15.218,0.0068 +01:41:17,MULTI,PLAYER 2,LEFT,0,670,0,670,LEFT,VALID,158.0,4.5,4.865,0.0068 +01:41:20,MULTI,PLAYER 2,UP,42,0,596,638,RIGHT,WRONG DIRECTION!,165.0,3.1,3.402,0.0061 +01:41:21,MULTI,PLAYER 2,UP,0,0,3777,3777,RIGHT,WRONG DIRECTION!,148.0,4.6,11.524,0.0073 +01:41:21,MULTI,PLAYER 2,UP,0,972,88,1060,LEFT,WRONG DIRECTION!,170.0,6.5,17.687,0.0079 +01:41:27,MULTI,PLAYER 1,LEFT,325,1749,0,2074,LEFT,VALID,142.0,4.9,10.076,0.0053 +01:41:30,MULTI,PLAYER 1,LEFT,5383,291,34,5708,UP,WRONG DIRECTION!,117.0,8.9,2.625,0.0055 +01:41:31,MULTI,PLAYER 1,LEFT,1248,0,0,1248,UP,WRONG DIRECTION!,71.0,4.3,1.549,0.0074 +01:41:32,MULTI,PLAYER 1,LEFT,1248,0,0,1248,UP,WRONG DIRECTION!,69.0,6.0,0.985,0.0058 +01:41:33,MULTI,PLAYER 1,LEFT,53,2057,0,2110,LEFT,VALID,102.0,7.4,1.421,0.0053 +01:41:37,MULTI,PLAYER 1,UP,30,199,277,506,RIGHT,WRONG DIRECTION!,57.0,3.5,3.562,0.0051 +01:41:38,MULTI,PLAYER 1,UP,1285,131,125,1541,UP,VALID,79.0,6.8,3.633,0.0068 +01:41:42,MULTI,PLAYER 1,UP,0,0,659,659,RIGHT,WRONG DIRECTION!,95.0,12.2,3.778,0.0076 +01:41:42,MULTI,PLAYER 2,UP,753,0,2722,3475,RIGHT,WRONG DIRECTION!,174.0,7.5,8.631,0.0046 +01:41:43,MULTI,PLAYER 2,UP,4879,0,0,4879,UP,VALID,152.0,6.8,12.036,0.0061 +01:41:44,MULTI,PLAYER 2,RIGHT,207,2502,0,2709,LEFT,WRONG DIRECTION!,133.0,3.9,8.173,0.0069 +01:41:45,MULTI,PLAYER 1,UP,353,103,179,635,UP,VALID,72.0,14.1,4.644,0.0051 +01:41:45,MULTI,PLAYER 2,RIGHT,3294,0,47,3341,UP,WRONG DIRECTION!,166.0,10.1,4.644,0.0051 +01:41:46,MULTI,PLAYER 1,RIGHT,2765,10,685,3460,UP,WRONG DIRECTION!,78.0,7.9,7.345,0.0055 +01:41:48,MULTI,PLAYER 1,RIGHT,772,190,997,1959,RIGHT,VALID,102.0,16.7,8.367,0.0065 +01:41:49,MULTI,PLAYER 2,RIGHT,720,1521,35,2276,LEFT,WRONG DIRECTION!,126.0,12.5,37.308,0.0067 +01:41:50,MULTI,PLAYER 2,RIGHT,0,0,597,597,RIGHT,VALID,144.0,5.5,39.443,0.0082 +01:41:50,MULTI,PLAYER 1,UP,1060,0,0,1060,UP,VALID,59.0,5.1,33.62,0.0084 +01:41:52,MULTI,PLAYER 1,LEFT,0,0,695,695,RIGHT,WRONG DIRECTION!,93.0,13.7,5.506,0.0064 +01:41:53,MULTI,PLAYER 2,LEFT,558,64,336,958,UP,WRONG DIRECTION!,107.0,7.6,4.408,0.0077 +01:41:53,MULTI,PLAYER 2,LEFT,90,204,755,1049,RIGHT,WRONG DIRECTION!,109.0,9.7,1.285,0.0078 +01:41:54,MULTI,PLAYER 2,LEFT,520,3013,447,3980,LEFT,VALID,126.0,12.5,3.091,0.0057 +01:42:01,MULTI,PLAYER 1,LEFT,72,2406,0,2478,LEFT,VALID,70.0,5.5,1.054,0.0051 +01:42:09,MULTI,PLAYER 2,RIGHT,1625,12,368,2005,UP,WRONG DIRECTION!,123.0,13.6,5.218,0.007 +01:42:10,MULTI,PLAYER 2,RIGHT,1760,0,0,1760,UP,WRONG DIRECTION!,125.0,12.1,4.375,0.0073 +01:42:11,MULTI,PLAYER 1,UP,3484,0,24,3508,UP,VALID,87.0,21.9,5.959,0.0068 +01:42:24,SINGLE,PLAYER 1,LEFT,236,0,7855,8091,RIGHT,WRONG DIRECTION!,176.0,12.4,15.703,0.0063 +01:42:24,SINGLE,PLAYER 1,LEFT,0,2942,0,2942,LEFT,VALID,164.0,8.9,10.389,0.0067 +01:44:29,SINGLE,PLAYER 1,RIGHT,248,0,1243,1491,RIGHT,VALID,91.0,1.9,13.529,0.0068 +01:44:30,SINGLE,PLAYER 1,LEFT,1510,206,917,2633,UP,WRONG DIRECTION!,111.0,7.3,16.296,0.0062 +01:44:33,SINGLE,PLAYER 1,LEFT,1056,12,140,1208,UP,WRONG DIRECTION!,73.0,4.5,5.883,0.005 +01:44:34,SINGLE,PLAYER 1,LEFT,413,15,54,482,UP,WRONG DIRECTION!,85.0,5.7,6.078,0.0064 +01:44:37,SINGLE,PLAYER 1,LEFT,2023,2,38,2063,UP,WRONG DIRECTION!,102.0,7.1,1.853,0.0053 +01:44:38,SINGLE,PLAYER 1,LEFT,402,1,3,406,UP,WRONG DIRECTION!,42.0,10.5,54.029,0.0099 +01:44:45,SINGLE,PLAYER 1,LEFT,2739,2,0,2741,UP,WRONG DIRECTION!,92.0,4.4,3.05,0.0062 +01:44:45,SINGLE,PLAYER 1,LEFT,4010,5050,0,9060,LEFT,VALID,133.0,13.0,7.626,0.0074 +01:44:52,SINGLE,PLAYER 1,UP,1870,94,639,2603,UP,VALID,101.0,22.9,6.909,0.007 +01:44:55,SINGLE,PLAYER 1,LEFT,103,1050,4621,5774,RIGHT,WRONG DIRECTION!,107.0,23.3,5.878,0.0062 +01:44:55,SINGLE,PLAYER 1,LEFT,437,1424,1445,3306,RIGHT,WRONG DIRECTION!,145.0,12.5,6.407,0.0075 +01:44:56,SINGLE,PLAYER 1,LEFT,0,2005,62,2067,LEFT,VALID,154.0,15.0,2.363,0.0056 +01:44:57,SINGLE,PLAYER 1,UP,141,68,867,1076,RIGHT,WRONG DIRECTION!,120.0,10.2,5.25,0.006 +01:45:06,SINGLE,PLAYER 1,UP,21,430,157,608,LEFT,WRONG DIRECTION!,62.0,4.9,4.793,0.0064 +01:45:13,SINGLE,PLAYER 1,UP,2142,1803,0,3945,UP,VALID,120.0,19.2,6.515,0.0072 +01:45:22,SINGLE,PLAYER 1,LEFT,1846,52,0,1898,UP,WRONG DIRECTION!,140.0,5.9,3.464,0.0065 +01:45:26,SINGLE,PLAYER 1,LEFT,1501,907,25,2433,UP,WRONG DIRECTION!,154.0,6.5,2.304,0.0063 +01:45:27,SINGLE,PLAYER 1,LEFT,717,810,538,2065,LEFT,WEAK LEFT PUNCH!,179.0,19.1,5.102,0.008 +01:45:28,SINGLE,PLAYER 1,LEFT,0,0,2202,2202,RIGHT,WRONG DIRECTION!,159.0,10.8,4.048,0.0073 +01:45:29,SINGLE,PLAYER 1,LEFT,0,6480,0,6480,LEFT,VALID,126.0,15.1,11.052,0.0064 +01:45:31,SINGLE,PLAYER 1,LEFT,1097,42,0,1139,UP,WRONG DIRECTION!,128.0,7.0,4.009,0.0058 +01:45:32,SINGLE,PLAYER 1,LEFT,1173,118,9,1300,UP,WRONG DIRECTION!,156.0,19.8,2.667,0.0058 +01:45:33,SINGLE,PLAYER 1,LEFT,42,418,0,460,LEFT,VALID,90.0,13.1,5.614,0.0071 +01:45:34,SINGLE,PLAYER 1,LEFT,1594,0,141,1735,UP,WRONG DIRECTION!,155.0,12.5,2.053,0.0055 +01:45:36,SINGLE,PLAYER 1,LEFT,298,86,2542,2926,RIGHT,WRONG DIRECTION!,102.0,10.7,9.064,0.0061 +01:45:38,SINGLE,PLAYER 1,LEFT,51,0,2151,2202,RIGHT,WRONG DIRECTION!,165.0,15.1,2.243,0.0062 +01:45:38,SINGLE,PLAYER 1,LEFT,613,1378,1826,3817,RIGHT,WRONG DIRECTION!,163.0,23.9,5.432,0.0051 +01:45:39,SINGLE,PLAYER 1,LEFT,136,5648,0,5784,LEFT,VALID,121.0,26.7,4.285,0.0057 +01:45:42,SINGLE,PLAYER 1,LEFT,2233,671,11,2915,UP,WRONG DIRECTION!,118.0,7.5,11.724,0.0073 +01:45:43,SINGLE,PLAYER 1,LEFT,457,5500,0,5957,LEFT,VALID,148.0,12.4,4.639,0.007 +01:45:51,SINGLE,PLAYER 1,UP,2057,269,28,2354,UP,VALID,100.0,7.6,5.835,0.0057 +01:45:53,SINGLE,PLAYER 1,RIGHT,0,2223,8,2231,LEFT,WRONG DIRECTION!,125.0,6.3,2.722,0.0066 +01:45:54,SINGLE,PLAYER 1,RIGHT,202,1534,4,1740,LEFT,WRONG DIRECTION!,99.0,5.5,2.492,0.0066 +01:45:55,SINGLE,PLAYER 1,RIGHT,17,1849,0,1866,LEFT,WRONG DIRECTION!,84.0,6.1,9.679,0.0045 +01:45:59,SINGLE,PLAYER 1,RIGHT,338,1146,0,1484,LEFT,WRONG DIRECTION!,90.0,3.1,26.087,0.0082 +01:46:46,SINGLE,PLAYER 1,RIGHT,1439,517,4692,6648,RIGHT,VALID,131.0,7.9,16.108,0.0073 +01:46:53,SINGLE,PLAYER 1,UP,1476,0,0,1476,UP,VALID,93.0,6.9,6.977,0.0059 +01:47:00,SINGLE,PLAYER 1,RIGHT,1091,53,387,1531,UP,WRONG DIRECTION!,126.0,13.9,2.066,0.0069 +01:47:01,SINGLE,PLAYER 1,RIGHT,966,34,423,1423,UP,WRONG DIRECTION!,124.0,15.2,0.868,0.0064 +01:47:03,SINGLE,PLAYER 1,RIGHT,640,37,804,1481,RIGHT,VALID,121.0,10.4,2.531,0.0061 +01:47:04,SINGLE,PLAYER 1,UP,1104,0,42,1146,UP,VALID,65.0,3.3,2.219,0.0055 +01:47:26,SINGLE,PLAYER 1,LEFT,3160,85,0,3245,UP,WRONG DIRECTION!,107.0,7.1,5.175,0.006 +01:47:27,SINGLE,PLAYER 1,LEFT,24,587,0,611,LEFT,VALID,89.0,7.0,4.701,0.0083 +01:47:35,SINGLE,PLAYER 1,RIGHT,2326,0,828,3154,UP,WRONG DIRECTION!,153.0,8.2,2.975,0.0053 +01:47:36,SINGLE,PLAYER 1,RIGHT,18,367,1364,1749,RIGHT,VALID,151.0,17.0,4.663,0.0064 +01:47:36,SINGLE,PLAYER 1,LEFT,0,861,0,861,LEFT,VALID,100.0,18.5,0.927,0.0072 +01:47:40,SINGLE,PLAYER 1,RIGHT,26,107,907,1040,RIGHT,VALID,99.0,12.9,3.056,0.0069 diff --git a/main.py b/main.py index 264cc5d..64b59e0 100644 --- a/main.py +++ b/main.py @@ -51,6 +51,7 @@ frame = cv.resize(frame, (config.WIDTH, config.HEIGHT)) frame = cv.flip(frame, 1) prev_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) +frame_count = 0 #== Main Loop ==# @@ -72,6 +73,9 @@ visual_motion = cv.absdiff(gray, prev_gray) # Calculate diff between current and prev frame. If it hasn't change, then black (0), else higher num (closer to255) heatmap = cv.applyColorMap(visual_motion * 5, cv.COLORMAP_JET) # Apply grayscal color to JET color, visual_motion * 5 to amplify weak differences + frame_count += 1 + debugger.log_latency(frame_count, pipeline.last_proc_time) + if config.WARMUP_FRAMES > 0: config.WARMUP_FRAMES -= 1 # 60 sec for the camera to adjust auto exposure and white balance, also to let players see the calibration screen and stand still to let MOG2 learn the background better, reducing false positives in the actual game # Show calibration text diff --git a/modes.py b/modes.py index f59d450..872ffaa 100644 --- a/modes.py +++ b/modes.py @@ -1,6 +1,8 @@ import cv2 as cv +import numpy as np import time import random +import pygame import config from player import Player from ui import DamageText @@ -197,7 +199,7 @@ def update(self, mask, player, current_time, sound, floating_texts): if current_time > self.action_timer: self.state = "FIRE" self.action_timer = current_time + 1.2 - try: sound.play_sfx("crit_p1") + try: sound.play_sfx("laser") except: pass elif self.state == "FIRE": @@ -207,7 +209,7 @@ def update(self, mask, player, current_time, sound, floating_texts): x_start = self.zone * zone_w zone_mask = mask[:, x_start:x_start + zone_w] - if cv.countNonZero(zone_mask) > 100: + if cv.countNonZero(zone_mask) > 50: player.health -= config.LASER_BOSS_DAMAGE self.has_damaged = True floating_texts.append(DamageText(f"-{config.LASER_BOSS_DAMAGE}", player.target[0], player.target[1], (0, 0, 255))) @@ -227,17 +229,78 @@ def draw_effects(self, heatmap, current_time): if self.state in ["WARN", "FIRE"]: zone_w = config.WIDTH // 3 x_start = self.zone * zone_w + zone_center_x = x_start + (zone_w // 2) + # Warning Phase if self.state == "WARN": - cv.rectangle(heatmap, (x_start, 0), (x_start + zone_w, config.HEIGHT), (0, 165, 255), 4) - cv.putText(heatmap, "DANGER!", (x_start + 10, config.HEIGHT//2), cv.FONT_HERSHEY_SIMPLEX, 1, (0, 165, 255), 3) - + # Draw warning box + cv.rectangle(heatmap, + (x_start, 0), + (x_start + zone_w, config.HEIGHT), + (0, 165, 255), 4) + + # Text: DANGER + text = "DANGER!" + (tw, th), _ = cv.getTextSize(text, cv.FONT_HERSHEY_SIMPLEX, 1.2, 3) + + text_x = zone_center_x - tw // 2 + text_y = config.HEIGHT // 2 - 20 + + # Shadow (important for readability) + cv.putText(heatmap, text, + (text_x + 2, text_y + 2), + cv.FONT_HERSHEY_SIMPLEX, 1.2, + (0, 0, 0), 5) + + # Main text + cv.putText(heatmap, text, + (text_x, text_y), + cv.FONT_HERSHEY_SIMPLEX, 1.2, + (0, 165, 255), 3) + + # Fire Phase elif self.state == "FIRE": overlay = heatmap.copy() - cv.rectangle(overlay, (x_start, 0), (x_start + zone_w, config.HEIGHT), (0, 0, 255), -1) - cv.addWeighted(overlay, 0.5, heatmap, 0.5, 0, heatmap) - - cv.rectangle(heatmap, (x_start, 0), (x_start + zone_w, config.HEIGHT), (0, 0, 255), 8) + + # Red danger fill + cv.rectangle(overlay, + (x_start, 0), + (x_start + zone_w, config.HEIGHT), + (0, 0, 255), -1) + + # Blend effect (slightly reduced for clarity) + cv.addWeighted(overlay, 0.35, heatmap, 0.65, 0, heatmap) + + # Strong border + # Pulsing thickness , box breathes, player notices danger faster + if int(current_time * 5) % 2 == 0: + thickness = 6 + else: + thickness = 2 + + cv.rectangle(heatmap, + (x_start, 0), + (x_start + zone_w, config.HEIGHT), + (0, 0, 255), thickness) + + # Text: DODGE! + dodge_text = "DODGE!" + (tw, th), _ = cv.getTextSize(dodge_text, cv.FONT_HERSHEY_SIMPLEX, 1.2, 3) + + text_x = zone_center_x - tw // 2 + text_y = config.HEIGHT // 2 - 20 + + # Shadow + cv.putText(heatmap, dodge_text, + (text_x + 2, text_y + 2), + cv.FONT_HERSHEY_SIMPLEX, 1.2, + (0, 0, 0), 5) + + # Main + cv.putText(heatmap, dodge_text, + (text_x, text_y), + cv.FONT_HERSHEY_SIMPLEX, 1.2, + (255, 255, 255), 3) class ScannerBoss(BaseBoss): def __init__(self): @@ -245,58 +308,124 @@ def __init__(self): self.has_damaged = False self.first_run = True self.scan_start_time = 0 # Tracks when the freeze actually started + self.freeze_sound_played = False + self.scan_sound_played = False - def update(self, mask, player, current_time, sound, floating_texts): + def update(self, mask, player, current_time, sound, floating_texts, diff): + + # First run delay if self.first_run: self.action_timer = current_time + config.BOSS_IDLE_TIME self.first_run = False + # From IDLE To SCAN if self.state == "IDLE": if current_time > self.action_timer: + self.state = "SCAN" - scan_duration = random.uniform(1.0, 10.0) # Random freez duration between 1 and 10 seconds + scan_duration = random.uniform(1.0, 10.0) self.action_timer = current_time + scan_duration self.scan_start_time = current_time self.has_damaged = False - try: sound.play_sfx("button") - except: pass + + # Reset sound flags + self.freeze_sound_played = False + self.scan_sound_played = False + + # From SCAN To FREEZE elif self.state == "SCAN": + + # Play freeze sound slightly later + if not self.freeze_sound_played and current_time > self.scan_start_time + 0.2: + sound.play_sfx("freeze") + self.freeze_sound_played = True + + # End scan if current_time > self.action_timer: self.state = "IDLE" self.action_timer = current_time + config.BOSS_IDLE_TIME + 1.0 + + # Damage Logic elif not self.has_damaged: + if current_time > self.scan_start_time + 0.6: - motion = cv.countNonZero(mask) - freeze_threshold = config.MISS_THRESHOLD * 1.2 # Increase freeze threshold - if motion > freeze_threshold: + # Motion Measurements + motion_area = cv.countNonZero(mask) # how big movement + motion_strength = np.mean(diff) # how strong movement + + # Thresholds + area_threshold = config.MISS_THRESHOLD * 1.2 + + # Decision Logic + # HIGH DAMAGE → strong + large movement + if motion_area > area_threshold and motion_strength > 20: player.health -= 20 self.has_damaged = True - try: sound.play_sfx("hurt_p1") - except: pass - floating_texts.append(DamageText("HIGH!", config.MID_X, 200, (0, 0, 255))) - elif motion > freeze_threshold * 0.75: + sound.play_sfx("hurt_p1") + + floating_texts.append( + DamageText("HIGH!", config.MID_X, 200, (0, 0, 255)) + ) + + # MEDIUM DAMAGE → moderate movement + elif motion_area > area_threshold * 0.7 and motion_strength > 10: player.health -= 10 self.has_damaged = True - try: sound.play_sfx("hurt_p1") - except: pass - floating_texts.append(DamageText("WARNING!", config.MID_X, 200, (0, 255, 255))) - elif motion > freeze_threshold * 0.4: - floating_texts.append(DamageText("STAY STILL!", config.MID_X, 200, (255, 0, 0))) + sound.play_sfx("hurt_p1") + + floating_texts.append( + DamageText("WARNING!", config.MID_X, 200, (0, 255, 255)) + ) + + # LOW WARNING → slight movement + elif motion_strength > 5: + floating_texts.append( + DamageText("STAY STILL!", config.MID_X, 200, (255, 0, 0)) + ) def draw_effects(self, heatmap, current_time): + center_x = config.WIDTH // 2 + + # IDLE to Show SCAN Countdown if self.state == "IDLE" and not self.first_run: time_left = max(0.0, self.action_timer - current_time) - cv.putText(heatmap, f"SCAN IN: {time_left:.1f}s", (config.MID_X - 120, 100), - cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2) + + text = f"SCAN IN: {time_left:.1f}s" + (tw, th), _ = cv.getTextSize(text, cv.FONT_HERSHEY_SIMPLEX, 1, 2) + + cv.putText(heatmap, text, + (center_x - tw // 2, 80), + cv.FONT_HERSHEY_SIMPLEX, 1, + (255, 255, 255), 2) + + # SCAN to FREEZE Phase elif self.state == "SCAN": overlay = heatmap.copy() - cv.rectangle(overlay, (0,0), (config.WIDTH, config.HEIGHT), (255, 255, 0), -1) - cv.addWeighted(overlay, 0.3, heatmap, 0.7, 0, heatmap) - cv.putText(heatmap, "FREEZE!", (config.MID_X - 100, 100), - cv.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 4) + + # Yellow overlay + cv.rectangle(overlay, (0, 0), (config.WIDTH, config.HEIGHT), (0, 255, 255), -1) + cv.addWeighted(overlay, 0.25, heatmap, 0.75, 0, heatmap) + + # FREEZE text + freeze_text = "FREEZE!" + (tw, th), _ = cv.getTextSize(freeze_text, cv.FONT_HERSHEY_SIMPLEX, 2, 4) + + cv.putText(heatmap, freeze_text, + (center_x - tw // 2, 120), + cv.FONT_HERSHEY_SIMPLEX, 2, + (0, 0, 255), 4) + + # Instruction + instruction = "DON'T MOVE" + (tw2, th2), _ = cv.getTextSize(instruction, cv.FONT_HERSHEY_SIMPLEX, 1, 2) + + cv.putText(heatmap, instruction, + (center_x - tw2 // 2, 180), + cv.FONT_HERSHEY_SIMPLEX, 1, + (0, 255, 255), 2) class DeflectorBoss(BaseBoss): def __init__(self): @@ -314,6 +443,7 @@ def update(self, mask, player, current_time, sound, floating_texts): fx = random.randint(100, config.WIDTH - 100) fy = random.randint(100, config.HEIGHT - 100) self.fireball = [fx, fy, 40, current_time + config.DEFLECTOR_BOMB_TIME] # x, y, radius, expiration timer + sound.play_sfx("deflector") elif self.state == "ATTACK": if self.fireball: fx, fy, fr, f_timer = self.fireball @@ -334,14 +464,23 @@ def update(self, mask, player, current_time, sound, floating_texts): floating_texts.append(DamageText("EXHAUSTED!", fx, fy-30, (128, 128, 128))) floating_texts.append(DamageText(f"-{config.DEFLECTOR_BOSS_DAMAGE}", fx, fy, (0, 0, 255))) self.fireball = None + # Boss Heals + self.health += 10 + self.health = min(self.health, config.DEFLECTOR_BOSS_HEALTH) + floating_texts.append(DamageText("BOSS HEALS!", fx, fy - 30, (0, 0, 255))) self.state = "IDLE" self.action_timer = current_time + config.DEFLECTOR_FAIL_COOLDOWN elif current_time > f_timer: # Fireball exploded # Fail: Timer ran out + sound.play_sfx("deflector_explode") player.health -= config.DEFLECTOR_BOSS_DAMAGE sound.play_sfx("hurt_p1") floating_texts.append(DamageText(f"-{config.DEFLECTOR_BOSS_DAMAGE}", fx, fy, (0, 0, 255))) self.fireball = None + # Boss Heals + self.health += 10 + self.health = min(self.health, config.DEFLECTOR_BOSS_HEALTH) + floating_texts.append(DamageText("BOSS HEALS!", fx, fy - 30, (0, 0, 255))) self.state = "IDLE" self.action_timer = current_time + config.DEFLECTOR_FAIL_COOLDOWN @@ -366,7 +505,16 @@ def __init__(self, sound_manager, previous_boss=None): # Remove the previous boss from boss_classes pool boss_classes = [b for b in boss_classes if b.__name__ != previous_boss.__class__.__name__] - self.boss = random.choice(boss_classes)() + self.boss = random.choice(boss_classes)() # Selects the blueprints at random, then creates the boss object in memory + + # Play boss intro sound + try: + pygame.time.delay(200) + if isinstance(self.boss, LaserBoss): self.sound.play_sfx("laser_boss") + elif isinstance(self.boss, ScannerBoss): self.sound.play_sfx("scanner_boss") + elif isinstance(self.boss, DeflectorBoss): self.sound.play_sfx("deflector_boss") + except: + pass self.floating_texts = [] self.p1_feedback = {"text": "", "color": (0,0,0), "time": 0} @@ -405,7 +553,11 @@ def update(self, vision, current_time): self.boss.action_timer -= (0.01 * speed_factor) # 2. Boss Attacks Player (Using customized strategy logic) - self.boss.update(mask, self.p1, current_time, self.sound, self.floating_texts) + # ScannerBoss requires the raw visual_motion matrix to calculate hit strength + if isinstance(self.boss, ScannerBoss): + self.boss.update(mask, self.p1, current_time, self.sound, self.floating_texts, vision['visual_motion']) + else: + self.boss.update(mask, self.p1, current_time, self.sound, self.floating_texts) def draw(self, heatmap, current_time): self.p1.draw_ui(heatmap) diff --git a/sound.py b/sound.py index 12210e9..99007ab 100644 --- a/sound.py +++ b/sound.py @@ -11,6 +11,7 @@ def __init__(self): # === 1. Load Sound Effects (SFX) === # Loading MP3s as SFX is supported in newer Pygame versions + # Player / Basic SFX self.load_sfx("hit_p1", "assets/sfx/hit_p1.mp3") # Impact when P1 is hit self.load_sfx("hit_p2", "assets/sfx/hit_p2.mp3") # Impact when P2 is hit self.load_sfx("crit_p1", "assets/sfx/crit_p1.mp3") # Crit Impact on P1 @@ -19,6 +20,18 @@ def __init__(self): self.load_sfx("hurt_p2", "assets/sfx/hurt_p2.mp3") # P2 Grunt self.load_sfx("button", "assets/sfx/button.mp3") # Menu button hit self.load_sfx("screenshot", "assets/sfx/screenshot.mp3") # Screenshot sound + + # Boss / Gameplay SFX + self.load_sfx("laser", "assets/sfx/laser.mp3") + self.load_sfx("laser_boss", "assets/sfx/laser_boss.mp3") + + self.load_sfx("freeze", "assets/sfx/freeze.mp3") + self.load_sfx("scanner_boss", "assets/sfx/scanner_boss.mp3") + + self.load_sfx("deflector", "assets/sfx/deflector.mp3") + self.load_sfx("deflector_boss", "assets/sfx/deflector_boss.mp3") + self.load_sfx("deflector_explode", "assets/sfx/deflector_explode.mp3") + # === 2. Define Music Tracks === self.menu_bgm = "assets/bgm/bgm_menu.mp3" @@ -34,8 +47,15 @@ def load_sfx(self, name, path): """Loads a sound effect and stores it in the sfx dictionary""" if os.path.exists(path): try: - self.sfx[name] = pygame.mixer.Sound(path) - self.sfx[name].set_volume(0.5) + sound = pygame.mixer.Sound(path) + # Custom Volume Tuning + if name in ["deflector_explode"]: + sound.set_volume(0.7) # Louder explosion + elif name in ["laser", "laser_boss"]: + sound.set_volume(0.6) + else: + sound.set_volume(0.5) + self.sfx[name] = sound except Exception as e: print(f"Error Loading {name}: {e}") else: diff --git a/stats/Objective 1 Performance/academic_latency_graph.png b/stats/Objective 1 Performance/academic_latency_graph.png new file mode 100644 index 0000000..d870d38 Binary files /dev/null and b/stats/Objective 1 Performance/academic_latency_graph.png differ diff --git a/stats/Pseudocolor Mapping Analysis/mapping_results.csv b/stats/Pseudocolor Mapping Analysis/mapping_results.csv new file mode 100644 index 0000000..27f15f9 --- /dev/null +++ b/stats/Pseudocolor Mapping Analysis/mapping_results.csv @@ -0,0 +1,301 @@ +Motion,Hue +201392,41.33125 +3788,118.5203125 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +27,119.989453125 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +18,119.99296875 +0,120.0 +27,119.989453125 +15,119.994140625 +0,120.0 +419,119.836328125 +0,120.0 +18,119.99296875 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +21,119.991796875 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +1713,119.330859375 +18,119.99296875 +0,120.0 +30,119.98828125 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +15,119.994140625 +531,119.792578125 +115,119.955078125 +217,119.915234375 +353,119.862109375 +457,119.821484375 +660,119.7421875 +722,119.71796875 +773,119.698046875 +866,119.66171875 +1023,119.600390625 +0,120.0 +1194,119.53359375 +4051,118.417578125 +1433,119.440234375 +1282,119.49921875 +1103,119.569140625 +951,119.628515625 +696,119.728125 +480,119.8125 +245,119.904296875 +78,119.96953125 +27,119.989453125 +54,119.97890625 +0,120.0 +528,119.79375 +116,119.9546875 +165,119.935546875 +388,119.8484375 +756,119.7046875 +1102,119.56953125 +1487,119.419140625 +2018,119.21171875 +2392,119.065625 +2666,118.95859375 +3110,118.78515625 +3350,118.69140625 +3557,118.610546875 +3557,118.610546875 +3847,118.497265625 +3953,118.455859375 +4107,118.395703125 +4230,118.34765625 +4499,118.242578125 +4482,118.24921875 +4121,118.390234375 +4335,118.306640625 +4559,118.219140625 +4690,118.16796875 +4697,118.165234375 +4618,118.19609375 +4022,118.42890625 +3838,118.50078125 +3434,118.65859375 +3050,118.80859375 +2631,118.972265625 +2266,119.11484375 +1833,119.283984375 +1328,119.48125 +865,119.662109375 +568,119.778125 +350,119.86328125 +141,119.944921875 +15,119.994140625 +0,120.0 +12,119.9953125 +85,119.966796875 +232,119.909375 +354,119.86171875 +506,119.80234375 +605,119.763671875 +703,119.725390625 +1024,119.6 +1316,119.4859375 +1658,119.35234375 +2216,119.134375 +2822,118.89765625 +3249,118.730859375 +3671,118.566015625 +3908,118.4734375 +4025,118.427734375 +4173,118.369921875 +4102,118.39765625 +3892,118.4796875 +3639,118.578515625 +3000,118.828125 +2751,118.925390625 +2510,119.01953125 +2181,119.148046875 +2113,119.174609375 +1815,119.291015625 +1604,119.3734375 +1349,119.473046875 +1309,119.488671875 +1058,119.58671875 +994,119.61171875 +953,119.627734375 +776,119.696875 +730,119.71484375 +570,119.77734375 +500,119.8046875 +427,119.833203125 +384,119.85 +391,119.847265625 +285,119.888671875 +154,119.93984375 +45,119.982421875 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +9,119.996484375 +0,120.0 +0,120.0 +27,119.989453125 +76,119.9703125 +36,119.9859375 +63,119.975390625 +90,119.96484375 +150,119.94140625 +142,119.94453125 +169,119.933984375 +161,119.937109375 +207,119.919140625 +189,119.926171875 +222,119.91328125 +209,119.918359375 +216,119.915625 +204,119.9203125 +163,119.936328125 +165,119.935546875 +98,119.96171875 +129,119.949609375 +154,119.93984375 +135,119.947265625 +149,119.941796875 +93,119.963671875 +98,119.96171875 +142,119.94453125 +95,119.962890625 +51,119.980078125 +35,119.986328125 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +0,120.0 +9,119.996484375 +0,120.0 +87,119.966015625 +373,119.854296875 +761,119.702734375 +1681,119.343359375 +2559,119.000390625 +3473,118.643359375 +4196,118.3609375 +4997,118.048046875 +5681,117.780859375 +6247,117.559765625 +6781,117.351171875 +6984,117.271875 +6746,117.36484375 +6205,117.576171875 +5428,117.8796875 +3640,118.578125 +1595,119.376953125 +461,119.819921875 +1268,119.5046875 +2611,118.980078125 +3913,118.471484375 +4837,118.110546875 +5735,117.759765625 +6520,117.453125 +6981,117.273046875 +7512,117.065625 +8090,116.83984375 +8744,116.584375 +9302,116.36640625 +10001,116.093359375 +10151,116.034765625 +11467,115.520703125 +17457,113.180859375 +23279,110.906640625 diff --git a/stats/Pseudocolor Mapping Analysis/mapping_scatter.png b/stats/Pseudocolor Mapping Analysis/mapping_scatter.png new file mode 100644 index 0000000..05c8cd0 Binary files /dev/null and b/stats/Pseudocolor Mapping Analysis/mapping_scatter.png differ diff --git a/stats/Pseudocolor Mapping Analysis/mapping_summary.txt b/stats/Pseudocolor Mapping Analysis/mapping_summary.txt new file mode 100644 index 0000000..28b0112 --- /dev/null +++ b/stats/Pseudocolor Mapping Analysis/mapping_summary.txt @@ -0,0 +1,6 @@ +PSEUDOCOLOR MAPPING ANALYSIS + +Total Frames: 300 +Correlation (Motion vs Hue): -1.0000 + +Strong inverse relationship (Correct mapping) diff --git a/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_bar_chart.png b/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_bar_chart.png new file mode 100644 index 0000000..c57b797 Binary files /dev/null and b/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_bar_chart.png differ diff --git a/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_performance.txt b/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_performance.txt new file mode 100644 index 0000000..87289e4 --- /dev/null +++ b/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_performance.txt @@ -0,0 +1,4 @@ +=== PERFORMANCE COMPARISON === + +Motion Extraction (absdiff): 0.900 ms +Pseudocolour (applyColorMap): 2.298 ms diff --git a/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_validation.txt b/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_validation.txt new file mode 100644 index 0000000..fa2a70d --- /dev/null +++ b/stats/Pseudocolour Transformation Performance Analysis/pseudocolour_validation.txt @@ -0,0 +1,4 @@ +=== PSEUDOCOLOUR LUT VALIDATION === + +Input (0) -> Output BGR: [128, 0, 0] +Input (255) -> Output BGR: [0, 0, 128] diff --git a/stats/Real-Time Pipeline Performance/latency_performance_graph.png b/stats/Real-Time Pipeline Performance/latency_performance_graph.png new file mode 100644 index 0000000..635b300 Binary files /dev/null and b/stats/Real-Time Pipeline Performance/latency_performance_graph.png differ diff --git a/stats/Real-Time Pipeline Performance/latency_statistics.txt b/stats/Real-Time Pipeline Performance/latency_statistics.txt new file mode 100644 index 0000000..57b6ca8 --- /dev/null +++ b/stats/Real-Time Pipeline Performance/latency_statistics.txt @@ -0,0 +1,5 @@ +REAL-TIME PIPELINE PERFORMANCE + +Mean Latency: 6.64 ms +Standard Deviation: 1.23 +99th Percentile: 10.00 ms diff --git a/ui.py b/ui.py index 89083be..57434b2 100644 --- a/ui.py +++ b/ui.py @@ -1,5 +1,6 @@ import cv2 as cv import time +import random import config class Button: @@ -29,6 +30,9 @@ def draw(self, frame): text_x = x + (w - tw) // 2 text_y = y + (h + th) // 2 text_color = config.BUTTON_TEXT_COLOR if self.active else (150, 150, 150) + + # Shadow + cv.putText(frame, self.text, (text_x + 2, text_y + 2), font, scale, (0, 0, 0), thickness + 2) cv.putText(frame, self.text, (text_x, text_y), font, scale, text_color, thickness) def is_punched(self, mask): @@ -66,8 +70,9 @@ def __init__(self, text, x, y, color=(0, 0, 255)): def update(self): """Moves the text up slightly""" - # Move up by 3 pixels per frame - self.y -= 3 + # Move up by 2 pixels per frame + self.y -= 2 + self.x += random.randint(-1, 1) def is_expired(self): """Checks if the text should disappear""" @@ -75,8 +80,13 @@ def is_expired(self): def draw(self, frame): """Draws the damage text on the screen""" - cv.putText(frame, self.text, (self.x, int(self.y)), - cv.FONT_HERSHEY_SIMPLEX, 1.5, self.color, 3) + x, y = int(self.x), int(self.y) + + # Shadow + cv.putText(frame, self.text, (x + 2, y + 2), cv.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 0), 4) + + # Main + cv.putText(frame, self.text, (x, y), cv.FONT_HERSHEY_SIMPLEX, 1.2, self.color, 2) class PlayerHUD: def __init__(self, name, is_left_side): @@ -92,20 +102,30 @@ def draw(self, frame, health, stamina, overheated): cv.rectangle(frame, (50, 50), (50 + int(health * 3), 70), (0, 0, 255), -1) # Stamina Bar (Yellow) cv.rectangle(frame, (50, 80), (50 + int(stamina * 3), 90), (255, 255, 0), -1) + + # Shadow then Main for Name + cv.putText(frame, self.name, (52, 32), cv.FONT_HERSHEY_SIMPLEX, 1, (0,0,0), 3) cv.putText(frame, self.name, (50, 30), cv.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2) if overheated: - cv.putText(frame, "OVERHEAT!", (50, config.HEIGHT//2), cv.FONT_HERSHEY_SIMPLEX, 1.5, (0,0,255), 3) + x, y = 50, config.HEIGHT // 2 + cv.putText(frame, "OVERHEAT!", (x + 2, y + 2), cv.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 0), 5) + cv.putText(frame, "OVERHEAT!", (x, y), cv.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) else: # Health Bar (Red) cv.rectangle(frame, (config.WIDTH - 50 - int(health * 3), 50), (config.WIDTH - 50, 70), (0, 0, 255), -1) # Stamina Bar (Yellow) cv.rectangle(frame, (config.WIDTH - 50 - int(stamina * 3), 80), (config.WIDTH - 50, 90), (255, 255, 0), -1) + + # Shadow then Main for Name + cv.putText(frame, self.name, (config.WIDTH - 198, 32), cv.FONT_HERSHEY_SIMPLEX, 1, (0,0,0), 3) cv.putText(frame, self.name, (config.WIDTH - 200, 30), cv.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2) if overheated: - cv.putText(frame, "OVERHEAT!", (config.WIDTH - 300, config.HEIGHT//2), cv.FONT_HERSHEY_SIMPLEX, 1.5, (0,0,255), 3) + x, y = config.WIDTH - 300, config.HEIGHT // 2 + cv.putText(frame, "OVERHEAT!", (x + 2, y + 2), cv.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 0), 5) + cv.putText(frame, "OVERHEAT!", (x, y), cv.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) class MainMenuScreen: def __init__(self): diff --git a/update_info.txt b/update_info.txt deleted file mode 100644 index 4929c25..0000000 --- a/update_info.txt +++ /dev/null @@ -1,11 +0,0 @@ -11/2/2026 - Added title screen (Space to start game and q to quite) - Added Fullscreen ability (Press 'F') - Added Stamina (Too much excessive move will cause stamina drain, which the player will freeze) - Added hit type (Miss, Hit, Crit) - -16/2/2026 - Added sound effect and bgm - Added damage indicator - Improve main menu and game over menu - Fixed Overheat bug \ No newline at end of file