sciveo 0.1.67__tar.gz → 0.1.68__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. {sciveo-0.1.67 → sciveo-0.1.68}/PKG-INFO +1 -1
  2. sciveo-0.1.68/sciveo/media/capture/motion_detection.py +139 -0
  3. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/capture/nvr.py +19 -7
  4. sciveo-0.1.68/sciveo/media/capture/readers.py +122 -0
  5. sciveo-0.1.68/sciveo/version.py +2 -0
  6. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo.egg-info/PKG-INFO +1 -1
  7. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo.egg-info/SOURCES.txt +2 -0
  8. sciveo-0.1.67/sciveo/version.py +0 -2
  9. {sciveo-0.1.67 → sciveo-0.1.68}/README.md +0 -0
  10. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/__init__.py +0 -0
  11. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/api/__init__.py +0 -0
  12. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/api/base.py +0 -0
  13. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/api/predictors.py +0 -0
  14. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/api/server.py +0 -0
  15. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/api/upload.py +0 -0
  16. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/cli.py +0 -0
  17. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/common/__init__.py +0 -0
  18. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/common/configuration.py +0 -0
  19. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/common/model.py +0 -0
  20. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/common/optimizers.py +0 -0
  21. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/common/sampling.py +0 -0
  22. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/content/__init__.py +0 -0
  23. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/content/dataset.py +0 -0
  24. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/content/experiment.py +0 -0
  25. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/content/project.py +0 -0
  26. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/content/runner.py +0 -0
  27. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/__init__.py +0 -0
  28. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/capture/__init__.py +0 -0
  29. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/capture/cam.py +0 -0
  30. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/__init__.py +0 -0
  31. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/base.py +0 -0
  32. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/encoders/__init__.py +0 -0
  33. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/encoders/base.py +0 -0
  34. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/encoders/normalizer.py +0 -0
  35. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/nlp/__init__.py +0 -0
  36. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/nlp/search.py +0 -0
  37. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/time_series/__init__.py +0 -0
  38. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/time_series/dataset.py +0 -0
  39. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/time_series/predictor.py +0 -0
  40. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/time_series/trainer.py +0 -0
  41. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/ml/time_series/window_generator.py +0 -0
  42. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/__init__.py +0 -0
  43. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/base.py +0 -0
  44. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/job_daemon.py +0 -0
  45. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/layouts/__init__.py +0 -0
  46. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/layouts/base.py +0 -0
  47. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/pipeline.py +0 -0
  48. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/postprocessors/__init__.py +0 -0
  49. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/postprocessors/base.py +0 -0
  50. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/postprocessors/default.py +0 -0
  51. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/__init__.py +0 -0
  52. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/audio/__init__.py +0 -0
  53. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/audio/audio.py +0 -0
  54. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/audio/audio_extractor_process.py +0 -0
  55. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/aws.py +0 -0
  56. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/base.py +0 -0
  57. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/file/__init__.py +0 -0
  58. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/file/archive.py +0 -0
  59. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/__init__.py +0 -0
  60. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/album.py +0 -0
  61. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/album_in_image.py +0 -0
  62. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/depth_esimation.py +0 -0
  63. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/embeddings.py +0 -0
  64. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/filters.py +0 -0
  65. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/generators.py +0 -0
  66. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/histogram.py +0 -0
  67. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/mask.py +0 -0
  68. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/object_detection.py +0 -0
  69. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/resize.py +0 -0
  70. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/segmentation.py +0 -0
  71. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/image/watermark.py +0 -0
  72. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/media_info.py +0 -0
  73. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/nlp/__init__.py +0 -0
  74. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/nlp/address.py +0 -0
  75. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/qr.py +0 -0
  76. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/sci/__init__.py +0 -0
  77. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/sci/base.py +0 -0
  78. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/sci/dataset.py +0 -0
  79. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/sci/time_series/__init__.py +0 -0
  80. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/sci/time_series/predictor.py +0 -0
  81. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/sci/time_series/trainer.py +0 -0
  82. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/tpu_base.py +0 -0
  83. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/__init__.py +0 -0
  84. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/generators.py +0 -0
  85. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/motion_detection.py +0 -0
  86. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/resize.py +0 -0
  87. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/video_album.py +0 -0
  88. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/video_frames.py +0 -0
  89. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/processors/video/video_resample.py +0 -0
  90. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/queues.py +0 -0
  91. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/server.py +0 -0
  92. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/web/__init__.py +0 -0
  93. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/pipelines/web/server.py +0 -0
  94. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/tools/__init__.py +0 -0
  95. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/media/tools/video_interactive.py +0 -0
  96. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/__init__.py +0 -0
  97. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/base.py +0 -0
  98. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/dataset/__init__.py +0 -0
  99. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/dataset/object_detection.py +0 -0
  100. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/evaluation/__init__.py +0 -0
  101. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/evaluation/markdown.py +0 -0
  102. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/evaluation/object_detection.py +0 -0
  103. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/__init__.py +0 -0
  104. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/base.py +0 -0
  105. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/description.py +0 -0
  106. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/embeddings.py +0 -0
  107. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/object_detection.py +0 -0
  108. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/tools.py +0 -0
  109. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/images/transformers.py +0 -0
  110. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/nlp/__init__.py +0 -0
  111. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/nlp/embeddings.py +0 -0
  112. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/nlp/tokenizers/__init__.py +0 -0
  113. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/nlp/tokenizers/bpe.py +0 -0
  114. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/video/__init__.py +0 -0
  115. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/ml/video/description.py +0 -0
  116. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/monitoring/__init__.py +0 -0
  117. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/monitoring/monitor.py +0 -0
  118. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/monitoring/start.py +0 -0
  119. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/monitoring/watchdog/__init__.py +0 -0
  120. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/monitoring/watchdog/base.py +0 -0
  121. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/monitoring/watchdog/process.py +0 -0
  122. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/network/__init__.py +0 -0
  123. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/network/camera.py +0 -0
  124. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/network/sniffer.py +0 -0
  125. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/network/tools.py +0 -0
  126. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/__init__.py +0 -0
  127. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/array.py +0 -0
  128. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/aws/__init__.py +0 -0
  129. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/aws/priority_queue.py +0 -0
  130. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/aws/s3.py +0 -0
  131. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/common.py +0 -0
  132. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/complexity.py +0 -0
  133. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/compress.py +0 -0
  134. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/configuration.py +0 -0
  135. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/crypto.py +0 -0
  136. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/daemon.py +0 -0
  137. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/draw/__init__.py +0 -0
  138. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/draw/contours.py +0 -0
  139. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/formating.py +0 -0
  140. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/hardware.py +0 -0
  141. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/http.py +0 -0
  142. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/logger.py +0 -0
  143. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/os.py +0 -0
  144. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/queue.py +0 -0
  145. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/random.py +0 -0
  146. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/remote.py +0 -0
  147. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/simple_counter.py +0 -0
  148. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/synchronized.py +0 -0
  149. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/timers.py +0 -0
  150. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/tools/totp.py +0 -0
  151. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/web/__init__.py +0 -0
  152. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo/web/common.py +0 -0
  153. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo.egg-info/dependency_links.txt +0 -0
  154. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo.egg-info/entry_points.txt +0 -0
  155. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo.egg-info/requires.txt +0 -0
  156. {sciveo-0.1.67 → sciveo-0.1.68}/sciveo.egg-info/top_level.txt +0 -0
  157. {sciveo-0.1.67 → sciveo-0.1.68}/setup.cfg +0 -0
  158. {sciveo-0.1.67 → sciveo-0.1.68}/setup.py +0 -0
  159. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_complexity.py +0 -0
  160. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_compress.py +0 -0
  161. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_configuration.py +0 -0
  162. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_crypto.py +0 -0
  163. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_eval_markdown.py +0 -0
  164. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_ml_datasets.py +0 -0
  165. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_monitoring.py +0 -0
  166. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_runner.py +0 -0
  167. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_sampling.py +0 -0
  168. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_tokenizers.py +0 -0
  169. {sciveo-0.1.67 → sciveo-0.1.68}/test/test_totp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sciveo
3
- Version: 0.1.67
3
+ Version: 0.1.68
4
4
  Description-Content-Type: text/markdown
5
5
  Provides-Extra: mon
6
6
  Provides-Extra: net
@@ -0,0 +1,139 @@
1
+ #
2
+ # Stanislav Georgiev, Softel Labs
3
+ #
4
+ # This is a proprietary file and may not be copied,
5
+ # distributed, or modified without express permission
6
+ # from the owner. For licensing inquiries, please
7
+ # contact s.georgiev@softel.bg.
8
+ #
9
+ # 2025
10
+ #
11
+
12
+ import time
13
+ import os
14
+ import shutil
15
+ import cv2
16
+ import numpy as np
17
+
18
+ from sciveo.tools.logger import *
19
+ from sciveo.tools.daemon import DaemonBase
20
+ from sciveo.media.capture.readers import VideoReaderFFMPEG
21
+ from sciveo.tools.queue import TouchedFilePathQueue
22
+
23
+
24
+ class MotionDetectorGrid:
25
+ def __init__(self, grid):
26
+ self.grid = np.array(grid)
27
+ self.grid_mask = None
28
+ self.zero = False
29
+
30
+ def init_grid(self, frame):
31
+ self.grid_mask = np.zeros_like(frame, dtype=np.float32)
32
+
33
+ for i in range(0, self.grid_mask.shape[0]):
34
+ for j in range(0, self.grid_mask.shape[1]):
35
+ gi = int(self.grid.shape[0] * i / self.grid_mask.shape[0])
36
+ gj = int(self.grid.shape[1] * j / self.grid_mask.shape[1])
37
+
38
+ self.grid_mask[i][j] = self.grid[gi][gj]
39
+
40
+ self.zero = self.grid_mask.sum() <= 0.0
41
+
42
+ def apply(self, frame):
43
+ if self.grid_mask is None:
44
+ self.init_grid(frame)
45
+ result = (frame * self.grid_mask).astype(np.uint8)
46
+ return result
47
+
48
+
49
+ class VideoMotionDetector:
50
+ def __init__(self, source_id, configuration):
51
+ super().__init__()
52
+ self.source_id = source_id
53
+ self.configuration = configuration
54
+ self.count_motion_hold = 0
55
+ self.detection_grid = MotionDetectorGrid(self.configuration["grid"])
56
+
57
+ self.init()
58
+
59
+ def init(self):
60
+ self.fgbg = cv2.createBackgroundSubtractorMOG2(history=50, varThreshold=20, detectShadows=False)
61
+
62
+ def apply(self, frame):
63
+ md_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
64
+ md_frame = cv2.blur(src=md_frame, ksize=(10,10))
65
+ md_frame = self.fgbg.apply(md_frame)
66
+ md_frame = self.detection_grid.apply(md_frame)
67
+ return md_frame
68
+
69
+ def check_motion(self, fgmask):
70
+ k_motion_level = 0.5 + 0.5 * min(self.configuration["threshold_motion_hold"], self.count_motion_hold) / self.configuration["threshold_motion_hold"]
71
+ current_threshold = self.configuration["threshold_motion"] * k_motion_level
72
+ motion_level = np.mean(fgmask)
73
+
74
+ if motion_level > current_threshold:
75
+ self.count_motion_hold += 1
76
+ if self.count_motion_hold >= self.configuration["threshold_motion_hold"]:
77
+ debug(self.source_id, "motion detected", motion_level, "threshold", current_threshold)
78
+ return True
79
+ else:
80
+ self.count_motion_hold = 0
81
+ return False
82
+
83
+ def __call__(self, list_frames):
84
+ self.count_motion_hold = 0
85
+
86
+ for frame in list_frames:
87
+ try:
88
+ if self.detection_grid.zero:
89
+ continue
90
+
91
+ fgmask = self.apply(frame)
92
+
93
+ if self.check_motion(fgmask):
94
+ return True
95
+ except Exception as e:
96
+ exception(e)
97
+ time.sleep(3)
98
+
99
+ return False
100
+
101
+
102
+ class MotionDetectorDaemon(DaemonBase):
103
+ def __init__(self, configuration):
104
+ super().__init__()
105
+ self.configuration = configuration
106
+ self.queue = TouchedFilePathQueue(self.configuration["src"], period=3, touched_timeout=3)
107
+ self.motion_detectors = {}
108
+
109
+ def on_motion(self, source_id, file_name, file_path, configuration):
110
+ debug("MOTION", source_id, file_name, configuration)
111
+ if "dst" in self.configuration:
112
+ dst_file_path = os.path.join(self.configuration["dst"], file_name)
113
+ shutil.copy(file_path, dst_file_path)
114
+ debug("CP", file_path, "=>", dst_file_path)
115
+
116
+ def run(self):
117
+ while(self.is_running):
118
+ try:
119
+ file_name, file_path = self.queue.pop()
120
+ debug("pop", file_name, file_path)
121
+
122
+ file_name_split = file_name.split("___")
123
+ if len(file_name_split) >= 3:
124
+ source_id = file_name_split[0]
125
+
126
+ if source_id in self.configuration:
127
+ configuration = self.configuration.get(source_id, { "threshold_motion": 5, "threshold_motion_hold": 8, "grid": [[0.0]] })
128
+ md = self.motion_detectors.get(source_id, VideoMotionDetector(source_id, configuration))
129
+ motion = md(VideoReaderFFMPEG.read(file_path, resolution=360, RGB=False, gpu_id=0))
130
+
131
+ if motion:
132
+ self.on_motion(source_id, file_name, file_path, configuration)
133
+
134
+ os.remove(file_path)
135
+ debug("RM", file_path)
136
+
137
+ except Exception as e:
138
+ exception(e)
139
+ time.sleep(5)
@@ -22,6 +22,7 @@ from sciveo.tools.logger import *
22
22
  from sciveo.tools.daemon import DaemonBase
23
23
  from sciveo.tools.queue import TouchedFilePathQueue
24
24
  from sciveo.tools.simple_counter import RunCounter
25
+ from sciveo.media.capture.motion_detection import MotionDetectorDaemon
25
26
 
26
27
 
27
28
  class VideoCameraCaptureDaemon(DaemonBase):
@@ -117,7 +118,7 @@ class VideoRecorder:
117
118
  def __init__(self, path_configuration):
118
119
  with open(path_configuration, 'r') as fp:
119
120
  self.configuration = json.load(fp)
120
- self.cams = []
121
+ self.daemons = []
121
122
 
122
123
  for cam_id, cam_config in self.configuration["cam"].items():
123
124
  cam = VideoCameraCaptureDaemon(
@@ -126,14 +127,17 @@ class VideoRecorder:
126
127
  self.configuration.get("max_video_len", 60),
127
128
  self.configuration.get("transport", "tcp")
128
129
  )
129
- self.cams.append(cam)
130
+ self.daemons.append(cam)
131
+
132
+ if "motion" in self.configuration:
133
+ self.daemons.append(MotionDetectorDaemon(configuration=self.configuration["motion"]))
130
134
 
131
135
  self.queue = TouchedFilePathQueue(self.configuration["path"]["tmp"], period=5, touched_timeout=5)
132
136
  self.cleaner_timer = RunCounter(1000, self.clean_old_videos)
133
137
 
134
138
  def start(self):
135
- for cam in self.cams:
136
- cam.start()
139
+ for d in self.daemons:
140
+ d.start()
137
141
 
138
142
  time.sleep(10)
139
143
 
@@ -171,9 +175,17 @@ class VideoRecorder:
171
175
  video_base_path = os.path.join(self.configuration["path"]["video"], cam_id, video_date)
172
176
  video_file_path = os.path.join(video_base_path, video_file_name)
173
177
 
174
- debug("MV", file_path, "=>", video_file_path)
175
178
  os.makedirs(video_base_path, exist_ok=True)
176
- shutil.move(file_path, video_file_path)
179
+ shutil.copy(file_path, video_file_path)
180
+ debug("CP", file_path, "=>", video_file_path)
181
+
182
+ if "motion" in self.configuration:
183
+ motion_file_path = os.path.join(self.configuration["motion"]["src"], f"{cam_id}___{video_date}___{video_file_name}")
184
+ shutil.move(file_path, motion_file_path)
185
+ debug("MV", file_path, "=>", motion_file_path)
186
+ else:
187
+ os.remove(file_path)
188
+ debug("RM", file_path)
177
189
 
178
190
  def clean_old_videos(self):
179
191
  try:
@@ -182,7 +194,7 @@ class VideoRecorder:
182
194
  debug("cmd", cmd)
183
195
  os.system(cmd)
184
196
  except Exception as e:
185
- excprint([self], e, cmd)
197
+ exception(e, cmd)
186
198
 
187
199
 
188
200
  if __name__ == '__main__':
@@ -0,0 +1,122 @@
1
+ #
2
+ # Stanislav Georgiev, Softel Labs
3
+ #
4
+ # This is a proprietary file and may not be copied,
5
+ # distributed, or modified without express permission
6
+ # from the owner. For licensing inquiries, please
7
+ # contact s.georgiev@softel.bg.
8
+ #
9
+ # 2025
10
+ #
11
+
12
+ import os
13
+ import select
14
+ import json
15
+ import subprocess
16
+ import ffmpeg
17
+ import json
18
+ import numpy as np
19
+ from sciveo.tools.logger import *
20
+ from sciveo.tools.timers import Timer
21
+
22
+
23
+ class VideoReaderFFMPEG:
24
+ def __init__(self, path, resolution=720, RGB=False, gpu_id=-1) -> None:
25
+ self.path = path
26
+ self.resolution = resolution
27
+ self.RGB = RGB
28
+ self.gpu_id = gpu_id
29
+
30
+ @staticmethod
31
+ def get_dim(w, h, resolution):
32
+ if resolution <= 0:
33
+ return w, h
34
+
35
+ if h > resolution:
36
+ ratio = resolution / h
37
+ else:
38
+ ratio = 1.0
39
+ width = int(w * ratio)
40
+ height = int(h * ratio)
41
+ width += width % 2
42
+ height += height % 2
43
+ return width, height
44
+
45
+ @staticmethod
46
+ def read(file_path, resolution=720, RGB=False, gpu_id=-1, timeout=30):
47
+ timer_video_read = Timer()
48
+ frames = []
49
+
50
+ probe = subprocess.check_output([
51
+ "ffprobe", "-v", "error",
52
+ "-select_streams", "v:0",
53
+ "-show_entries", "stream=width,height,codec_name",
54
+ "-of", "json", file_path
55
+ ])
56
+ stream_data = json.loads(probe)["streams"][0]
57
+
58
+ in_w, in_h = int(stream_data["width"]), int(stream_data["height"])
59
+ out_w, out_h = VideoReaderFFMPEG.get_dim(in_w, in_h, resolution)
60
+ frame_size = out_w * out_h * 3
61
+
62
+ gpu_nvdec_id = int(os.environ.get("GPU_NVDEC_H264_ID", gpu_id))
63
+
64
+ codec = stream_data["codec_name"]
65
+ if codec in ["h264", "avc1"]:
66
+ decoder = "h264_cuvid"
67
+ elif codec in ["hevc", "h265", "hvc1"]:
68
+ decoder = "hevc_cuvid"
69
+ gpu_nvdec_id = int(os.environ.get("GPU_NVDEC_H265_ID", gpu_nvdec_id))
70
+
71
+ HW = "CPU"
72
+ pix_fmt = "rgb24" if RGB else "bgr24"
73
+
74
+ if min(gpu_id, gpu_nvdec_id) < 0:
75
+ HW = "CPU"
76
+ ffmpeg_input = ffmpeg.input(file_path)
77
+ else:
78
+ HW = "GPU"
79
+ ffmpeg_input = ffmpeg.input(file_path, hwaccel='cuda', hwaccel_device=gpu_nvdec_id, c=decoder)
80
+
81
+ if in_w != out_w or in_h != out_h:
82
+ ffmpeg_input = ffmpeg_input.filter('scale', out_w, out_h)
83
+
84
+ process = (
85
+ ffmpeg_input
86
+ .output('pipe:', format='rawvideo', pix_fmt=pix_fmt, vsync='vfr')
87
+ .global_args('-v', 'quiet')
88
+ .run_async(pipe_stdout=True, pipe_stderr=subprocess.DEVNULL)
89
+ )
90
+
91
+ fd = process.stdout.fileno()
92
+
93
+ while True:
94
+ ready, _, _ = select.select([fd], [], [], timeout)
95
+ if not ready:
96
+ warning("VideoReaderFFMPEG::read", file_path, f"[{HW}][{gpu_id}] not ready FAIL")
97
+ process.kill()
98
+ process.wait()
99
+ frames = []
100
+ break
101
+
102
+ in_bytes = process.stdout.read(frame_size)
103
+ if not in_bytes:
104
+ break
105
+ frame = np.frombuffer(in_bytes, np.uint8).reshape([out_h, out_w, 3]).copy()
106
+ frames.append(frame)
107
+
108
+ process.wait()
109
+
110
+ if gpu_id >= 0 and len(frames) == 0:
111
+ warning("VideoReaderFFMPEG::read", file_path, f"[{HW}][{gpu_id}] reading not successful, fallback to CPU")
112
+ return VideoReaderFFMPEG.read(file_path, resolution, RGB, gpu_id=-1)
113
+
114
+ elapsed = timer_video_read.stop()
115
+ FPS = round(len(frames) / elapsed, 2)
116
+ debug("VideoReaderFFMPEG::read", file_path, f"[{HW}] video reading {len(frames)} frames, codec[{codec}] decoder[{decoder}] pix_fmt[{pix_fmt}] [{out_w}x{out_h}], elapsed {round(elapsed, 1)} FPS {FPS}")
117
+ return frames
118
+
119
+ def __call__(self):
120
+ return VideoReaderFFMPEG.read(self.path, self.resolution, RGB=self.RGB, gpu_id=self.gpu_id)
121
+
122
+
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = '0.1.68'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sciveo
3
- Version: 0.1.67
3
+ Version: 0.1.68
4
4
  Description-Content-Type: text/markdown
5
5
  Provides-Extra: mon
6
6
  Provides-Extra: net
@@ -27,7 +27,9 @@ sciveo/content/runner.py
27
27
  sciveo/media/__init__.py
28
28
  sciveo/media/capture/__init__.py
29
29
  sciveo/media/capture/cam.py
30
+ sciveo/media/capture/motion_detection.py
30
31
  sciveo/media/capture/nvr.py
32
+ sciveo/media/capture/readers.py
31
33
  sciveo/media/ml/__init__.py
32
34
  sciveo/media/ml/base.py
33
35
  sciveo/media/ml/encoders/__init__.py
@@ -1,2 +0,0 @@
1
-
2
- __version__ = '0.1.67'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes