foreversheikh commited on
Commit
f83e10a
·
verified ·
1 Parent(s): 5643cd0

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +207 -0
app.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)