|
| 1 | +import cv2 |
| 2 | +from PyQt5.QtCore import qDebug |
| 3 | + |
| 4 | +from base_thread import BaseThread |
| 5 | +from structures import ImageFrame |
| 6 | +from utils import gstreamer_pipeline |
| 7 | + |
| 8 | + |
| 9 | +class CaptureThread(BaseThread): |
| 10 | + |
| 11 | + def __init__(self, |
| 12 | + device_id, |
| 13 | + flip_method=2, |
| 14 | + drop_if_full=True, |
| 15 | + api_preference=cv2.CAP_GSTREAMER, |
| 16 | + resolution=None, |
| 17 | + parent=None): |
| 18 | + """ |
| 19 | + device_id: device number of the camera. |
| 20 | + flip_method: 0 for identity, 2 for 180 degree rotation (if the camera is installed |
| 21 | + up-side-down). |
| 22 | + drop_if_full: drop the frame if buffer is full. |
| 23 | + api_preference: cv2.CAP_GSTREAMER for csi cameras, usually cv2.CAP_ANY would suffice. |
| 24 | + resolution: camera resolution (width, height). |
| 25 | + """ |
| 26 | + super(CaptureThread, self).__init__(parent) |
| 27 | + self.device_id = device_id |
| 28 | + self.flip_method = flip_method |
| 29 | + self.drop_if_full = drop_if_full |
| 30 | + self.api_preference = api_preference |
| 31 | + self.resolution = resolution |
| 32 | + self.cap = cv2.VideoCapture() |
| 33 | + # an instance of the MultiBufferManager object, |
| 34 | + # for synchronizing this thread with other cameras. |
| 35 | + self.buffer_manager = None |
| 36 | + |
| 37 | + def run(self): |
| 38 | + if self.buffer_manager is None: |
| 39 | + raise ValueError("This thread has not been binded to any buffer manager yet") |
| 40 | + |
| 41 | + while True: |
| 42 | + self.stop_mutex.lock() |
| 43 | + if self.stopped: |
| 44 | + self.stopped = False |
| 45 | + self.stop_mutex.unlock() |
| 46 | + break |
| 47 | + self.stop_mutex.unlock() |
| 48 | + |
| 49 | + # save capture time |
| 50 | + self.processing_time = self.clock.elapsed() |
| 51 | + # start timer (used to calculate capture rate) |
| 52 | + self.clock.start() |
| 53 | + |
| 54 | + # synchronize with other streams (if enabled for this stream) |
| 55 | + self.buffer_manager.sync(self.device_id) |
| 56 | + |
| 57 | + if not self.cap.grab(): |
| 58 | + continue |
| 59 | + |
| 60 | + # retrieve frame and add it to buffer |
| 61 | + _, frame = self.cap.retrieve() |
| 62 | + img_frame = ImageFrame(self.clock.msecsSinceStartOfDay(), frame) |
| 63 | + self.buffer_manager.get_device(self.device_id).add(img_frame, self.drop_if_full) |
| 64 | + |
| 65 | + # update statistics |
| 66 | + self.update_fps(self.processing_time) |
| 67 | + self.stat_data.frames_processed_count += 1 |
| 68 | + # inform GUI of updated statistics |
| 69 | + self.update_statistics_gui.emit(self.stat_data) |
| 70 | + |
| 71 | + qDebug("Stopping capture thread...") |
| 72 | + |
| 73 | + def connect_camera(self): |
| 74 | + options = gstreamer_pipeline(cam_id=self.device_id, flip_method=self.flip_method) |
| 75 | + self.cap.open(options, self.api_preference) |
| 76 | + # return false if failed to open camera |
| 77 | + if not self.cap.isOpened(): |
| 78 | + qDebug("Cannot open camera {}".format(self.device_id)) |
| 79 | + return False |
| 80 | + else: |
| 81 | + # try to set camera resolution |
| 82 | + if self.resolution is not None: |
| 83 | + width, height = self.resolution |
| 84 | + self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) |
| 85 | + self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) |
| 86 | + # some camera may become closed if the resolution is not supported |
| 87 | + if not self.cap.isOpened(): |
| 88 | + qDebug("Resolution not supported by camera device: {}".format(self.resolution)) |
| 89 | + return False |
| 90 | + # use the default resolution |
| 91 | + else: |
| 92 | + width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| 93 | + height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| 94 | + self.resolution = (width, height) |
| 95 | + |
| 96 | + return True |
| 97 | + |
| 98 | + def disconnect_camera(self): |
| 99 | + # disconnect camera if it's already openned. |
| 100 | + if self.cap.isOpened(): |
| 101 | + self.cap.release() |
| 102 | + return True |
| 103 | + # else do nothing and return |
| 104 | + else: |
| 105 | + return False |
| 106 | + |
| 107 | + def is_camera_connected(self): |
| 108 | + return self.cap.isOpened() |
0 commit comments