LISHUZUOXUN_yangjiang/MCamera/camera.py

213 lines
7.0 KiB
Python
Raw Normal View History

2024-09-23 14:54:15 +08:00
import threading
import time
import cv2
from AcrossPlatform.get_platform import SYS_PLATFORM, WINDOWS, LINUX
from Speaker.speak_base import beep
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
CAMERA_ID = "camera_id"
FRAME_DAT = "frame_data"
CATCH_TIME = "catch_time"
RESIZE_SIZE = (640, 480)
class CameraEvent(object):
"""An Event-like class that signals all active clients when a new frame is
available.
"""
def __init__(self):
self.events = {}
def wait(self):
"""Invoked from each client's thread to wait for the next frame."""
ident = get_ident()
if ident not in self.events:
# this is a new client
# add an entry for it in the self.events dict
# each entry has two elements, a threading.Event() and a timestamp
self.events[ident] = [threading.Event(), time.time()]
return self.events[ident][0].wait()
def set(self):
"""Invoked by the camera thread when a new frame is available."""
now = time.time()
remove = None
for ident, event in self.events.items():
if not event[0].isSet():
# if this client's event is not set, then set it
# also update the last set timestamp to now
event[0].set()
event[1] = now
else:
# if the client's event is already set, it means the client
# did not process a previous frame
# if the event stays set for more than 5 seconds, then assume
# the client is gone and remove it
if now - event[1] > 1:
remove = ident
if remove:
del self.events[remove]
def clear(self):
"""Invoked from each client's thread after a frame was processed."""
self.events[get_ident()][0].clear()
class Camera(object):
thread = {} # background thread that reads frames from camera
frame = {} # current frame is stored here by background thread
queue_frame = []
cache_time = 1
frame_cache = {}
last_access = 0 # time of last client access to the camera
event = {}
# 相机所有源
source_list = set()
# 相机记录
record_signal = False
# 错误计数
error_counting = 0
2024-09-23 16:09:59 +08:00
max_error = 50
2024-09-23 14:54:15 +08:00
def __init__(self):
"""Start the background camera thread if it isn't running yet."""
if not Camera.thread:
for source in Camera.source_list:
Camera.event[source] = CameraEvent()
Camera.last_access = time.time()
# start background frame thread
Camera.thread[source] = threading.Thread(target=self._thread, args=(source,))
Camera.thread[source].start()
# wait until frames are available
while self.get_frame(video_source=source) is None:
time.sleep(0.1)
@classmethod
def add_source(cls, source):
Camera.source_list.add(source)
@classmethod
def get_cache(cls):
if Camera.queue_frame:
return Camera.queue_frame.pop(0)
@classmethod
def get_all_cache(cls):
result = Camera.queue_frame.copy()
cls.clear_cache()
return result
@classmethod
def clear_cache(cls):
Camera.queue_frame.clear()
@classmethod
def start_record(cls):
cls.record_signal = True
@classmethod
def stop_record(cls):
cls.record_signal = False
@classmethod
def get_frame(cls, video_source=0):
"""Return the current camera frame."""
Camera.last_access = time.time()
# wait for a signal from the camera thread
Camera.event[video_source].wait()
Camera.event[video_source].clear()
return Camera.frame[video_source]
@staticmethod
def reload_camera(video_source):
# 根据平台导入对应的包
if SYS_PLATFORM == WINDOWS:
camera = cv2.VideoCapture(video_source, cv2.CAP_DSHOW)
elif SYS_PLATFORM == LINUX:
camera = cv2.VideoCapture(video_source, cv2.CAP_V4L2)
else:
camera = None
if camera:
# 设置摄像头帧率为30
camera.set(cv2.CAP_PROP_FPS, 20)
# 设置摄像头图像大小为640x480
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
return camera
@staticmethod
def reload_camera_normally(video_source):
# 根据平台导入对应的包
camera = cv2.VideoCapture(video_source)
if camera:
# 设置摄像头帧率为30
camera.set(cv2.CAP_PROP_FPS, 20)
# 设置摄像头图像大小为640x480
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
return camera
@staticmethod
def camera_restart(video_source):
camera = Camera.reload_camera(video_source)
while not camera.isOpened():
print('无法打开摄像头,正在重新初始化,请稍后!')
beep()
camera = Camera.reload_camera(video_source)
return camera
@staticmethod
def camera_restart_normally(video_source):
camera = Camera.reload_camera_normally(video_source)
while not camera.isOpened():
print('无法打开摄像头,正在重新初始化,请稍后!')
beep()
camera = Camera.reload_camera_normally(video_source)
return camera
@staticmethod
def frames(video_source):
camera = Camera.camera_restart(video_source)
while True:
try:
ret, img = camera.read()
if not ret:
Camera.error_counting += 1
if Camera.error_counting > Camera.max_error:
camera = Camera.camera_restart_normally(video_source)
else:
camera = Camera.camera_restart(video_source)
# 防止摄像机松动导致帧错误
if img is not None:
yield img
except cv2.Error:
continue
@classmethod
def _thread(cls, video_source):
"""Camera background thread."""
print(f'Starting CAMERA@{video_source} thread.')
frames_iterator = cls.frames(video_source)
for frame in frames_iterator:
if cls.record_signal:
frame_pkg = {
CAMERA_ID: video_source,
FRAME_DAT: frame,
CATCH_TIME: time.time()
}
Camera.queue_frame.append(frame_pkg)
Camera.frame[video_source] = frame
Camera.event[video_source].set() # send signal to clients