assentian1970 commited on
Commit
63181af
·
verified ·
1 Parent(s): dadd243

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +352 -270
app.py CHANGED
@@ -1,66 +1,39 @@
 
1
  import spaces
2
  import torch
 
 
 
 
 
 
3
  from datetime import datetime
 
4
  from transformers import AutoModel, AutoTokenizer
5
- import gradio as gr
6
  from PIL import Image
7
  from decord import VideoReader, cpu
8
  import os
9
  import gc
 
10
  import tempfile
11
  from ultralytics import YOLO
12
  import numpy as np
13
  import cv2
14
- from modelscope.hub.snapshot_download import snapshot_download
15
- from ultralytics.nn.modules import Conv, C2f
16
- from torch import nn
17
- import ultralytics.nn.modules as modules
18
-
19
- # Add custom C3k2 module definition
20
- class C3k2(nn.Module):
21
- def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
22
- super().__init__()
23
- c_ = int(c2 * e)
24
- self.cv1 = Conv(c1, c_, 1, 1)
25
- self.cv2 = Conv(c1, c_, 1, 1)
26
- self.cv3 = Conv(2 * c_, c2, 1)
27
- self.m = nn.Sequential(*(C2f(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
28
-
29
- def forward(self, x):
30
- return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))
31
 
32
- # Patch the Ultralytics module
33
- modules.C3k2 = C3k2
34
 
35
- # Fix GLIBCXX dependency
36
- os.environ['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH'
37
 
38
- # Initialize GPU
39
- @spaces.GPU
40
- def initialize_gpu():
41
- if torch.cuda.is_available():
42
  torch.randn(10).cuda()
43
- initialize_gpu()
44
-
45
- # Load YOLO model with error handling
46
- try:
47
- YOLO_MODEL = YOLO('best_yolov11.pt')
48
- except Exception as e:
49
- raise RuntimeError(f"YOLO model loading failed: {str(e)}")
50
-
51
- # Model configuration
52
- MODEL_NAME = 'iic/mPLUG-Owl3-7B-240728'
53
- try:
54
- model_dir = snapshot_download(MODEL_NAME,
55
- cache_dir='./models',
56
- revision='main')
57
- except Exception as e:
58
- raise RuntimeError(f"Model download failed: {str(e)}")
59
-
60
- # Device setup
61
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
62
 
63
- # File validation
64
  IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
65
  VIDEO_EXTENSIONS = {'.mp4', '.mkv', '.mov', '.avi', '.flv', '.wmv', '.webm', '.m4v'}
66
 
@@ -73,293 +46,402 @@ def is_image(filename):
73
  def is_video(filename):
74
  return get_file_extension(filename) in VIDEO_EXTENSIONS
75
 
76
- @spaces.GPU
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  def load_model_and_tokenizer():
78
- """Load 8-bit quantized model"""
79
  try:
80
- torch.cuda.empty_cache()
81
- gc.collect()
 
82
 
83
  model = AutoModel.from_pretrained(
84
- model_dir,
85
  attn_implementation='sdpa',
86
  trust_remote_code=True,
87
- load_in_8bit=True,
88
- device_map="auto",
89
- torch_dtype=torch.float16
90
  )
91
-
92
  tokenizer = AutoTokenizer.from_pretrained(
93
- model_dir,
94
  trust_remote_code=True
95
  )
96
- processor = model.init_processor(tokenizer)
97
  model.eval()
 
98
  return model, tokenizer, processor
99
  except Exception as e:
100
- print(f"Model loading error: {str(e)}")
101
  raise
102
 
103
- def process_yolo_results(results):
104
- """Process YOLO detection results"""
105
- machinery_mapping = {
106
- 'tower_crane': "Tower Crane",
107
- 'mobile_crane': "Mobile Crane",
108
- 'compactor': "Compactor/Roller",
109
- 'roller': "Compactor/Roller",
110
- 'bulldozer': "Bulldozer",
111
- 'dozer': "Bulldozer",
112
- 'excavator': "Excavator",
113
- 'dump_truck': "Dump Truck",
114
- 'truck': "Dump Truck",
115
- 'concrete_mixer_truck': "Concrete Mixer",
116
- 'loader': "Loader",
117
- 'pump_truck': "Pump Truck",
118
- 'pile_driver': "Pile Driver",
119
- 'grader': "Grader",
120
- 'other_vehicle': "Other Vehicle"
121
- }
122
-
123
- counts = {"Worker": 0, **{v: 0 for v in machinery_mapping.values()}}
124
-
125
- for r in results:
126
- for box in r.boxes:
127
- if box.conf.item() < 0.5:
128
- continue
129
-
130
- cls_name = YOLO_MODEL.names[int(box.cls.item())].lower()
131
- if cls_name == 'worker':
132
- counts["Worker"] += 1
133
- continue
134
-
135
- for key, value in machinery_mapping.items():
136
- if key in cls_name:
137
- counts[value] += 1
138
- break
 
139
 
140
- return counts["Worker"], sum(counts.values()) - counts["Worker"], counts
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
- @spaces.GPU
143
  def detect_people_and_machinery(media_path):
144
- """GPU-accelerated detection"""
145
  try:
146
- max_people = 0
147
- max_machines = {k: 0 for k in [
148
- "Tower Crane", "Mobile Crane", "Compactor/Roller", "Bulldozer",
149
- "Excavator", "Dump Truck", "Concrete Mixer", "Loader",
150
- "Pump Truck", "Pile Driver", "Grader", "Other Vehicle"
151
- ]}
152
-
 
 
 
 
 
 
 
 
153
  if isinstance(media_path, str) and is_video(media_path):
154
  cap = cv2.VideoCapture(media_path)
155
  fps = cap.get(cv2.CAP_PROP_FPS)
156
  sample_rate = max(1, int(fps))
157
-
158
  while cap.isOpened():
159
  ret, frame = cap.read()
160
  if not ret:
161
  break
162
-
163
- if cap.get(cv2.CAP_PROP_POS_FRAMES) % sample_rate == 0:
164
  results = YOLO_MODEL(frame)
165
- people, machines, types = process_yolo_results(results)
166
-
167
- max_people = max(max_people, people)
168
- for k in max_machines:
169
- max_machines[k] = max(max_machines[k], types.get(k, 0))
170
-
171
  cap.release()
172
  else:
173
- img = cv2.imread(media_path) if isinstance(media_path, str) else cv2.cvtColor(np.array(media_path), cv2.COLOR_RGB2BGR)
 
 
 
174
  results = YOLO_MODEL(img)
175
- max_people, _, types = process_yolo_results(results)
176
- for k in max_machines:
177
- max_machines[k] = types.get(k, 0)
178
-
179
- filtered = {k: v for k, v in max_machines.items() if v > 0}
180
- return max_people, sum(filtered.values()), filtered
181
-
182
  except Exception as e:
183
- print(f"Detection error: {str(e)}")
184
  return 0, 0, {}
185
 
186
- @spaces.GPU
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  def analyze_video_activities(video_path):
188
- """Video analysis with chunk processing"""
189
  try:
190
- model, tokenizer, processor = load_model_and_tokenizer()
191
- responses = []
192
-
193
- vr = VideoReader(video_path, ctx=cpu(0))
194
- frame_step = max(1, int(vr.get_avg_fps()))
195
- total_frames = len(vr)
196
-
197
- for i in range(0, total_frames, 16):
198
- end_idx = min(i+16, total_frames)
199
- frames = [Image.fromarray(vr[j].asnumpy()) for j in range(i, end_idx)]
200
-
201
- inputs = processor(
202
- [{"role": "user", "content": "Analyze construction activities", "video_frames": frames}],
203
- videos=[frames]
204
- ).to(DEVICE)
205
-
206
- response = model.generate(**inputs, max_new_tokens=200)
207
- responses.append(response[0])
208
-
209
- del frames, inputs
210
  torch.cuda.empty_cache()
211
-
212
- del model, tokenizer, processor
213
- return "\n".join(responses)
214
-
215
  except Exception as e:
216
- print(f"Video analysis error: {str(e)}")
217
- return "Activity analysis unavailable"
218
 
219
- @spaces.GPU
220
- def analyze_image_activities(image_path):
221
- """Image analysis pipeline"""
222
  try:
223
- model, tokenizer, processor = load_model_and_tokenizer()
224
- image = Image.open(image_path).convert("RGB")
225
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  inputs = processor(
227
- [{"role": "user", "content": "Analyze construction site", "images": [image]}],
228
- images=[image]
229
- ).to(DEVICE)
230
-
231
- response = model.generate(**inputs, max_new_tokens=200)
232
-
233
- del model, tokenizer, processor, image, inputs
234
- torch.cuda.empty_cache()
 
 
 
235
  return response[0]
236
-
237
  except Exception as e:
238
- print(f"Image analysis error: {str(e)}")
239
- return "Activity analysis unavailable"
240
 
241
- @spaces.GPU
242
- def annotate_video_with_bboxes(video_path):
243
- """Video annotation with detection overlay"""
244
  try:
245
- cap = cv2.VideoCapture(video_path)
246
- fps = cap.get(cv2.CAP_PROP_FPS)
247
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
248
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
249
-
250
- temp_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
251
- writer = cv2.VideoWriter(temp_file.name, cv2.VideoWriter_fourcc(*'mp4v'), fps, (width, height))
252
-
253
- frame_count = 0
254
- while cap.isOpened():
255
- ret, frame = cap.read()
256
- if not ret:
257
- break
258
-
259
- if frame_count % 5 == 0:
260
- results = YOLO_MODEL(frame)
261
- counts = {}
262
-
263
- for r in results:
264
- for box in r.boxes:
265
- if box.conf.item() < 0.5:
266
- continue
267
-
268
- cls_id = int(box.cls.item())
269
- class_name = YOLO_MODEL.names[cls_id]
270
- counts[class_name] = counts.get(class_name, 0) + 1
271
-
272
- x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
273
- cv2.rectangle(frame, (x1, y1), (x2, y2), (0,255,0), 2)
274
- cv2.putText(frame, f"{class_name} {box.conf.item():.2f}",
275
- (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
276
-
277
- summary = ", ".join([f"{k}:{v}" for k,v in counts.items()])
278
- cv2.putText(frame, summary, (10,30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 2)
279
-
280
- writer.write(frame)
281
- frame_count += 1
282
-
283
- cap.release()
284
- writer.release()
285
- return temp_file.name
286
-
287
  except Exception as e:
288
- print(f"Video annotation error: {str(e)}")
289
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
- def process_diary(day, date, media):
292
- """Main processing pipeline"""
 
 
 
293
  try:
294
- if not media:
295
- return [day, date, "No data", "No data", "No data", "No data", None]
296
-
297
- with tempfile.NamedTemporaryFile(delete=False) as tmp:
298
- tmp.write(media.read())
299
- media_path = tmp.name
300
-
301
- detected_people, detected_machinery, machine_types = detect_people_and_machinery(media_path)
302
- annotated_video = None
303
-
304
- try:
305
- if is_image(media.name):
306
- activities = analyze_image_activities(media_path)
307
  else:
308
- activities = analyze_video_activities(media_path)
309
- annotated_video = annotate_video_with_bboxes(media_path)
310
- except Exception as e:
311
- activities = f"Analysis error: {str(e)}"
312
-
313
- os.remove(media_path)
314
- return [
315
- day,
316
- date,
317
- str(detected_people),
318
- str(detected_machinery),
319
- ", ".join([f"{k}:{v}" for k,v in machine_types.items()]),
320
- activities,
321
- annotated_video
322
- ]
323
-
324
  except Exception as e:
325
- print(f"Processing error: {str(e)}")
326
- return [day, date, "Error", "Error", "Error", "Error", None]
327
 
328
- # Gradio Interface
329
- with gr.Blocks(title="Digital Site Diary", css="video {height: auto !important;}") as demo:
330
- gr.Markdown("# 🏗️ Digital Construction Diary")
331
-
332
  with gr.Row():
333
  with gr.Column():
334
- gr.Markdown("### Site Details")
335
- day = gr.Textbox(label="Day Number", value="1")
336
- date = gr.Textbox(label="Date", value=datetime.now().strftime("%Y-%m-%d"))
337
- media = gr.File(label="Upload Media", file_types=["image", "video"])
338
- submit_btn = gr.Button("Generate Report", variant="primary")
339
-
 
 
 
 
 
 
 
 
 
 
 
 
340
  with gr.Column():
341
- gr.Markdown("### Safety Report")
342
  model_day = gr.Textbox(label="Day")
343
  model_date = gr.Textbox(label="Date")
344
- model_people = gr.Textbox(label="Worker Count")
345
- model_machinery = gr.Textbox(label="Machinery Count")
346
- model_machinery_types = gr.Textbox(label="Machinery Breakdown")
347
- model_activities = gr.Textbox(label="Activity Analysis", lines=4)
348
- model_video = gr.Video(label="Safety Annotations")
349
-
350
  submit_btn.click(
351
- process_diary,
352
- inputs=[day, date, media],
353
  outputs=[
354
  model_day,
355
  model_date,
356
  model_people,
357
- model_machinery,
358
  model_machinery_types,
359
- model_activities,
360
- model_video
361
  ]
362
  )
363
 
364
  if __name__ == "__main__":
365
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ #!/usr/bin/env python
2
  import spaces
3
  import torch
4
+ @spaces.GPU
5
+ def debug():
6
+ torch.randn(10).cuda()
7
+ debug()
8
+
9
+ import gradio as gr
10
  from datetime import datetime
11
+ import torch
12
  from transformers import AutoModel, AutoTokenizer
13
+ from modelscope.hub.snapshot_download import snapshot_download
14
  from PIL import Image
15
  from decord import VideoReader, cpu
16
  import os
17
  import gc
18
+ import io
19
  import tempfile
20
  from ultralytics import YOLO
21
  import numpy as np
22
  import cv2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ # Load YOLOv11 model (update the path as needed)
25
+ YOLO_MODEL = YOLO('best_yolov11.pt')
26
 
27
+ # Check if CUDA is available
28
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
29
 
30
+ # Initialize GPU if available (already done by debug() above)
31
+ if DEVICE == "cuda":
32
+ def init_debug():
 
33
  torch.randn(10).cuda()
34
+ init_debug()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ # File type validation
37
  IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
38
  VIDEO_EXTENSIONS = {'.mp4', '.mkv', '.mov', '.avi', '.flv', '.wmv', '.webm', '.m4v'}
39
 
 
46
  def is_video(filename):
47
  return get_file_extension(filename) in VIDEO_EXTENSIONS
48
 
49
+ # Model configuration
50
+ MODEL_NAME = 'iic/mPLUG-Owl3-7B-240728'
51
+ MODEL_CACHE_DIR = os.getenv('TRANSFORMERS_CACHE', './models')
52
+ os.makedirs(MODEL_CACHE_DIR, exist_ok=True)
53
+
54
+ # Download and cache the model
55
+ try:
56
+ model_path = snapshot_download(MODEL_NAME, cache_dir=MODEL_CACHE_DIR)
57
+ except Exception as e:
58
+ print(f"Error downloading model: {str(e)}")
59
+ model_path = os.path.join(MODEL_CACHE_DIR, MODEL_NAME)
60
+
61
+ MAX_NUM_FRAMES = 32
62
+
63
  def load_model_and_tokenizer():
64
+ """Load a fresh instance of the model and tokenizer"""
65
  try:
66
+ if DEVICE == "cuda":
67
+ torch.cuda.empty_cache()
68
+ gc.collect()
69
 
70
  model = AutoModel.from_pretrained(
71
+ model_path,
72
  attn_implementation='sdpa',
73
  trust_remote_code=True,
74
+ torch_dtype=torch.bfloat16 if DEVICE == "cuda" else torch.float32,
75
+ device_map='auto'
 
76
  )
 
77
  tokenizer = AutoTokenizer.from_pretrained(
78
+ model_path,
79
  trust_remote_code=True
80
  )
 
81
  model.eval()
82
+ processor = model.init_processor(tokenizer)
83
  return model, tokenizer, processor
84
  except Exception as e:
85
+ print(f"Error loading model: {str(e)}")
86
  raise
87
 
88
+ def process_video_chunk(video_frames, model, tokenizer, processor, prompt):
89
+ """Process a chunk of video frames with mPLUG model"""
90
+ messages = [
91
+ {
92
+ "role": "user",
93
+ "content": prompt,
94
+ "video_frames": video_frames
95
+ }
96
+ ]
97
+ model_messages = []
98
+ videos = []
99
+ for msg in messages:
100
+ content_str = msg["content"]
101
+ if "video_frames" in msg and msg["video_frames"]:
102
+ content_str += "<|video|>"
103
+ videos.append(msg["video_frames"])
104
+ model_messages.append({
105
+ "role": msg["role"],
106
+ "content": content_str
107
+ })
108
+ model_messages.append({
109
+ "role": "assistant",
110
+ "content": ""
111
+ })
112
+ inputs = processor(
113
+ model_messages,
114
+ images=None,
115
+ videos=videos if videos else None
116
+ )
117
+ inputs.to(DEVICE)
118
+ inputs.update({
119
+ 'tokenizer': tokenizer,
120
+ 'max_new_tokens': 100,
121
+ 'decode_text': True,
122
+ })
123
+ response = model.generate(**inputs)
124
+ return response[0]
125
 
126
+ def encode_video_in_chunks(video_path):
127
+ """Extract frames from a video in chunks"""
128
+ vr = VideoReader(video_path, ctx=cpu(0))
129
+ sample_fps = round(vr.get_avg_fps() / 1) # 1 FPS
130
+ frame_idx = [i for i in range(0, len(vr), sample_fps)]
131
+ chunks = [
132
+ frame_idx[i:i + MAX_NUM_FRAMES]
133
+ for i in range(0, len(frame_idx), MAX_NUM_FRAMES)
134
+ ]
135
+ for chunk_idx, chunk in enumerate(chunks):
136
+ frames = vr.get_batch(chunk).asnumpy()
137
+ frames = [Image.fromarray(v.astype('uint8')) for v in frames]
138
+ yield chunk_idx, frames
139
 
 
140
  def detect_people_and_machinery(media_path):
141
+ """Detect people and machinery using YOLOv11 for images and videos"""
142
  try:
143
+ max_people_count = 0
144
+ max_machine_types = {
145
+ "Tower Crane": 0,
146
+ "Mobile Crane": 0,
147
+ "Compactor/Roller": 0,
148
+ "Bulldozer": 0,
149
+ "Excavator": 0,
150
+ "Dump Truck": 0,
151
+ "Concrete Mixer": 0,
152
+ "Loader": 0,
153
+ "Pump Truck": 0,
154
+ "Pile Driver": 0,
155
+ "Grader": 0,
156
+ "Other Vehicle": 0
157
+ }
158
  if isinstance(media_path, str) and is_video(media_path):
159
  cap = cv2.VideoCapture(media_path)
160
  fps = cap.get(cv2.CAP_PROP_FPS)
161
  sample_rate = max(1, int(fps))
162
+ frame_count = 0
163
  while cap.isOpened():
164
  ret, frame = cap.read()
165
  if not ret:
166
  break
167
+ if frame_count % sample_rate == 0:
 
168
  results = YOLO_MODEL(frame)
169
+ people, _, machine_types = process_yolo_results(results)
170
+ max_people_count = max(max_people_count, people)
171
+ for k, v in machine_types.items():
172
+ max_machine_types[k] = max(max_machine_types[k], v)
173
+ frame_count += 1
 
174
  cap.release()
175
  else:
176
+ if isinstance(media_path, str):
177
+ img = cv2.imread(media_path)
178
+ else:
179
+ img = cv2.cvtColor(np.array(media_path), cv2.COLOR_RGB2BGR)
180
  results = YOLO_MODEL(img)
181
+ max_people_count, _, max_machine_types = process_yolo_results(results)
182
+ max_machine_types = {k: v for k, v in max_machine_types.items() if v > 0}
183
+ total_machinery_count = sum(max_machine_types.values())
184
+ return max_people_count, total_machinery_count, max_machine_types
 
 
 
185
  except Exception as e:
186
+ print(f"Error in YOLO detection: {str(e)}")
187
  return 0, 0, {}
188
 
189
+ def process_yolo_results(results):
190
+ """Process YOLO detection results and count people and machinery"""
191
+ people_count = 0
192
+ machine_types = {
193
+ "Tower Crane": 0,
194
+ "Mobile Crane": 0,
195
+ "Compactor/Roller": 0,
196
+ "Bulldozer": 0,
197
+ "Excavator": 0,
198
+ "Dump Truck": 0,
199
+ "Concrete Mixer": 0,
200
+ "Loader": 0,
201
+ "Pump Truck": 0,
202
+ "Pile Driver": 0,
203
+ "Grader": 0,
204
+ "Other Vehicle": 0
205
+ }
206
+ for r in results:
207
+ boxes = r.boxes
208
+ for box in boxes:
209
+ cls = int(box.cls[0])
210
+ conf = float(box.conf[0])
211
+ class_name = YOLO_MODEL.names[cls]
212
+ if class_name.lower() == 'worker' and conf > 0.5:
213
+ people_count += 1
214
+ machinery_mapping = {
215
+ 'tower_crane': "Tower Crane",
216
+ 'mobile_crane': "Mobile Crane",
217
+ 'compactor': "Compactor/Roller",
218
+ 'roller': "Compactor/Roller",
219
+ 'bulldozer': "Bulldozer",
220
+ 'dozer': "Bulldozer",
221
+ 'excavator': "Excavator",
222
+ 'dump_truck': "Dump Truck",
223
+ 'truck': "Dump Truck",
224
+ 'concrete_mixer_truck': "Concrete Mixer",
225
+ 'loader': "Loader",
226
+ 'pump_truck': "Pump Truck",
227
+ 'pile_driver': "Pile Driver",
228
+ 'grader': "Grader",
229
+ 'other_vehicle': "Other Vehicle"
230
+ }
231
+ if conf > 0.5:
232
+ class_lower = class_name.lower()
233
+ for key, value in machinery_mapping.items():
234
+ if key in class_lower:
235
+ machine_types[value] += 1
236
+ break
237
+ total_machinery = sum(machine_types.values())
238
+ return people_count, total_machinery, machine_types
239
+
240
  def analyze_video_activities(video_path):
241
+ """Analyze video using mPLUG model with chunking"""
242
  try:
243
+ all_responses = []
244
+ chunk_generator = encode_video_in_chunks(video_path)
245
+ for chunk_idx, video_frames in chunk_generator:
246
+ model, tokenizer, processor = load_model_and_tokenizer()
247
+ prompt = ("Analyze this construction site video chunk and describe the activities happening. "
248
+ "Focus on construction activities, machinery usage, and worker actions.")
249
+ response = process_video_chunk(video_frames, model, tokenizer, processor, prompt)
250
+ all_responses.append(f"Time period {chunk_idx + 1}:\n{response}")
251
+ del model, tokenizer, processor
 
 
 
 
 
 
 
 
 
 
 
252
  torch.cuda.empty_cache()
253
+ gc.collect()
254
+ return "\n\n".join(all_responses)
 
 
255
  except Exception as e:
256
+ print(f"Error analyzing video: {str(e)}")
257
+ return "Error analyzing video activities"
258
 
259
+ def process_image(image_path, model, tokenizer, processor, prompt):
260
+ """Process single image with mPLUG model"""
 
261
  try:
262
+ image = Image.open(image_path)
263
+ messages = [{
264
+ "role": "user",
265
+ "content": prompt,
266
+ "images": [image]
267
+ }]
268
+ model_messages = []
269
+ images = []
270
+ for msg in messages:
271
+ content_str = msg["content"]
272
+ if "images" in msg and msg["images"]:
273
+ content_str += "<|image|>"
274
+ images.extend(msg["images"])
275
+ model_messages.append({
276
+ "role": msg["role"],
277
+ "content": content_str
278
+ })
279
+ model_messages.append({
280
+ "role": "assistant",
281
+ "content": ""
282
+ })
283
  inputs = processor(
284
+ model_messages,
285
+ images=images,
286
+ videos=None
287
+ )
288
+ inputs.to(DEVICE)
289
+ inputs.update({
290
+ 'tokenizer': tokenizer,
291
+ 'max_new_tokens': 100,
292
+ 'decode_text': True,
293
+ })
294
+ response = model.generate(**inputs)
295
  return response[0]
 
296
  except Exception as e:
297
+ print(f"Error processing image: {str(e)}")
298
+ return "Error processing image"
299
 
300
+ def analyze_image_activities(image_path):
301
+ """Analyze image using mPLUG model"""
 
302
  try:
303
+ model, tokenizer, processor = load_model_and_tokenizer()
304
+ prompt = ("Analyze this construction site image and describe the activities happening. "
305
+ "Focus on construction activities, machinery usage, and worker actions.")
306
+ response = process_image(image_path, model, tokenizer, processor, prompt)
307
+ del model, tokenizer, processor
308
+ if DEVICE == "cuda":
309
+ torch.cuda.empty_cache()
310
+ gc.collect()
311
+ return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  except Exception as e:
313
+ print(f"Error analyzing image: {str(e)}")
314
+ return "Error analyzing image activities"
315
+
316
+ def annotate_video_with_bboxes(video_path):
317
+ """
318
+ Reads the video frame-by-frame, runs YOLO, draws bounding boxes and summaries,
319
+ then writes the annotated frames into a new video.
320
+ """
321
+ cap = cv2.VideoCapture(video_path)
322
+ fps = cap.get(cv2.CAP_PROP_FPS)
323
+ w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
324
+ h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
325
+ out_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
326
+ annotated_video_path = out_file.name
327
+ out_file.close()
328
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
329
+ writer = cv2.VideoWriter(annotated_video_path, fourcc, fps, (w, h))
330
+ while True:
331
+ ret, frame = cap.read()
332
+ if not ret:
333
+ break
334
+ results = YOLO_MODEL(frame)
335
+ frame_counts = {}
336
+ for r in results:
337
+ boxes = r.boxes
338
+ for box in boxes:
339
+ cls_id = int(box.cls[0])
340
+ conf = float(box.conf[0])
341
+ if conf < 0.5:
342
+ continue
343
+ x1, y1, x2, y2 = box.xyxy[0]
344
+ class_name = YOLO_MODEL.names[cls_id]
345
+ x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
346
+ color = (0, 255, 0)
347
+ cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
348
+ label_text = f"{class_name} {conf:.2f}"
349
+ cv2.putText(frame, label_text, (x1, y1 - 6),
350
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
351
+ frame_counts[class_name] = frame_counts.get(class_name, 0) + 1
352
+ summary_str = ", ".join(f"{cls_name}: {count}" for cls_name, count in frame_counts.items())
353
+ cv2.putText(
354
+ frame,
355
+ summary_str,
356
+ (15, 30),
357
+ cv2.FONT_HERSHEY_SIMPLEX,
358
+ 1.0,
359
+ (255, 255, 0),
360
+ 2
361
+ )
362
+ writer.write(frame)
363
+ cap.release()
364
+ writer.release()
365
+ return annotated_video_path
366
 
367
+ @spaces.GPU
368
+ def process_diary(day, date, total_people, total_machinery, machinery_types, activities, media):
369
+ """Process the site diary entry and return outputs along with an annotated video (if video)"""
370
+ if media is None:
371
+ return [day, date, "No media uploaded", "No media uploaded", "No media uploaded", "No media uploaded", None]
372
  try:
373
+ if not hasattr(media, 'name'):
374
+ raise ValueError("Invalid file upload")
375
+ file_ext = get_file_extension(media.name)
376
+ if not (is_image(media.name) or is_video(media.name)):
377
+ raise ValueError(f"Unsupported file type: {file_ext}")
378
+ with tempfile.NamedTemporaryFile(suffix=file_ext, delete=False) as temp_file:
379
+ temp_path = temp_file.name
380
+ if hasattr(media, 'name') and os.path.exists(media.name):
381
+ with open(media.name, 'rb') as f:
382
+ temp_file.write(f.read())
 
 
 
383
  else:
384
+ file_content = media.read() if hasattr(media, 'read') else media
385
+ temp_file.write(file_content if isinstance(file_content, bytes) else file_content.read())
386
+ detected_people, detected_machinery, detected_machinery_types = detect_people_and_machinery(temp_path)
387
+ annotated_video_path = None
388
+ if is_image(media.name):
389
+ detected_activities = analyze_image_activities(temp_path)
390
+ else:
391
+ detected_activities = analyze_video_activities(temp_path)
392
+ annotated_video_path = annotate_video_with_bboxes(temp_path)
393
+ if os.path.exists(temp_path):
394
+ os.remove(temp_path)
395
+ detected_types_str = ", ".join([f"{k}: {v}" for k, v in detected_machinery_types.items()])
396
+ return [day, date, str(detected_people), str(detected_machinery), detected_types_str, detected_activities, annotated_video_path]
 
 
 
397
  except Exception as e:
398
+ print(f"Error processing media: {str(e)}")
399
+ return [day, date, "Error processing media", "Error processing media", "Error processing media", "Error processing media", None]
400
 
401
+ with gr.Blocks(title="Digital Site Diary") as demo:
402
+ gr.Markdown("# 📝 Digital Site Diary")
 
 
403
  with gr.Row():
404
  with gr.Column():
405
+ gr.Markdown("### User Input")
406
+ day = gr.Textbox(label="Day", value='9')
407
+ date = gr.Textbox(label="Date", placeholder="YYYY-MM-DD", value=datetime.now().strftime("%Y-%m-%d"))
408
+ total_people = gr.Number(label="Total Number of People", precision=0, value=10)
409
+ total_machinery = gr.Number(label="Total Number of Machinery", precision=0, value=3)
410
+ machinery_types = gr.Textbox(
411
+ label="Number of Machinery Per Type",
412
+ placeholder="e.g., Excavator: 2, Roller: 1",
413
+ value="Excavator: 2, Roller: 1"
414
+ )
415
+ activities = gr.Textbox(
416
+ label="Activity",
417
+ placeholder="e.g., 9 AM: Excavation, 10 AM: Concreting",
418
+ value="9 AM: Excavation, 10 AM: Concreting",
419
+ lines=3
420
+ )
421
+ media = gr.File(label="Upload Image/Video", file_types=["image", "video"])
422
+ submit_btn = gr.Button("Submit", variant="primary")
423
  with gr.Column():
424
+ gr.Markdown("### Model Detection")
425
  model_day = gr.Textbox(label="Day")
426
  model_date = gr.Textbox(label="Date")
427
+ model_people = gr.Textbox(label="Total Number of People")
428
+ model_machinery = gr.Textbox(label="Total Number of Machinery")
429
+ model_machinery_types = gr.Textbox(label="Number of Machinery Per Type")
430
+ model_activities = gr.Textbox(label="Activity", lines=5)
431
+ model_annotated_video = gr.Video(label="Annotated Video")
 
432
  submit_btn.click(
433
+ fn=process_diary,
434
+ inputs=[day, date, total_people, total_machinery, machinery_types, activities, media],
435
  outputs=[
436
  model_day,
437
  model_date,
438
  model_people,
439
+ model_machinery,
440
  model_machinery_types,
441
+ model_activities,
442
+ model_annotated_video
443
  ]
444
  )
445
 
446
  if __name__ == "__main__":
447
+ demo.launch()