
Handling video streams
The video stream of the webcam is handled by a series of steps that begin with the __init__ method. These steps might appear overly complicated at first, but they are necessary in order to allow the video to run smoothly, even at higher frame rates (that is, to counteract flickering).
The wxPython module works with events and callback methods. When a certain event is triggered, it can cause a certain class method to be executed (in other words, a method can bind to an event). We will use this mechanism to our advantage and display a new frame every so often using the following steps:
- We create a timer that will generate a wx.EVT_TIMER event whenever 1000./self.fps milliseconds have passed:
self.timer = wx.Timer(self)
self.timer.Start(1000. / self.fps)
- Whenever the timer is up, we want the _on_next_frame method to be called. It will try to acquire a new video frame:
self.Bind(wx.EVT_TIMER, self._on_next_frame)
- The _on_next_frame method will process the new video frame and store the processed frame in a bitmap. This will trigger another event, wx.EVT_PAINT. We want to bind this event to the _on_paint method, which will paint the display of the new frame. So, we create a placeholder for the video and bind wx.EVT_PAINT to it:
self.video_pnl.Bind(wx.EVT_PAINT, self._on_paint)
The _on_next_frame method grabs a new frame and, once done, sends the frame to another method, process_frame, for further processing (which is an abstract method and should be implemented by the child class):
def _on_next_frame(self, event):
"""
Capture a new frame from the capture device,
send an RGB version to `self.process_frame`, refresh.
"""
success, frame = self._acquire_frame()
if success:
# process current frame
frame = self.process_frame(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
...
The processed frame (frame) is then stored in a bitmap buffer (self.bmp). Calling Refresh triggers the aforementioned wx.EVT_PAINT event, which binds to _on_paint:
...
# update buffer and paint (EVT_PAINT triggered by Refresh)
self.bmp.CopyFromBuffer(frame)
self.Refresh(eraseBackground=False)
The paint method then grabs the frame from the buffer and displays it:
def _on_paint(self, event):
""" Draw the camera frame stored in `self.bmp` onto `self.video_pnl`.
"""
wx.BufferedPaintDC(self.video_pnl).DrawBitmap(self.bmp, 0, 0)
The next section shows you how to create a custom filter layout.