PrashanthB461 commited on
Commit
dc66d57
·
verified ·
1 Parent(s): 376968c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -113
app.py CHANGED
@@ -1,127 +1,246 @@
1
- import { LightningElement, api } from 'lwc';
2
- import uploadVideo from '@salesforce/apex/VideoFileUpload.uploadVideoFile';
3
- import { ShowToastEvent } from 'lightning/platformShowToastEvent';
4
-
5
- export default class VideoUploadWithAI extends LightningElement {
6
- @api recordId;
7
- videoFile;
8
- base64FileData;
9
- fileName;
10
- complianceScore;
11
- violationsFound = false;
12
- violationsList = [];
13
- snapshots = [];
14
- pdfReportUrl;
15
- isLoading = false;
16
- isDragging = false;
17
-
18
- handleFileChange(event) {
19
- const file = event.target.files[0];
20
- if (file) {
21
- this.fileName = file.name;
22
- this.convertToBase64(file);
23
- } else {
24
- this.showToast('Error', 'No video file selected.', 'error');
25
- }
26
- }
27
 
28
- convertToBase64(file) {
29
- const reader = new FileReader();
30
- reader.onloadend = () => {
31
- this.base64FileData = reader.result.split(',')[1];
32
- };
33
- reader.readAsDataURL(file);
34
- }
 
35
 
36
- handleDragOver(event) {
37
- event.preventDefault();
38
- this.isDragging = true;
39
- }
 
 
40
 
41
- handleDragEnter(event) {
42
- event.preventDefault();
43
- this.isDragging = true;
44
- }
 
45
 
46
- handleDragLeave(event) {
47
- event.preventDefault();
48
- this.isDragging = false;
49
- }
 
 
 
 
 
 
50
 
51
- handleDrop(event) {
52
- event.preventDefault();
53
- this.isDragging = false;
54
- const file = event.dataTransfer.files[0];
55
- if (file && file.type.startsWith('video/')) {
56
- this.fileName = file.name;
57
- this.convertToBase64(file);
58
- } else {
59
- this.showToast('Error', 'Please drop a valid video file.', 'error');
60
- }
61
- }
62
 
63
- get dropzoneClass() {
64
- return this.isDragging
65
- ? 'slds-box slds-box_small dropzone dragging'
66
- : 'slds-box slds-box_small dropzone';
67
- }
68
 
69
- async handleSubmit() {
70
- if (!this.base64FileData) {
71
- this.showToast('Error', 'Please select a video file.', 'error');
72
- return;
73
- }
74
 
75
- if (!this.recordId) {
76
- this.showToast('Error', 'No record ID provided. Please ensure this component is placed on a record page.', 'error');
77
- return;
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
- this.isLoading = true;
81
- try {
82
- const result = await uploadVideo({
83
- reportId: this.recordId,
84
- fileData: this.base64FileData,
85
- fileName: this.fileName
86
- });
87
- console.log('Apex Result:', JSON.stringify(result));
88
-
89
- if (result.status === 'Reviewed') {
90
- this.complianceScore = result.complianceScore ? parseFloat(result.complianceScore) : 0;
91
- this.violationsList = result.violationsList || [];
92
- this.violationsFound = this.violationsList.length > 0;
93
- this.snapshots = result.snapshots || [];
94
- this.pdfReportUrl = result.pdfReportUrl || '';
95
- this.showToast('Success', 'Video processed successfully.', 'success');
96
- } else {
97
- let errorMessage = result.status || 'Unknown error occurred.';
98
- if (errorMessage.includes('INSUFFICIENT_ACCESS') || errorMessage.includes('PERMISSION')) {
99
- errorMessage += ' - Please contact your Salesforce admin to grant necessary permissions.';
100
- }
101
- this.showToast('Error', errorMessage, 'error');
102
- }
103
- } catch (error) {
104
- console.error('Error in handleSubmit:', error);
105
- let errorMessage = 'Failed to process video: ' + (error.message || 'Unknown error.');
106
- if (error.message.includes('INSUFFICIENT_ACCESS') || error.message.includes('PERMISSION')) {
107
- errorMessage += ' - Please contact your Salesforce admin to grant necessary permissions.';
108
- }
109
- this.showToast('Error', errorMessage, 'error');
110
- } finally {
111
- this.isLoading = false;
112
  }
113
- }
114
 
115
- handleDownloadReport() {
116
- if (this.pdfReportUrl) {
117
- window.open(this.pdfReportUrl, '_blank');
118
- } else {
119
- this.showToast('Error', 'No PDF report available to download.', 'error');
 
 
 
120
  }
121
- }
122
 
123
- showToast(title, message, variant) {
124
- const event = new ShowToastEvent({ title, message, variant });
125
- this.dispatchEvent(event);
 
 
 
 
 
 
 
126
  }
127
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import gradio as gr
4
+ import torch
5
+ import numpy as np
6
+ from ultralytics import YOLO
7
+ import time
8
+ from reportlab.lib.pagesizes import letter
9
+ from reportlab.pdfgen import canvas
10
+ from reportlab.lib.utils import ImageReader
11
+ import base64
12
+ from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # ==========================
15
+ # Configuration
16
+ # ==========================
17
+ DEFAULT_MODEL_PATH = "models/yolov8_safety.pt"
18
+ FALLBACK_MODEL = "yolov8n.pt"
19
+ MODEL_PATH = os.getenv("SAFETY_MODEL_PATH", DEFAULT_MODEL_PATH)
20
+ OUTPUT_DIR = "output"
21
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
22
 
23
+ VIOLATION_LABELS = {
24
+ 0: "no_helmet",
25
+ 1: "no_harness",
26
+ 2: "unsafe_posture",
27
+ 3: "unsafe_zone"
28
+ }
29
 
30
+ # ==========================
31
+ # Device Setup
32
+ # ==========================
33
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
34
+ print(f"✅ Using device: {device}")
35
 
36
+ # ==========================
37
+ # Load Model
38
+ # ==========================
39
+ try:
40
+ selected_model = MODEL_PATH if os.path.isfile(MODEL_PATH) else FALLBACK_MODEL
41
+ model = YOLO(selected_model)
42
+ print(f"✅ Model loaded: {selected_model}")
43
+ except Exception as e:
44
+ print(f"❌ Failed to load model: {e}")
45
+ raise
46
 
47
+ # ==========================
48
+ # Video Processing
49
+ # ==========================
50
+ def process_video(video_data, frame_skip=5, max_frames=100):
51
+ try:
52
+ print("Processing video data...")
53
+ # Save uploaded video data to a temporary file
54
+ video_path = os.path.join(OUTPUT_DIR, f"temp_{int(time.time())}.mp4")
55
+ with open(video_path, "wb") as f:
56
+ f.write(video_data)
57
+ print(f"Video saved to {video_path}")
58
 
59
+ video = cv2.VideoCapture(video_path)
60
+ if not video.isOpened():
61
+ raise ValueError("Could not open video file.")
 
 
62
 
63
+ frame_count = 0
64
+ violations = []
65
+ snapshots = []
66
+ processed_frame_count = 0
67
+ start_time = time.time()
68
 
69
+ while True:
70
+ ret, frame = video.read()
71
+ if not ret:
72
+ break
73
+
74
+ if frame_count % frame_skip != 0:
75
+ frame_count += 1
76
+ continue
77
+
78
+ # Model inference
79
+ results = model(frame, device=device)
80
+
81
+ for result in results:
82
+ for box in result.boxes:
83
+ cls = int(box.cls)
84
+ conf = float(box.conf)
85
+ xywh = box.xywh.cpu().numpy()[0]
86
+
87
+ label = VIOLATION_LABELS.get(cls, f"class_{cls}")
88
+ violation = {
89
+ "frame": frame_count,
90
+ "violation": label,
91
+ "confidence": round(conf, 2),
92
+ "bounding_box": [round(x, 2) for x in xywh],
93
+ "timestamp": frame_count / video.get(cv2.CAP_PROP_FPS)
94
+ }
95
+ violations.append(violation)
96
+
97
+ # Save snapshot
98
+ snapshot_path = os.path.join(OUTPUT_DIR, f"snapshot_{frame_count}_{label}.jpg")
99
+ cv2.imwrite(snapshot_path, frame)
100
+ snapshots.append({
101
+ "violation": label,
102
+ "frame": frame_count,
103
+ "snapshot_url": f"https://huggingface.co/spaces/PrashanthB461/AI_Safety_Demo1/output/{os.path.basename(snapshot_path)}"
104
+ })
105
+
106
+ frame_count += 1
107
+ processed_frame_count += 1
108
+
109
+ if processed_frame_count >= max_frames:
110
+ break
111
+
112
+ if time.time() - start_time > 30:
113
+ print("⏰ Exceeded 30 seconds of processing time.")
114
+ break
115
 
116
+ video.release()
117
+ os.remove(video_path)
118
+
119
+ score = calculate_safety_score(violations)
120
+ pdf_base64 = generate_pdf_report(violations, snapshots, score)
121
+
122
+ return {
123
+ "violations": violations,
124
+ "snapshots": snapshots,
125
+ "score": score,
126
+ "pdf_base64": pdf_base64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  }
 
128
 
129
+ except Exception as e:
130
+ print(f"❌ Error processing video: {e}")
131
+ return {
132
+ "violations": [],
133
+ "snapshots": [],
134
+ "score": 0,
135
+ "pdf_base64": "",
136
+ "error": str(e)
137
  }
 
138
 
139
+ # ==========================
140
+ # Safety Score Calculation
141
+ # ==========================
142
+ def calculate_safety_score(violations):
143
+ base_score = 100
144
+ penalties = {
145
+ "no_helmet": 25,
146
+ "no_harness": 30,
147
+ "unsafe_posture": 20,
148
+ "unsafe_zone": 25
149
  }
150
+ for v in violations:
151
+ base_score -= penalties.get(v["violation"], 0)
152
+ return max(base_score, 0)
153
+
154
+ # ==========================
155
+ # PDF Report Generation
156
+ # ==========================
157
+ def generate_pdf_report(violations, snapshots, score):
158
+ try:
159
+ pdf_path = os.path.join(OUTPUT_DIR, f"report_{int(time.time())}.pdf")
160
+ c = canvas.Canvas(pdf_path, pagesize=letter)
161
+ width, height = letter
162
+
163
+ # Title
164
+ c.setFont("Helvetica-Bold", 16)
165
+ c.drawString(50, height - 50, "Worksite Safety Compliance Report")
166
+
167
+ # Compliance Score
168
+ c.setFont("Helvetica", 12)
169
+ c.drawString(50, height - 80, f"Compliance Score: {score}%")
170
+
171
+ # Violations Table
172
+ y = height - 120
173
+ c.setFont("Helvetica-Bold", 12)
174
+ c.drawString(50, y, "Detected Violations:")
175
+ y -= 20
176
+
177
+ for v in violations:
178
+ c.setFont("Helvetica", 10)
179
+ text = f"Violation: {v['violation']}, Timestamp: {v['timestamp']:.2f}s, Confidence: {v['confidence']}"
180
+ c.drawString(50, y, text)
181
+ y -= 20
182
+
183
+ # Add snapshot if available
184
+ snapshot = next((s for s in snapshots if s["frame"] == v["frame"] and s["violation"] == v["violation"]), None)
185
+ if snapshot and os.path.exists(snapshot["snapshot_url"].split('/')[-1]):
186
+ img = ImageReader(snapshot["snapshot_url"].split('/')[-1])
187
+ c.drawImage(img, 50, y - 100, width=200, height=150)
188
+ y -= 170
189
+
190
+ if y < 50:
191
+ c.showPage()
192
+ y = height - 50
193
+
194
+ c.save()
195
+ print(f"PDF generated at {pdf_path}")
196
+
197
+ # Convert PDF to base64
198
+ with open(pdf_path, "rb") as f:
199
+ pdf_base64 = base64.b64encode(f.read()).decode('utf-8')
200
+
201
+ # Clean up
202
+ os.remove(pdf_path)
203
+ print("PDF converted to base64 and file removed")
204
+ return pdf_base64
205
+ except Exception as e:
206
+ print(f"❌ Error generating PDF: {e}")
207
+ return ""
208
+
209
+ # ==========================
210
+ # Gradio Interface
211
+ # ==========================
212
+ def gradio_interface(video_file):
213
+ try:
214
+ if not video_file:
215
+ return {"error": "Please upload a video file."}, "", "", []
216
+
217
+ with open(video_file, "rb") as f:
218
+ video_data = f.read()
219
+
220
+ result = process_video(video_data)
221
+ return (
222
+ result["violations"],
223
+ f"Safety Score: {result['score']}%",
224
+ result["pdf_base64"],
225
+ result["snapshots"]
226
+ )
227
+ except Exception as e:
228
+ print(f"❌ Error in gradio_interface: {e}")
229
+ return {"error": str(e)}, "", "", []
230
+
231
+ interface = gr.Interface(
232
+ fn=gradio_interface,
233
+ inputs=gr.Video(label="Upload Site Video"),
234
+ outputs=[
235
+ gr.JSON(label="Detected Safety Violations"),
236
+ gr.Textbox(label="Compliance Score"),
237
+ gr.Textbox(label="PDF Base64 (for API use)"),
238
+ gr.JSON(label="Snapshots")
239
+ ],
240
+ title="Worksite Safety Violation Analyzer",
241
+ description="Upload short site videos to detect safety violations (e.g., no helmet, no harness, unsafe posture)."
242
+ )
243
+
244
+ if __name__ == "__main__":
245
+ print("🚀 Launching Safety Analyzer App...")
246
+ interface.launch()