Skip to content

Commit

Permalink
add new files
Browse files Browse the repository at this point in the history
  • Loading branch information
neozhaoliang committed Jul 10, 2020
1 parent 7e441e3 commit a2e0380
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
52 changes: 52 additions & 0 deletions base_thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from queue import Queue
import cv2
from PyQt5.QtCore import (QThread, QTime, QMutex, pyqtSignal, QMutexLocker)

from structures import ThreadStatisticsData


class BaseThread(QThread):

"""
Base class for all types of threads (capture, processing, stitching, ...,
etc). Mainly for collecting statistics of this thread.
"""

FPS_STAT_QUEUE_LENGTH = 32

update_statistics_gui = pyqtSignal(ThreadStatisticsData)

def __init__(self, parent=None):
super(BaseThread, self).__init__(parent)
self.init_commons()

def init_commons(self):
self.stopped = False
self.stop_mutex = QMutex()
self.clock = QTime()
self.fps = Queue()
self.processing_time = 0
self.processing_mutex = QMutex()
self.fps_sum = 0
self.stat_data = ThreadStatisticsData()

def stop(self):
with QMutexLocker(self.stop_mutex):
self.stopped = True

def update_fps(self, dt):
# add instantaneous fps value to queue
if dt > 0:
self.fps.put(1000 / dt)

# discard redundant items in the fps queue
if self.fps.qsize() > self.FPS_STAT_QUEUE_LENGTH:
self.fps.get()

# update statistics
if self.fps.qsize() == self.FPS_STAT_QUEUE_LENGTH:
while not self.fps.empty():
self.fps_sum += self.fps.get()

self.stat_data.average_fps = round(self.fps_sum / self.FPS_STAT_QUEUE_LENGTH, 2)
self.fps_sum = 0
108 changes: 108 additions & 0 deletions capture_thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import cv2
from PyQt5.QtCore import qDebug

from base_thread import BaseThread
from structures import ImageFrame
from utils import gstreamer_pipeline


class CaptureThread(BaseThread):

def __init__(self,
device_id,
flip_method=2,
drop_if_full=True,
api_preference=cv2.CAP_GSTREAMER,
resolution=None,
parent=None):
"""
device_id: device number of the camera.
flip_method: 0 for identity, 2 for 180 degree rotation (if the camera is installed
up-side-down).
drop_if_full: drop the frame if buffer is full.
api_preference: cv2.CAP_GSTREAMER for csi cameras, usually cv2.CAP_ANY would suffice.
resolution: camera resolution (width, height).
"""
super(CaptureThread, self).__init__(parent)
self.device_id = device_id
self.flip_method = flip_method
self.drop_if_full = drop_if_full
self.api_preference = api_preference
self.resolution = resolution
self.cap = cv2.VideoCapture()
# an instance of the MultiBufferManager object,
# for synchronizing this thread with other cameras.
self.buffer_manager = None

def run(self):
if self.buffer_manager is None:
raise ValueError("This thread has not been binded to any buffer manager yet")

while True:
self.stop_mutex.lock()
if self.stopped:
self.stopped = False
self.stop_mutex.unlock()
break
self.stop_mutex.unlock()

# save capture time
self.processing_time = self.clock.elapsed()
# start timer (used to calculate capture rate)
self.clock.start()

# synchronize with other streams (if enabled for this stream)
self.buffer_manager.sync(self.device_id)

if not self.cap.grab():
continue

# retrieve frame and add it to buffer
_, frame = self.cap.retrieve()
img_frame = ImageFrame(self.clock.msecsSinceStartOfDay(), frame)
self.buffer_manager.get_device(self.device_id).add(img_frame, self.drop_if_full)

# update statistics
self.update_fps(self.processing_time)
self.stat_data.frames_processed_count += 1
# inform GUI of updated statistics
self.update_statistics_gui.emit(self.stat_data)

qDebug("Stopping capture thread...")

def connect_camera(self):
options = gstreamer_pipeline(cam_id=self.device_id, flip_method=self.flip_method)
self.cap.open(options, self.api_preference)
# return false if failed to open camera
if not self.cap.isOpened():
qDebug("Cannot open camera {}".format(self.device_id))
return False
else:
# try to set camera resolution
if self.resolution is not None:
width, height = self.resolution
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
# some camera may become closed if the resolution is not supported
if not self.cap.isOpened():
qDebug("Resolution not supported by camera device: {}".format(self.resolution))
return False
# use the default resolution
else:
width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.resolution = (width, height)

return True

def disconnect_camera(self):
# disconnect camera if it's already openned.
if self.cap.isOpened():
self.cap.release()
return True
# else do nothing and return
else:
return False

def is_camera_connected(self):
return self.cap.isOpened()

0 comments on commit a2e0380

Please sign in to comment.