配置jetcam
库的结构
使用traitlets来实现状态检查和参数控制,同时进行参数更新的提示。
camera.py
import traitlets
import threading
import numpy as np
class Camera(traitlets.HasTraits):
#声明几个类型
value = traitlets.Any()#
width = traitlets.Integer(default_value=224)
height = traitlets.Integer(default_value=224)
format = traitlets.Unicode(default_value='bgr8')
running = traitlets.Bool(default_value=False)
def __init__(self, *args, **kwargs):
super(Camera, self).__init__(*args, **kwargs)
#初始化类型。
if self.format == 'bgr8':
self.value = np.empty((self.height, self.width, 3), dtype=np.uint8)
#初始化,隐藏的运行验证函数是错误。
self._running = False
#此处的定义是检查报错函数。类似于装饰器的写法
def _read(self):
"""Blocking call to read frame from camera"""
raise NotImplementedError
def read(self):
if self._running:
raise RuntimeError('Cannot read directly while camera is running')
self.value = self._read()
return self.value
def _capture_frames(self):
while True:
if not self._running:
break
self.value = self._read()
#此处观察到running改变时,调用
@traitlets.observe('running')
#是否运行的监测
def _on_running(self, change):
#相机启用
if change['new'] and not change['old']:
# transition from not running -> running
self._running = True
#开始开启一个运行读取的帧的函数,此处未具体实现,在类继承的时候写。
self.thread = threading.Thread(target=self._capture_frames)
self.thread.start()
#如果从真变到假
elif change['old'] and not change['new']:
# transition from running -> not running
#内部决定关闭,线程终止
self._running = False
self.thread.join()
csi_camera.py
下面是CSI继承原相机的代码
from .camera import Camera
import atexit
#终止函数在程序终止是调用。可以在任何地方声明。
#atexit 按注册的相反顺序执行这些函数; 例如注册A、B、C,在解释器终止时按顺序C,B,A运行。
#Note:如果程序是非正常crash,或者通过os._exit()退出,注册的退出函数将不会被调用。
import cv2
import numpy as np
import threading
import traitlets
class CSICamera(Camera):
#因为Camera继承了hastrait,所以该类拥有原属性。
capture_device = traitlets.Integer(default_value=0)
capture_fps = traitlets.Integer(default_value=30)
capture_width = traitlets.Integer(default_value=640)
capture_height = traitlets.Integer(default_value=480)
#初始化
def __init__(self, *args, **kwargs):
super(CSICamera, self).__init__(*args, **kwargs)
#此处使用opencv的接口调用了内部定义的函数,该函数返回了定义相机的gstreamer属性。且参数是类的初始化数据。
try:
self.cap = cv2.VideoCapture(self._gst_str(), cv2.CAP_GSTREAMER)
#尝试读取帧
re, image = self.cap.read()
if not re:
raise RuntimeError('Could not read image from camera.')
except:
raise RuntimeError(
'Could not initialize camera. Please see error trace.')
#此处注册了一个释放相机的函数,是opencv的函数。
atexit.register(self.cap.release)
def _gst_str(self):
return 'nvarguscamerasrc sensor-id=%d ! video/x-raw(memory:NVMM), width=%d, height=%d, format=(string)NV12, framerate=(fraction)%d/1 ! nvvidconv ! video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! videoconvert ! appsink' % (
self.capture_device, self.capture_width, self.capture_height, self.capture_fps, self.width, self.height)
def _read(self):
re, image = self.cap.read()
if re:
return image
else:
raise RuntimeError('Could not read image from camera')
usb_camera.py
from .camera import Camera
import atexit
import cv2
import numpy as np
import threading
import traitlets
class USBCamera(Camera):
capture_fps = traitlets.Integer(default_value=30)
capture_width = traitlets.Integer(default_value=640)
capture_height = traitlets.Integer(default_value=480)
capture_device = traitlets.Integer(default_value=0)
def __init__(self, *args, **kwargs):
super(USBCamera, self).__init__(*args, **kwargs)
try:
self.cap = cv2.VideoCapture(self._gst_str(), cv2.CAP_GSTREAMER)
re , image = self.cap.read()
if not re:
raise RuntimeError('Could not read image from camera.')
except:
raise RuntimeError(
'Could not initialize camera. Please see error trace.')
atexit.register(self.cap.release)
def _gst_str(self):
return 'v4l2src device=/dev/video{} ! video/x-raw, width=(int){}, height=(int){}, framerate=(fraction){}/1 ! videoconvert ! video/x-raw, format=(string)BGR ! appsink'.format(self.capture_device, self.capture_width, self.capture_height, self.capture_fps)
def _read(self):
re, image = self.cap.read()
if re:
image_resized = cv2.resize(image,(int(self.width),int(self.height)))
return image_resized
else:
raise RuntimeError('Could not read image from camera')
utils.py
import cv2
def bgr8_to_jpeg(value, quality=75):
return bytes(cv2.imencode('.jpg', value)[1])
用到的依赖环境
此处只分析了CSI相机的内容,usb的区别不大,两者仅在相机参数部分有区别,通过v4l2src,nvarguscamerasrc两个相机配置系统来确定的。
该程序有缺陷,在正常的相机调用的时候,因为是python文件,会正常的退出,调用atexit来释放掉相机的内容。但是在jupyter运行时,如果使用的是循环,就会出现相机一直占用,而块资源无法释放,会挂掉内核。即从os._exit()退出,此时不会释放相机资源导致再次调用出现问题。
解决方法:修改类,添加手动释放的调用方法,在调用时,通过按键来触发释放函数。
如果想要使用笔记本的镜头,将
CSI或者USB的cv2的接口,从流式相机调用
self.cap = cv2.VideoCapture(self._gst_str(), cv2.CAP_GSTREAMER)
改为
self.cap = cv2.VideoCapture(0)
即可
如果相机未释放,通过camera.cap.release()释放。
相机初始化,可以给出帧率,长宽的一些信息。