foreversheikh commited on
Commit
892f517
·
verified ·
1 Parent(s): f83e10a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +212 -207
app.py CHANGED
@@ -1,207 +1,212 @@
1
- import os
2
- import cv2
3
- import torch
4
- import numpy as np
5
- import time
6
- from datetime import datetime
7
- import threading
8
- import base64
9
- from werkzeug.utils import secure_filename
10
-
11
- from flask import Flask, render_template, Response, request, jsonify
12
- from flask_socketio import SocketIO
13
-
14
- # Important: Make sure your custom utility scripts are accessible
15
- from utils.load_model import load_models
16
- from utils.utils import build_transforms
17
- from network.TorchUtils import get_torch_device
18
- from yolo_detection import analyze_video_with_yolo
19
-
20
- # ---- App Setup ----
21
- app = Flask(__name__)
22
- app.config['SECRET_KEY'] = 'your_secret_key!'
23
-
24
- # ADDED: Configuration for uploaded files
25
- UPLOAD_FOLDER = 'uploads'
26
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
27
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
28
-
29
- socketio = SocketIO(app, async_mode='eventlet')
30
-
31
- # ---- Global Config & Model Loading ----
32
- print("[INFO] Loading models...")
33
- DEVICE = get_torch_device()
34
- FEATURE_EXTRACTOR_PATH = r"pretrained\\c3d.pickle"
35
- AD_MODEL_PATH = r"exps\\c3d\\models\\epoch_80000.pt"
36
- YOLO_MODEL_PATH = r"yolo_my_model.pt"
37
- SAVE_DIR = "outputs/anomaly_frames"
38
- os.makedirs(SAVE_DIR, exist_ok=True)
39
-
40
- anomaly_detector, feature_extractor = load_models(
41
- FEATURE_EXTRACTOR_PATH, AD_MODEL_PATH, features_method="c3d", device=DEVICE
42
- )
43
- feature_extractor.eval()
44
- anomaly_detector.eval()
45
- TRANSFORMS = build_transforms(mode="c3d")
46
- ANOMALY_THRESHOLD = 0.4
47
- print("[INFO] Models loaded successfully.")
48
-
49
- VIDEO_PATHS = {
50
- "Abuse": r"static\\videos\\Abuse.mp4",
51
- "Arrest": r"static\\videos\\Arrest.mp4",
52
- "Arson": r"static\\videos\\Arson.mp4",
53
- "Assault": r"static\\videos\\Assault.mp4",
54
- "Burglary": r"static\\videos\\Burglary.mp4",
55
- "Explosion": r"static\\videos\\Explosion.mp4",
56
- "Fighting": r"static\\videos\\Fighting.mp4",
57
- "RoadAccidents": r"static\\videos\\RoadAccidents.mp4",
58
- "Robbery": r"static\\videos\\Robbery.mp4",
59
- "Shooting": r"static\\videos\\Shooting.mp4",
60
- "Shoplifting": r"static\\videos\\Shoplifting.mp4",
61
- "Stealing": r"static\\videos\\Stealing.mp4",
62
- "Vandalism": r"static\\videos\\Vandalism.mp4",
63
- "Normal": r"static\\videos\\Normal.mp4"
64
- }
65
-
66
- # --- Threading control ---
67
- thread = None
68
- thread_lock = threading.Lock()
69
- stop_event = threading.Event()
70
-
71
- # (The `smooth_score` and `video_processing_task` functions remain unchanged from the previous version)
72
- def smooth_score(scores, new_score, window=5):
73
- scores.append(new_score)
74
- if len(scores) > window:
75
- scores.pop(0)
76
- return float(np.mean(scores))
77
-
78
- def video_processing_task(video_path):
79
- global thread
80
- try:
81
- cap = cv2.VideoCapture(video_path)
82
- if not cap.isOpened():
83
- socketio.emit('processing_error', {'error': f'Could not open video file.'})
84
- return
85
- frame_buffer = []
86
- last_save_time = 0
87
- recent_scores = []
88
- FRAME_SKIP = 4
89
- frame_count = 0
90
- while cap.isOpened() and not stop_event.is_set():
91
- socketio.sleep(0.001)
92
- ret, frame = cap.read()
93
- if not ret: break
94
- frame_count += 1
95
- if frame_count % (FRAME_SKIP + 1) != 0: continue
96
- frame_buffer.append(frame.copy())
97
- if len(frame_buffer) == 16:
98
- frames_resized = [cv2.resize(f, (112, 112)) for f in frame_buffer]
99
- clip_np = np.array(frames_resized, dtype=np.uint8)
100
- clip_torch = torch.from_numpy(clip_np)
101
- clip_torch = TRANSFORMS(clip_torch)
102
- clip_torch = clip_torch.unsqueeze(0).to(DEVICE)
103
- with torch.no_grad():
104
- features = feature_extractor(clip_torch).detach()
105
- score_tensor = anomaly_detector(features).detach()
106
- score = float(score_tensor.view(-1)[0].item())
107
- score = smooth_score(recent_scores, score)
108
- score = float(np.clip(score, 0, 1))
109
- socketio.emit('update_graph', {'score': score})
110
- if score > ANOMALY_THRESHOLD and (time.time() - last_save_time) >= 30:
111
- last_save_time = time.time()
112
- socketio.emit('update_status', {'status': 'Anomaly detected! Saving clip...'})
113
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
114
- clip_dir = os.path.join(SAVE_DIR, f"anomaly_{timestamp}")
115
- os.makedirs(clip_dir, exist_ok=True)
116
- first_frame_path = os.path.join(clip_dir, "anomaly_frame.jpg")
117
- cv2.imwrite(first_frame_path, frame_buffer[0])
118
- try:
119
- yolo_result = analyze_video_with_yolo(first_frame_path, model_path=YOLO_MODEL_PATH, return_class=True)
120
- socketio.emit('update_yolo_text', {'text': f"YOLO Class: {yolo_result}"})
121
- _, buffer = cv2.imencode('.jpg', frame_buffer[0])
122
- b64_str = base64.b64encode(buffer).decode('utf-8')
123
- socketio.emit('update_yolo_image', {'image_data': b64_str})
124
- except Exception as e:
125
- socketio.emit('update_yolo_text', {'text': f'YOLO Error: {e}'})
126
- frame_buffer.clear()
127
- cap.release()
128
- if not stop_event.is_set():
129
- socketio.emit('processing_finished', {'message': 'Video finished.'})
130
- finally:
131
- with thread_lock:
132
- thread = None
133
- stop_event.clear()
134
-
135
- @app.route('/')
136
- def index():
137
- return render_template('index.html', anomaly_names=VIDEO_PATHS.keys())
138
-
139
- # ADDED: New route for handling video uploads
140
- @app.route('/upload', methods=['POST'])
141
- def upload_file():
142
- if 'video' not in request.files:
143
- return jsonify({'error': 'No video file found'}), 400
144
- file = request.files['video']
145
- if file.filename == '':
146
- return jsonify({'error': 'No video file selected'}), 400
147
- if file:
148
- filename = secure_filename(file.filename)
149
- # Add a timestamp to make filenames unique
150
- unique_filename = f"{datetime.now().strftime('%Y%m%d%HM%S')}_{filename}"
151
- save_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
152
- file.save(save_path)
153
- return jsonify({'success': True, 'filename': unique_filename})
154
- return jsonify({'error': 'File upload failed'}), 500
155
-
156
- # MODIFIED: This route now streams both demo and uploaded videos
157
- @app.route('/video_stream/<source>/<filename>')
158
- def video_stream(source, filename):
159
- if source == 'demo':
160
- path = VIDEO_PATHS.get(filename)
161
- elif source == 'upload':
162
- path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
163
- else:
164
- return "Invalid source", 404
165
-
166
- if not path or not os.path.exists(path):
167
- return "Video not found", 404
168
-
169
- def generate():
170
- with open(path, "rb") as f:
171
- while chunk := f.read(1024 * 1024):
172
- yield chunk
173
-
174
- return Response(generate(), mimetype="video/mp4")
175
-
176
- @socketio.on('start_processing')
177
- def handle_start_processing(data):
178
- global thread
179
- with thread_lock:
180
- if thread is None:
181
- stop_event.clear()
182
- source = data.get('source')
183
- filename = data.get('filename')
184
- video_path = None
185
-
186
- if source == 'demo':
187
- video_path = VIDEO_PATHS.get(filename)
188
- elif source == 'upload':
189
- video_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
190
-
191
- if video_path and os.path.exists(video_path):
192
- print(f"[INFO] Starting processing for {filename} from {source}")
193
- thread = socketio.start_background_task(target=video_processing_task, video_path=video_path)
194
- else:
195
- socketio.emit('processing_error', {'error': f'Video file not found!'})
196
-
197
- @socketio.on('reset_system')
198
- def handle_reset():
199
- global thread
200
- with thread_lock:
201
- if thread is not None:
202
- stop_event.set()
203
- socketio.emit('system_reset_confirm')
204
-
205
- if __name__ == '__main__':
206
- print("[INFO] Starting Flask server...")
207
- socketio.run(app, debug=True)
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import torch
4
+ import numpy as np
5
+ import time
6
+ from datetime import datetime
7
+ import threading
8
+ import base64
9
+ from werkzeug.utils import secure_filename
10
+
11
+ from flask import Flask, render_template, Response, request, jsonify
12
+ from flask_socketio import SocketIO
13
+
14
+ # CHANGED: Imports are now flat
15
+ from load_model import load_models
16
+ from utils import build_transforms
17
+ from TorchUtils import get_torch_device
18
+ from yolo_detection import analyze_video_with_yolo
19
+
20
+ # ---- App Setup ----
21
+ # CHANGED: Told Flask to look for templates/static files in the current folder ('.')
22
+ app = Flask(__name__, template_folder='.', static_folder='.')
23
+ app.config['SECRET_KEY'] = 'your_secret_key!'
24
+
25
+ # --- File Paths (CHANGED to relative paths) ---
26
+ UPLOAD_FOLDER = 'uploads'
27
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
28
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
29
+
30
+ SAVE_DIR = "outputs/anomaly_frames"
31
+ os.makedirs(SAVE_DIR, exist_ok=True)
32
+
33
+ # CHANGED: All paths are now relative to this app.py file
34
+ FEATURE_EXTRACTOR_PATH = "models/c3d.pickle"
35
+ AD_MODEL_PATH = "models/epoch_8000.pt"
36
+ YOLO_MODEL_PATH = "models/yolo_my_model.pt"
37
+
38
+ # CHANGED: All video paths are now relative
39
+ VIDEO_PATHS = {
40
+ "Abuse": "demo_videos/Abuse.mp4",
41
+ "Arrest": "demo_videos/Arrest.mp4",
42
+ "Arson": "demo_videos/Arson.mp4",
43
+ "Assault": "demo_videos/Assault.mp4",
44
+ "Burglary": "demo_videos/Burglary.mp4",
45
+ "Explosion": "demo_videos/Explosion.mp4",
46
+ "Fighting": "demo_videos/Fighting.mp4",
47
+ "RoadAccidents": "demo_videos/RoadAccidents.mp4",
48
+ "Robbery": "demo_videos/Robbery.mp4",
49
+ "Shooting": "demo_videos/Shooting.mp4",
50
+ "Shoplifting": "demo_videos/Shoplifting.mp4",
51
+ "Stealing": "demo_videos/Stealing.mp4",
52
+ "Vandalism": "demo_videos/Vandalism.mp4",
53
+ "Normal": "demo_videos/Normal.mp4"
54
+ }
55
+
56
+ # ---- Global Config & Model Loading ----
57
+ print("[INFO] Loading models...")
58
+ DEVICE = get_torch_device()
59
+ anomaly_detector, feature_extractor = load_models(
60
+ FEATURE_EXTRACTOR_PATH, AD_MODEL_PATH, features_method="c3d", device=DEVICE
61
+ )
62
+ feature_extractor.eval()
63
+ anomaly_detector.eval()
64
+ TRANSFORMS = build_transforms(mode="c3d")
65
+ ANOMALY_THRESHOLD = 0.5
66
+ print("[INFO] Models loaded successfully.")
67
+
68
+
69
+ # --- Threading control ---
70
+ thread = None
71
+ thread_lock = threading.Lock()
72
+ stop_event = threading.Event()
73
+
74
+ # (The `smooth_score` and `video_processing_task` functions remain unchanged)
75
+ def smooth_score(scores, new_score, window=5):
76
+ scores.append(new_score)
77
+ if len(scores) > window:
78
+ scores.pop(0)
79
+ return float(np.mean(scores))
80
+
81
+ def video_processing_task(video_path):
82
+ global thread
83
+ try:
84
+ cap = cv2.VideoCapture(video_path)
85
+ if not cap.isOpened():
86
+ socketio.emit('processing_error', {'error': f'Could not open video file.'})
87
+ return
88
+ frame_buffer = []
89
+ last_save_time = 0
90
+ recent_scores = []
91
+ FRAME_SKIP = 4
92
+ frame_count = 0
93
+ while cap.isOpened() and not stop_event.is_set():
94
+ socketio.sleep(0.001)
95
+ ret, frame = cap.read()
96
+ if not ret: break
97
+ frame_count += 1
98
+ if frame_count % (FRAME_SKIP + 1) != 0: continue
99
+ frame_buffer.append(frame.copy())
100
+ if len(frame_buffer) == 16:
101
+ frames_resized = [cv2.resize(f, (112, 112)) for f in frame_buffer]
102
+ clip_np = np.array(frames_resized, dtype=np.uint8)
103
+ clip_torch = torch.from_numpy(clip_np)
104
+ clip_torch = TRANSFORMS(clip_torch)
105
+ clip_torch = clip_torch.unsqueeze(0).to(DEVICE)
106
+ with torch.no_grad():
107
+ features = feature_extractor(clip_torch).detach()
108
+ score_tensor = anomaly_detector(features).detach()
109
+ score = float(score_tensor.view(-1)[0].item())
110
+ score = smooth_score(recent_scores, score)
111
+ score = float(np.clip(score, 0, 1))
112
+ socketio.emit('update_graph', {'score': score})
113
+ if score > ANOMALY_THRESHOLD and (time.time() - last_save_time) >= 30:
114
+ last_save_time = time.time()
115
+ socketio.emit('update_status', {'status': 'Anomaly detected! Saving clip...'})
116
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
117
+ clip_dir = os.path.join(SAVE_DIR, f"anomaly_{timestamp}")
118
+ os.makedirs(clip_dir, exist_ok=True)
119
+ first_frame_path = os.path.join(clip_dir, "anomaly_frame.jpg")
120
+ cv2.imwrite(first_frame_path, frame_buffer[0])
121
+ try:
122
+ yolo_result = analyze_video_with_yolo(first_frame_path, model_path=YOLO_MODEL_PATH, return_class=True)
123
+ socketio.emit('update_yolo_text', {'text': f"YOLO Class: {yolo_result}"})
124
+ _, buffer = cv2.imencode('.jpg', frame_buffer[0])
125
+ b64_str = base64.b64encode(buffer).decode('utf-8')
126
+ socketio.emit('update_yolo_image', {'image_data': b64_str})
127
+ except Exception as e:
128
+ socketio.emit('update_yolo_text', {'text': f'YOLO Error: {e}'})
129
+ frame_buffer.clear()
130
+ cap.release()
131
+ if not stop_event.is_set():
132
+ socketio.emit('processing_finished', {'message': 'Video finished.'})
133
+ finally:
134
+ with thread_lock:
135
+ thread = None
136
+ stop_event.clear()
137
+
138
+ @app.route('/')
139
+ def index():
140
+ # This will now look for 'index.html' in the same folder
141
+ return render_template('index.html', anomaly_names=VIDEO_PATHS.keys())
142
+
143
+ @app.route('/upload', methods=['POST'])
144
+ def upload_file():
145
+ if 'video' not in request.files:
146
+ return jsonify({'error': 'No video file found'}), 400
147
+ file = request.files['video']
148
+ if file.filename == '':
149
+ return jsonify({'error': 'No video file selected'}), 400
150
+ if file:
151
+ filename = secure_filename(file.filename)
152
+ unique_filename = f"{datetime.now().strftime('%Y%m%d%HM%S')}_{filename}"
153
+ save_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
154
+ file.save(save_path)
155
+ return jsonify({'success': True, 'filename': unique_filename})
156
+ return jsonify({'error': 'File upload failed'}), 500
157
+
158
+ @app.route('/video_stream/<source>/<filename>')
159
+ def video_stream(source, filename):
160
+ if source == 'demo':
161
+ path = VIDEO_PATHS.get(filename)
162
+ elif source == 'upload':
163
+ # Need to URL-decode the filename
164
+ safe_filename = secure_filename(filename)
165
+ path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)
166
+ else:
167
+ return "Invalid source", 404
168
+
169
+ if not path or not os.path.exists(path):
170
+ return "Video not found", 404
171
+
172
+ def generate():
173
+ with open(path, "rb") as f:
174
+ while chunk := f.read(1024 * 1024):
175
+ yield chunk
176
+
177
+ return Response(generate(), mimetype="video/mp4")
178
+
179
+ @socketio.on('start_processing')
180
+ def handle_start_processing(data):
181
+ global thread
182
+ with thread_lock:
183
+ if thread is None:
184
+ stop_event.clear()
185
+ source = data.get('source')
186
+ filename = data.get('filename')
187
+ video_path = None
188
+
189
+ if source == 'demo':
190
+ video_path = VIDEO_PATHS.get(filename)
191
+ elif source == 'upload':
192
+ safe_filename = secure_filename(filename)
193
+ video_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)
194
+
195
+ if video_path and os.path.exists(video_path):
196
+ print(f"[INFO] Starting processing for {filename} from {source}")
197
+ thread = socketio.start_background_task(target=video_processing_task, video_path=video_path)
198
+ else:
199
+ socketio.emit('processing_error', {'error': f'Video file not found!'})
200
+
201
+ @socketio.on('reset_system')
202
+ def handle_reset():
203
+ global thread
204
+ with thread_lock:
205
+ if thread is not None:
206
+ stop_event.set()
207
+ socketio.emit('system_reset_confirm')
208
+
209
+ if __name__ == '__main__':
210
+ print("[INFO] Starting Flask server...")
211
+ # Using 0.0.0.0 is still correct for services like Hugging Face
212
+ socketio.run(app, debug=True, host='0.0.0.0')