|
|
import threading |
|
|
from queue import Queue |
|
|
import queue |
|
|
import cv2 |
|
|
|
|
|
from CPR_Module.Common.logging_config import cpr_logger |
|
|
|
|
|
class ThreadedCamera: |
|
|
def __init__(self, source, requested_fps = 30): |
|
|
|
|
|
|
|
|
self.cap = cv2.VideoCapture(source) |
|
|
if not self.cap.isOpened(): |
|
|
raise ValueError(f"[VIDEO CAPTURE] Unable to open camera source: {source}") |
|
|
cpr_logger.info(f"[VIDEO CAPTURE] Camera source opened: {source}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set_success = self.cap.set(cv2.CAP_PROP_FPS, requested_fps) |
|
|
|
|
|
|
|
|
|
|
|
actual_fps = self.cap.get(cv2.CAP_PROP_FPS) |
|
|
self.fps = actual_fps |
|
|
|
|
|
cpr_logger.info(f"[VIDEO CAPTURE] Requested FPS: {requested_fps}, Set Success: {set_success}, Actual FPS: {actual_fps}") |
|
|
|
|
|
|
|
|
number_of_seconds_to_buffer = 5 |
|
|
queue_size = int(actual_fps * number_of_seconds_to_buffer) |
|
|
self.q = Queue(maxsize=queue_size) |
|
|
cpr_logger.info(f"[VIDEO CAPTURE] Queue size: {queue_size}") |
|
|
|
|
|
|
|
|
self.running = threading.Event() |
|
|
self.running.set() |
|
|
cpr_logger.info(f"[VIDEO CAPTURE] Camera running: {self.running.is_set()}") |
|
|
|
|
|
self.number_of_total_frames = 0 |
|
|
self.number_of_dropped_frames = 0 |
|
|
|
|
|
self.thread = None |
|
|
|
|
|
def start_capture(self): |
|
|
|
|
|
while not self.q.empty(): |
|
|
self.q.get() |
|
|
|
|
|
|
|
|
|
|
|
self.thread = threading.Thread(target=self._reader) |
|
|
cpr_logger.info(f"[VIDEO CAPTURE] Thread initialized: {self.thread}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.thread.daemon = True |
|
|
cpr_logger.info(f"[VIDEO CAPTURE] Thread daemon: {self.thread.daemon}") |
|
|
|
|
|
|
|
|
|
|
|
self.thread.start() |
|
|
|
|
|
def _reader(self): |
|
|
while self.running.is_set(): |
|
|
ret, frame = self.cap.read() |
|
|
if not ret: |
|
|
cpr_logger.info("Camera disconnected") |
|
|
self.q.put(None) |
|
|
break |
|
|
|
|
|
try: |
|
|
self.number_of_total_frames += 1 |
|
|
self.q.put(frame, timeout=0.1) |
|
|
except queue.Full: |
|
|
cpr_logger.info("Frame dropped") |
|
|
self.number_of_dropped_frames += 1 |
|
|
|
|
|
def read(self): |
|
|
return self.q.get() |
|
|
|
|
|
def release(self): |
|
|
|
|
|
cpr_logger.error(f"[VIDEO CAPTURE] Total frames: {self.number_of_total_frames}, Dropped frames: {self.number_of_dropped_frames}") |
|
|
|
|
|
self.running.clear() |
|
|
|
|
|
|
|
|
self.cap.release() |
|
|
|
|
|
|
|
|
self.thread.join(timeout=1.0) |
|
|
|
|
|
if self.thread.is_alive(): |
|
|
cpr_logger.info("Warning: Thread didn't terminate cleanly") |
|
|
|
|
|
|
|
|
def isOpened(self): |
|
|
return self.running.is_set() and self.cap.isOpened() |
|
|
|
|
|
def __del__(self): |
|
|
if self.running.is_set(): |
|
|
self.release() |