forked from neozhaoliang/surround-view-system-introduction
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7e441e3
commit a2e0380
Showing
2 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |