Skip to content

Commit 350a003

Browse files
committed
v1.1.6
1 parent 0c648b8 commit 350a003

File tree

11 files changed

+1836
-2
lines changed

11 files changed

+1836
-2
lines changed

build/lib/pcv/__init__.py

Whitespace-only changes.

build/lib/pcv/examples/__init__.py

Whitespace-only changes.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python
2+
3+
from pcv.vidIO import VideoReader, OutOfFrames
4+
import cv2
5+
6+
class VideoSwitcher:
7+
''' A class for switching between multiple videos. '''
8+
def __init__(self, *filenames, track_along=True):
9+
''' Opens multiple videos for switching between.
10+
11+
When iterating, if a video completes it is re-started.
12+
13+
'track_along' is a boolean specifying if all the videos track along
14+
together. Defaults to True, so when swapping to the next video it
15+
gets jumped ahead to the number of frames that were covered since
16+
it was last active. If set to False, swapping back to a video
17+
resumes where it left off.
18+
19+
'''
20+
self.filenames = filenames
21+
self.track_along = track_along
22+
self.readers = [self._initialise(video) for video in filenames]
23+
self._counts = [0 for video in filenames] # position of each video
24+
self._count = 0 # total frames processed
25+
self.active = 0 # current active video index
26+
27+
def _initialise(self, video):
28+
''' Initialise a video and its iterator. '''
29+
reader = VideoReader(video, destroy=None, verbose=False)
30+
iter(reader)
31+
return reader
32+
33+
@property
34+
def active(self):
35+
return self._active
36+
37+
@active.setter
38+
def active(self, value):
39+
''' Set a new reader as active. Track frames as required. '''
40+
self._active = value
41+
new_reader = self.readers[self._active]
42+
if self.track_along:
43+
extra = self._count - self._counts[self._active]
44+
new_frame = (new_reader.frame + extra) % new_reader._end
45+
new_reader.set_frame(new_frame)
46+
new_reader.reset_delay() # avoid pause jump/negative delay
47+
48+
@property
49+
def video(self):
50+
return self.readers[self.active]
51+
52+
@video.setter
53+
def video(self, replacement):
54+
self.readers[self.active] = replacement
55+
56+
def __enter__(self):
57+
''' Ensure all VideoReaders are '''
58+
for reader in self.readers:
59+
reader.__enter__()
60+
61+
return self
62+
63+
def __exit__(self, *args):
64+
''' Clean up all the VideoReaders at context end. '''
65+
for reader in self.readers:
66+
try:
67+
reader.__exit__(*args)
68+
except:
69+
continue
70+
71+
def __iter__(self):
72+
return self
73+
74+
def __next__(self):
75+
''' Get the next video frame from the active video.
76+
77+
Restarts the active video first if it was finished.
78+
79+
'''
80+
self._count += 1
81+
self._counts[self.active] += 1
82+
try:
83+
return next(self.video)
84+
except OutOfFrames:
85+
return self._reinitialise_active_video()
86+
87+
def _reinitialise_active_video(self):
88+
''' Close and re-open the currently active video. '''
89+
self.video.__exit__(None, None, None)
90+
filename = self.filenames[self.active]
91+
self.video = self._initialise(filename)
92+
return next(self.video)
93+
94+
def next_video(self):
95+
''' Switch to the next video (circular buffer). '''
96+
self.active = (self.active + 1) % len(self.readers)
97+
98+
def prev_video(self):
99+
''' Switch to the previous video (circular buffer). '''
100+
self.active = (self.active - 1) % len(self.readers)
101+
102+
def set_active(self, index):
103+
''' Switch to a specified video index, modulo the number of videos. '''
104+
self.active = index % len(self.readers)
105+
106+
107+
if __name__ == '__main__':
108+
from os import listdir
109+
from pcv.vidIO import Camera
110+
111+
class CoverAnalyser:
112+
''' Analyses a camera feed and switches videos when un/covered. '''
113+
def __init__(self, video_switcher):
114+
''' Cycles through 'video_switcher' videos based on frame data. '''
115+
self._video_switcher = video_switcher
116+
self._covered = False
117+
118+
def analyse(self, frame):
119+
''' Switches to the next video if the camera is un/covered.
120+
121+
Switches on both camera cover and un-cover events.
122+
123+
Mean values of 40 and 60 were relevant for my camera when testing,
124+
and may need to be different depending on camera and lighting.
125+
126+
'''
127+
mean = frame.mean()
128+
if (mean < 40 and not self._covered) \
129+
or (mean > 50 and self._covered):
130+
# switch to next video and toggle state
131+
self._video_switcher.next_video()
132+
self._covered ^= True # toggle state using boolean xor
133+
134+
videos = (file for file in listdir() if file.endswith('.mp4'))
135+
136+
print("press space to pause, 'q' to quit")
137+
with VideoSwitcher(*videos) as video_switcher:
138+
analyser = CoverAnalyser(video_switcher)
139+
with Camera(0) as cam:
140+
# zip together the videos and camera to progress in sync
141+
for (_, v_frame), (_, c_frame) in zip(video_switcher, cam):
142+
analyser.analyse(c_frame)
143+
cv2.imshow('video', v_frame)

0 commit comments

Comments
 (0)