hackerbhai commited on
Commit
3ad524c
Β·
verified Β·
1 Parent(s): 0b4bf54

πŸŽ₯ Add Video AI: model/video_ai.py

Browse files
Files changed (1) hide show
  1. model/video_ai.py +575 -0
model/video_ai.py ADDED
@@ -0,0 +1,575 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ πŸŽ₯ Video AI System - Real-time Video Analysis & Processing
3
+ 🎬 Understand β€’ 🎨 Edit β€’ πŸ“Š Extract β€’ 🎯 Recognize β€’ πŸ“Ί Real-time
4
+ """
5
+
6
+ import cv2
7
+ import numpy as np
8
+ from typing import Dict, List, Tuple, Optional
9
+ from datetime import datetime
10
+ import json
11
+
12
+ class VideoAnalyzer:
13
+ """πŸŽ₯ Comprehensive video analysis and processing"""
14
+
15
+ def __init__(self):
16
+ self.frame_cache = []
17
+ self.analysis_history = []
18
+
19
+ def analyze_video_content(self, video_path: str) -> Dict:
20
+ """🎬 Understand what's in a video"""
21
+ print(f"πŸ” Analyzing video: {video_path}")
22
+
23
+ cap = cv2.VideoCapture(video_path)
24
+
25
+ if not cap.isOpened():
26
+ return {"error": "❌ Cannot open video file"}
27
+
28
+ # Get video properties
29
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
30
+ frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
31
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
32
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
33
+ duration = frame_count / fps if fps > 0 else 0
34
+
35
+ print(f"πŸ“Š Video Info: {width}x{height}, {fps} FPS, {duration:.2f}s")
36
+
37
+ # Analyze key frames
38
+ analysis = {
39
+ "video_info": {
40
+ "resolution": f"{width}x{height}",
41
+ "fps": fps,
42
+ "duration": f"{duration:.2f}s",
43
+ "frames": frame_count
44
+ },
45
+ "scenes": [],
46
+ "objects_detected": [],
47
+ "motion_analysis": {},
48
+ "quality_metrics": {}
49
+ }
50
+
51
+ # Sample frames for analysis
52
+ sample_interval = max(1, frame_count // 10) # Sample 10 frames
53
+
54
+ for i in range(0, frame_count, sample_interval):
55
+ cap.set(cv2.CAP_PROP_POS_FRAMES, i)
56
+ ret, frame = cap.read()
57
+
58
+ if ret:
59
+ # Analyze frame
60
+ frame_analysis = self._analyze_frame(frame, i)
61
+ analysis["scenes"].append(frame_analysis)
62
+
63
+ cap.release()
64
+
65
+ # Aggregate results
66
+ analysis["summary"] = self._generate_video_summary(analysis)
67
+
68
+ return analysis
69
+
70
+ def _analyze_frame(self, frame: np.ndarray, frame_num: int) -> Dict:
71
+ """πŸ” Analyze individual frame"""
72
+ # Convert to different color spaces for analysis
73
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
74
+ hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
75
+
76
+ # Detect edges
77
+ edges = cv2.Canny(gray, 50, 150)
78
+
79
+ # Detect objects using contours
80
+ contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
81
+
82
+ # Calculate brightness
83
+ brightness = np.mean(gray)
84
+
85
+ # Calculate color distribution
86
+ color_hist = cv2.calcHist([hsv], [0, 1], None, [50, 60], [0, 180, 0, 256])
87
+
88
+ # Detect motion (simplified)
89
+ motion_score = np.std(gray)
90
+
91
+ return {
92
+ "frame_number": frame_num,
93
+ "brightness": float(brightness),
94
+ "objects_count": len(contours),
95
+ "motion_score": float(motion_score),
96
+ "dominant_colors": self._extract_dominant_colors(color_hist),
97
+ "complexity": float(np.mean(edges))
98
+ }
99
+
100
+ def _extract_dominant_colors(self, hist: np.ndarray) -> List[str]:
101
+ """🎨 Extract dominant colors from histogram"""
102
+ # Simplified color detection
103
+ colors = []
104
+ if np.max(hist) > 100:
105
+ colors.append("red")
106
+ if np.mean(hist) > 50:
107
+ colors.append("green")
108
+ if np.std(hist) > 30:
109
+ colors.append("blue")
110
+ return colors if colors else ["neutral"]
111
+
112
+ def _generate_video_summary(self, analysis: Dict) -> Dict:
113
+ """πŸ“Š Generate video summary"""
114
+ scenes = analysis["scenes"]
115
+
116
+ if not scenes:
117
+ return {"error": "No scenes analyzed"}
118
+
119
+ avg_brightness = sum(s["brightness"] for s in scenes) / len(scenes)
120
+ avg_objects = sum(s["objects_count"] for s in scenes) / len(scenes)
121
+ avg_motion = sum(s["motion_score"] for s in scenes) / len(scenes)
122
+
123
+ return {
124
+ "type": "educational" if avg_objects > 5 else "general",
125
+ "energy_level": "high" if avg_motion > 50 else "medium" if avg_motion > 30 else "low",
126
+ "brightness_level": "bright" if avg_brightness > 128 else "dark" if avg_brightness < 80 else "normal",
127
+ "content_complexity": "complex" if avg_objects > 10 else "moderate" if avg_objects > 5 else "simple",
128
+ "recommended_actions": self._recommend_actions(avg_brightness, avg_objects, avg_motion)
129
+ }
130
+
131
+ def _recommend_actions(self, brightness: float, objects: float, motion: float) -> List[str]:
132
+ """πŸ’‘ Recommend video improvements"""
133
+ actions = []
134
+
135
+ if brightness < 80:
136
+ actions.append("πŸ”† Increase brightness for better visibility")
137
+ elif brightness > 200:
138
+ actions.append("πŸŒ™ Reduce brightness to avoid overexposure")
139
+
140
+ if objects < 2:
141
+ actions.append("πŸ“¦ Add more visual elements for engagement")
142
+
143
+ if motion < 20:
144
+ actions.append("🎬 Add more dynamic movement")
145
+
146
+ return actions
147
+
148
+ def remove_objects(self, video_path: str, output_path: str, mask: np.ndarray = None) -> Dict:
149
+ """🎨 Remove objects from video using inpainting"""
150
+ print(f"🎨 Removing objects from video...")
151
+
152
+ cap = cv2.VideoCapture(video_path)
153
+
154
+ if not cap.isOpened():
155
+ return {"error": "❌ Cannot open video file"}
156
+
157
+ # Get video properties
158
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
159
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
160
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
161
+
162
+ # Create video writer
163
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
164
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
165
+
166
+ frame_count = 0
167
+ processed_frames = 0
168
+
169
+ while True:
170
+ ret, frame = cap.read()
171
+ if not ret:
172
+ break
173
+
174
+ frame_count += 1
175
+
176
+ # Apply object removal
177
+ if mask is not None:
178
+ # Use inpainting to remove masked objects
179
+ result = cv2.inpaint(frame, mask, 3, cv2.INPAINT_TELEA)
180
+ else:
181
+ result = frame
182
+
183
+ out.write(result)
184
+ processed_frames += 1
185
+
186
+ if processed_frames % 30 == 0:
187
+ print(f"βœ… Processed {processed_frames} frames...")
188
+
189
+ cap.release()
190
+ out.release()
191
+
192
+ return {
193
+ "status": "βœ… success",
194
+ "output_file": output_path,
195
+ "frames_processed": processed_frames,
196
+ "emoji": "🎨"
197
+ }
198
+
199
+ def extract_information(self, video_path: str) -> Dict:
200
+ """πŸ“Š Extract information from video"""
201
+ print(f"πŸ“Š Extracting information from video...")
202
+
203
+ analysis = self.analyze_video_content(video_path)
204
+
205
+ # Extract text (simplified - would use OCR in production)
206
+ extracted_info = {
207
+ "video_metadata": analysis["video_info"],
208
+ "content_analysis": analysis["summary"],
209
+ "key_moments": self._identify_key_moments(analysis["scenes"]),
210
+ "detected_patterns": self._detect_patterns(analysis["scenes"]),
211
+ "emoji": "πŸ“Š"
212
+ }
213
+
214
+ return extracted_info
215
+
216
+ def _identify_key_moments(self, scenes: List[Dict]) -> List[Dict]:
217
+ """🎯 Identify key moments in video"""
218
+ if not scenes:
219
+ return []
220
+
221
+ # Find frames with high motion or many objects
222
+ key_moments = []
223
+
224
+ for scene in scenes:
225
+ if scene["motion_score"] > 50 or scene["objects_count"] > 10:
226
+ key_moments.append({
227
+ "frame": scene["frame_number"],
228
+ "reason": "high_activity",
229
+ "emoji": "🎬"
230
+ })
231
+
232
+ return key_moments[:5] # Top 5 key moments
233
+
234
+ def _detect_patterns(self, scenes: List[Dict]) -> List[str]:
235
+ """πŸ” Detect patterns in video"""
236
+ if not scenes:
237
+ return []
238
+
239
+ patterns = []
240
+
241
+ # Check for consistent brightness
242
+ brightness_values = [s["brightness"] for s in scenes]
243
+ if np.std(brightness_values) < 20:
244
+ patterns.append("🌟 Consistent lighting throughout")
245
+
246
+ # Check for motion patterns
247
+ motion_values = [s["motion_score"] for s in scenes]
248
+ if np.mean(motion_values) > 50:
249
+ patterns.append("🎬 High-energy content")
250
+ elif np.mean(motion_values) < 20:
251
+ patterns.append("πŸ“š Educational/tutorial content")
252
+
253
+ return patterns
254
+
255
+ def enhance_video_quality(self, video_path: str, output_path: str) -> Dict:
256
+ """🎯 Enhance video quality"""
257
+ print(f"🎯 Enhancing video quality...")
258
+
259
+ cap = cv2.VideoCapture(video_path)
260
+
261
+ if not cap.isOpened():
262
+ return {"error": "❌ Cannot open video file"}
263
+
264
+ # Get video properties
265
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
266
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
267
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
268
+
269
+ # Create video writer
270
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
271
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
272
+
273
+ frame_count = 0
274
+
275
+ while True:
276
+ ret, frame = cap.read()
277
+ if not ret:
278
+ break
279
+
280
+ # Apply enhancements
281
+ enhanced = self._enhance_frame(frame)
282
+
283
+ out.write(enhanced)
284
+ frame_count += 1
285
+
286
+ cap.release()
287
+ out.release()
288
+
289
+ return {
290
+ "status": "βœ… success",
291
+ "output_file": output_path,
292
+ "frames_enhanced": frame_count,
293
+ "enhancements": [
294
+ "πŸ”† Brightness adjustment",
295
+ "🎨 Color correction",
296
+ "πŸ” Sharpening",
297
+ "πŸ“Š Contrast enhancement"
298
+ ],
299
+ "emoji": "🎯"
300
+ }
301
+
302
+ def _enhance_frame(self, frame: np.ndarray) -> np.ndarray:
303
+ """🎨 Enhance individual frame"""
304
+ # Convert to LAB color space
305
+ lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
306
+ l, a, b = cv2.split(lab)
307
+
308
+ # Apply CLAHE (Contrast Limited Adaptive Histogram Equalization)
309
+ clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
310
+ cl = clahe.apply(l)
311
+
312
+ # Merge channels
313
+ limg = cv2.merge((cl, a, b))
314
+ enhanced = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
315
+
316
+ # Apply sharpening
317
+ kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
318
+ sharpened = cv2.filter2D(enhanced, -1, kernel)
319
+
320
+ # Blend original and sharpened
321
+ result = cv2.addWeighted(enhanced, 0.7, sharpened, 0.3, 0)
322
+
323
+ return result
324
+
325
+ def stabilize_video(self, video_path: str, output_path: str) -> Dict:
326
+ """πŸ“Ί Stabilize video"""
327
+ print(f"πŸ“Ί Stabilizing video...")
328
+
329
+ cap = cv2.VideoCapture(video_path)
330
+
331
+ if not cap.isOpened():
332
+ return {"error": "❌ Cannot open video file"}
333
+
334
+ # Get video properties
335
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
336
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
337
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
338
+
339
+ # Read first frame
340
+ ret, prev_frame = cap.read()
341
+ if not ret:
342
+ return {"error": "❌ Cannot read first frame"}
343
+
344
+ prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
345
+
346
+ # Create video writer
347
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
348
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
349
+
350
+ # Stabilization transforms
351
+ transforms = []
352
+ frame_count = 0
353
+
354
+ while True:
355
+ ret, curr_frame = cap.read()
356
+ if not ret:
357
+ break
358
+
359
+ curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY)
360
+
361
+ # Detect features
362
+ prev_pts = cv2.goodFeaturesToTrack(prev_gray, maxCorners=200, qualityLevel=0.01, minDistance=30, blockSize=3)
363
+
364
+ if prev_pts is not None:
365
+ curr_pts, status, _ = cv2.calcOpticalFlowPyrLK(prev_gray, curr_gray, prev_pts, None)
366
+
367
+ # Filter valid points
368
+ idx = np.where(status == 1)[0]
369
+ prev_pts = prev_pts[idx]
370
+ curr_pts = curr_pts[idx]
371
+
372
+ if len(prev_pts) > 10:
373
+ # Estimate transform
374
+ m, _ = cv2.estimateAffinePartial2D(prev_pts, curr_pts)
375
+
376
+ if m is not None:
377
+ dx = m[0, 2]
378
+ dy = m[1, 2]
379
+ da = np.arctan2(m[1, 0], m[0, 0])
380
+
381
+ transforms.append([dx, dy, da])
382
+
383
+ prev_gray = curr_gray
384
+ frame_count += 1
385
+
386
+ cap.release()
387
+
388
+ # Apply stabilization
389
+ cap = cv2.VideoCapture(video_path)
390
+
391
+ trajectory = np.cumsum(transforms, axis=0)
392
+
393
+ # Smooth trajectory
394
+ smoothed = self._smooth_trajectory(trajectory)
395
+
396
+ # Calculate stabilization transforms
397
+ diff = smoothed - trajectory
398
+ stabilization_transforms = []
399
+
400
+ for i in range(len(diff)):
401
+ dx = diff[i, 0]
402
+ dy = diff[i, 1]
403
+ da = diff[i, 2]
404
+
405
+ m = np.zeros((2, 3))
406
+ m[0, 0] = np.cos(da)
407
+ m[0, 1] = -np.sin(da)
408
+ m[1, 0] = np.sin(da)
409
+ m[1, 1] = np.cos(da)
410
+ m[0, 2] = dx
411
+ m[1, 2] = dy
412
+
413
+ stabilization_transforms.append(m)
414
+
415
+ # Apply transforms and write video
416
+ frame_idx = 0
417
+
418
+ while True:
419
+ ret, frame = cap.read()
420
+ if not ret:
421
+ break
422
+
423
+ if frame_idx < len(stabilization_transforms):
424
+ stabilized = cv2.warpAffine(frame, stabilization_transforms[frame_idx], (width, height))
425
+ else:
426
+ stabilized = frame
427
+
428
+ out.write(stabilized)
429
+ frame_idx += 1
430
+
431
+ cap.release()
432
+ out.release()
433
+
434
+ return {
435
+ "status": "βœ… success",
436
+ "output_file": output_path,
437
+ "frames_stabilized": frame_idx,
438
+ "stabilization_level": "high",
439
+ "emoji": "πŸ“Ί"
440
+ }
441
+
442
+ def _smooth_trajectory(self, trajectory: np.ndarray, window_size: int = 30) -> np.ndarray:
443
+ """πŸ“Š Smooth trajectory using moving average"""
444
+ smoothed = np.zeros_like(trajectory)
445
+
446
+ for i in range(3): # x, y, angle
447
+ smoothed[:, i] = np.convolve(trajectory[:, i],
448
+ np.ones(window_size)/window_size,
449
+ mode='same')
450
+
451
+ return smoothed
452
+
453
+ def real_time_analysis(self, source=0) -> Dict:
454
+ """πŸ“Ί Real-time video analysis from camera or screen"""
455
+ print(f"πŸ“Ί Starting real-time analysis...")
456
+ print(f"πŸ“· Source: {'Camera' if source == 0 else 'Screen'}")
457
+
458
+ cap = cv2.VideoCapture(source)
459
+
460
+ if not cap.isOpened():
461
+ return {"error": "❌ Cannot open video source"}
462
+
463
+ print("βœ… Real-time analysis started!")
464
+ print("πŸ‘€ Press 'q' to quit")
465
+
466
+ analysis_results = []
467
+ frame_count = 0
468
+
469
+ while True:
470
+ ret, frame = cap.read()
471
+ if not ret:
472
+ break
473
+
474
+ frame_count += 1
475
+
476
+ # Analyze every 10th frame for performance
477
+ if frame_count % 10 == 0:
478
+ analysis = self._analyze_frame(frame, frame_count)
479
+ analysis_results.append(analysis)
480
+
481
+ # Display analysis
482
+ print(f"\nπŸ“Š Frame {frame_count}:")
483
+ print(f" 🎯 Objects: {analysis['objects_count']}")
484
+ print(f" πŸ’‘ Brightness: {analysis['brightness']:.1f}")
485
+ print(f" 🎬 Motion: {analysis['motion_score']:.1f}")
486
+
487
+ # Show frame (comment out for headless mode)
488
+ # cv2.imshow('Real-time Analysis', frame)
489
+
490
+ # if cv2.waitKey(1) & 0xFF == ord('q'):
491
+ # break
492
+
493
+ cap.release()
494
+ # cv2.destroyAllWindows()
495
+
496
+ return {
497
+ "status": "βœ… success",
498
+ "frames_analyzed": frame_count,
499
+ "analysis_results": analysis_results,
500
+ "emoji": "πŸ“Ί"
501
+ }
502
+
503
+
504
+ class CameraProcessor:
505
+ """πŸ“· Camera processing and visual recognition"""
506
+
507
+ def __init__(self):
508
+ self.recognition_history = []
509
+
510
+ def process_camera_frame(self, frame: np.ndarray) -> Dict:
511
+ """πŸ“· Process camera frame for visual recognition"""
512
+ # Convert to different formats
513
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
514
+ hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
515
+
516
+ # Detect faces (simplified)
517
+ face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
518
+ faces = face_cascade.detectMultiScale(gray, 1.1, 4)
519
+
520
+ # Detect objects
521
+ edges = cv2.Canny(gray, 50, 150)
522
+ contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
523
+
524
+ # Analyze scene
525
+ brightness = np.mean(gray)
526
+ contrast = np.std(gray)
527
+
528
+ return {
529
+ "faces_detected": len(faces),
530
+ "objects_detected": len(contours),
531
+ "brightness": float(brightness),
532
+ "contrast": float(contrast),
533
+ "scene_type": self._classify_scene(brightness, len(contours)),
534
+ "emoji": "πŸ“·"
535
+ }
536
+
537
+ def _classify_scene(self, brightness: float, object_count: int) -> str:
538
+ """🎯 Classify scene type"""
539
+ if brightness > 180 and object_count < 5:
540
+ return "🌟 Bright and simple"
541
+ elif brightness < 80:
542
+ return "πŸŒ™ Dark scene"
543
+ elif object_count > 20:
544
+ return "🎬 Complex scene"
545
+ else:
546
+ return "πŸ“š Normal scene"
547
+
548
+ def recognize_visual_elements(self, frame: np.ndarray) -> Dict:
549
+ """πŸ‘οΈ Recognize visual elements in frame"""
550
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
551
+
552
+ # Detect edges and shapes
553
+ edges = cv2.Canny(gray, 50, 150)
554
+
555
+ # Detect circles
556
+ circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20,
557
+ param1=50, param2=30, minRadius=0, maxRadius=0)
558
+
559
+ # Detect lines
560
+ lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, minLineLength=50, maxLineGap=10)
561
+
562
+ # Detect rectangles (simplified)
563
+ contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
564
+ rectangles = [c for c in contours if len(cv2.approxPolyDP(c, 0.02*cv2.contourArea(c), True)) == 4]
565
+
566
+ return {
567
+ "circles": len(circles[0]) if circles is not None else 0,
568
+ "lines": len(lines) if lines is not None else 0,
569
+ "rectangles": len(rectangles),
570
+ "total_shapes": len(contours),
571
+ "emoji": "πŸ‘οΈ"
572
+ }
573
+
574
+ # Export classes
575
+ __all__ = ['VideoAnalyzer', 'CameraProcessor']