Picamera2 Python library

发布时间 2023-11-15 22:37:57作者: 睡觉督导员

安装

目前2022年之后的镜像已经预装了picamera2, 如果需要安装使用

sudo apt install -y python3-picamera2

此包会安装X-windows和GUI依赖, 如果不需要使用,

sudo apt install -y python3-picamera2 --no-install-recommands

也可以使用pip安装

sudo apt install -y python3-libcamera python3-kms++
sudo apt install -y python3-prctl libatlas-base-dev ffmpeg python3-pip
sudo apt install -y python3-pyqt5 python3-opengl # only if you want GUI features
pip3 install numpy --upgrade
# 首先要安装上述依赖. 然后再安装picamrea2
pip3 install picamera2

自检例子

from picamera2 import Picamera2, Preview
import time

picam2 = Picamera2()  # 打开系统
camera_config = picam2.create_preview_configuration()  # 生成窗口配置
picam2.configure(camera_config)  # 使用该预览配置配置照相机系统
picam2.start_preview(Preview.QTGL)  # 打开 X windows窗口
# picam2.start_preview(Preview.DRM)  # 非X windows窗口
picam2.start()  # 开始运行相机
time.sleep(2)
picam2.capture_file("test.jpg")  # 捕获一张照片

高级接口

适用于只想快速使用的情况, 无需关注过多细节.

from picamera2 import Picamera2
picam2 = Picamera2()
picam2.start_and_capture_file("test.jpg")  # 直接捕获照片
picam2.start_and_capture_files("test%s.jpg") # 捕获多张照片
picam2.start_and_record_video("test.mp4", duration=5) # 捕获一段视频

安装其他软件

opencv
sudo apt install -y python3-opencv
sudo apt install -y opencv-data
TensorFlow Lite
pip3 install tflite-runtime
FFmpeg
sudo apt install -y ffmpeg

预览窗口

树莓派有四种预览窗口, 但是都使用同一种窗口参数

x          预览窗口的x偏移

y          预览窗口的y偏移

width      预览窗口宽度

height     预览窗口高度

transform  图像的水平反转和垂直翻转
           Transform(hflip=1)  水平翻转
           Transform(vflip=1)  垂直翻转
           Transform(hflip=1, vflip=1) 水平和垂直翻转

例子. 下面会在屏幕(100, 200)位置设置一个800*600像素的水平镜像显示的预览窗口

from picamera2 import Picamera2, Preview
from libcamera import Transform

picam2 = Picamera2()
picam2.start_preview(Preview.QTGL, x=100, y=200, width=800, height=600, transform=Transform(hflip=1))
picam2.start()

这里翻转只能针对预览窗口, 不应用于实际图片数据.

QTGL

picam2.start_preview(Preview.QTGL)  # 推荐 使用界面, 如果是远程访问,最好用QT

DRM/KMS

picam2.start_preview(Preview.DMR)  # X windows无法使用时使用, 不能使用鼠标. Raspberry Pi OS Lite的首选.

QT

picam2.start_preview(Preview.QT)  # 不推荐使用, 因为相比于QTGL,没有硬件加速. 比较费计算资源.

NULL

什么也不做, 只是驱动摄像系统. 接收相机图像, 然后传递给应用程序. 空窗口是默认启动的, 所以要启动其他窗口, 就需要提前设置(picam2.start_preview(Preview.QTGL)).

picam2.start_preview(Preview.NULL)

开始停止预览窗口

开启预览窗口,需要使用start_preview函数. 此函数接受一个参数, 参数的值可以是

  • None 没有预览窗口启动. 需要应用自己的代码来驱动相机系统(????)
  • False 使用NULL窗口
  • True 程序决定使用哪个窗口更合适.
  • Preview枚举中的一个值. 自己指定窗口

你建议中途停止/开启预览窗口, 有可能会导致相机帧的丢失.

Picamera2.start函数也有一个show_preview参数, 可以使用上述四个值, 来省略start_preview函数的使用. 但是Picamera2.stop函数不能停止预览窗口, 还需要主动调用stop_preview函数来关闭预览窗口.

设置预览窗口标题栏

通过Picamera2title_fields属性来设置预览窗口的标题.

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.start(show_preview=True)
picam2.title_fields = ["ExposureTime", "AnalogueGain"]

camera的配置

在创建Picamera2对象之后, 通常会生成一个相机配置, 来应用到相机系统. 一般可以直接使用提供的函数直接生成配置, 然后通过Picamera2.configure函数使用.

提供了三个生成配置的函数

Picamera2.create_preview_configuration 生成一个适用于预览窗口的配置,

Picamera2.create_still_configuration 生成一个适用于捕获高质量静止图像的配置

Picamera2.create_video_configuration 生成一个捕获视频文件图像的配置

from picamera2 import Picamera2, Preview

picam2 = Picamera2()
camera_config = picam2.create_preview_configuration()
picam2.configure(camera_config)  # 在使用之前, 可以修改配置
picam2.start()

上图是树莓派相机的工作流程.首先由相机产生数据, 之后由CSI将数据写入内存.然后ISP读取数据,进行处理. ISP会产生两路输出, 第一路为Main Stream其格式可以是RGB或者YUV. 另一路是低质量的输出模式,其格式是YUV, 他不大于主图像. 最后从内存直接落盘的数据是原始图像.

配置可以分成

通用配置, 可以用在整个相机系统和ISP上.

ISP每个输出流上的配置. main stream总是交给应用程序. 如果没有指定配置,就会使用默认配置.

通用配置

  • transform 垂直翻转和水平翻转

  • color_space 颜色空间, 主流和低质量流使用相同的参数. 原始流总是在一个相机特定的颜色空间中。

  • buffer_count 设置缓冲区集数. 一组缓冲区代表已请求的每个流的一个缓冲区

  • queue 是否允许系统为捕获请求准备一个帧队列。

  • display 选择需要展示的流的名称, 并不影响实际图像. 只在picamera2中有

  • encode 设置录像的编码格式. 不会影响图像. 只在picamera2中有

transform

from libcamera import Transform

Transform()
Transform(hflip=True)
Transform(vflip=True)
Transform(hflip=True, vflip=True)

使用方式

from picamera2 import Picamera2
from libcamera import Transform
picam2 = Picamera2()
preview_config = picam2.create_preview_configuration(transform=Transform(hflip=True))

colorfpace

from libcamera import ColorSpace
ColorSpace.Sycc()
ColorSpace.Rec709()

使用方式

from picamera2 import Picamera2
from libcamera import ColorSpace

picam2 = Picamera2()
preview_config picam2.create_preview_configuration(colour_space=ColorSpace.Sycc())

当不指定参数的时候会使用默认值.默认值根据不同情况来选择.

create_preview_configurationcreate_till_configuration 默认使用Sycc

create_preview_configuration中当main stream选择RGB格式时,是Sycc. 对于YUV格式,如果分辨率小于1280x720,它将选择SMPTE 170M,否则为Rec709.


流配置

每个流都可以有一个配置, 可以通过给配置产生函数, 设置对应的参数来获取对应的流配置, 比如main lores raw. 默认情况下, 主 流的配置是一直产生的, 所以main=的参数可以省略, 比如

from picamera2 import Picamera2

picam2 = Picamera2()
config = picam2.create_preview_configuration(lores={})  # 在生成main流的同时, 生成了lores流的配置.

配置的参数可以只传一个字典就行(应该是只要不是None就生成)

我们可以配置的参数主要是两类sizeformat. size主要指定图像的大小, format指定图像的格式.

库还提供了一个align_configuration函数用来校验图像大小是不是最佳大小.

from picamera2 import Picamera2

picam2 = Picamera2()
config = picam2.create_preview_configuration({"size": (808, 606)})
picam2.align_configuration(config)  # 校验参数.
picam2.configuration(config)

图像格式可以选用以下几种

XBGR8888 每个像素32位, 最后有一个虚拟的255值. 所以像素看起来是 [R,G,B,255]

XRGB8888 同上,但是顺序是 [B,G,R,255]

RGB888 每个像素24位, 顺序是[B,G,R]

BGR888 同上, 顺序是[R,G,B]

YUV420 Y平面值, U平面值, V平面值

lores 流默认使用YUV420格式.

原始流配置

raw stream 也可以调整图像大小,但是这是有限制的并不像ISP那样可以任意调整. 使用传感器模式提供更为准确的模式控制.

from pprint import *
from picamera2 import Picamera2
picam2 = Picamera2()
pprint(picam2.sensor_modes)  # 打印相机的配置. 这种配置是有多组可供选择的, 也就是说只能选择用或者不用, 而不是进行修改

bit_depth = 12 每个像素占用的位数

crop_limits = (0,0,4056,3040) 这告诉我们在全分辨率传感器输出中该模式的确切视野

exposure_limits = (114, 127156999) 曝光的最大值和最小值(毫秒)

format = SRGGB12_CSI2P 包装传感器格式。当请求原始流时,这可以作为“格式”传递

fps 10.0 最大帧率

unpacked = SRGGB12 如果需要解压缩的原始图像,则使用此格式代替原始流请求中的早期格式

配置和运行时配置

这也许不是正式的配置, 但是有时是需要的.

from picamera2 import Picamera2
picam2 = Picamera2()
picam2.create_video_configuration()["controls"]
>> {'NoiseReductionMode': <NoiseReductionMode.Fast: 1>, 'FrameDurationLimits': (33333, 33333)}

配置生成方法提供了与用例对应的推荐运行时控制值. 这些值可以被覆盖或更改, 但由于最优值或通常的值有时有点技术性,所以自动提供它们是很有帮助的. 推荐的配置是降噪和采样率. 修改配置

from picamera2 import Picamera2

picam2 = Picamera2()
config = picam2.create_video_configuration(controls={"FrameDurationLimits": (40000, 40000)})

配置对象

CameraConfiguration类可以代替之前的那些配置参数.

Picamera2对象在创建的时候就包含了三个配置: preview_configuration, still_configuration, video_configuration. CameraConfiguration和 "preview", "still", "video"都可以传递个configure 函数使用.

改变的主流的尺寸

from picamera2 import Picamera2

picam2 = Picamera()
picam2.preview_configuration.main.size = (800, 600)
picam2.configure("preview")

上述代码等于 picam2.create_preview_configuration({"size": (800, 600)}) 同时上述的main 可以省略 picam2.previw_configuration.size = (800, 600)

配置低质量流和 原始流

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.preview_configuration.enable_lores()
picam2.preview_configuration.lores.size = (320, 240)
picam2.configure("preview")

流对齐

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.preview_configuration.main.size = (800, 600)
picam2.preview_configuration.main.format = "YUV420"
picam2.preview_configuration.align()
picam2.configure("preview")

提供控制值

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.video_configuration.controls.FrameDurationLimits = (40000, 40000)
# picam2.video_configuration.controls.FrameRate = 25.0  替代方案.
picam2.configure("video")

相机控制和属性

在相机运行时可以被改变的称为控制, 不能被改变的称为配置.

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.camera_controls  # 检查相机的控制.

控制中不包含的是 1. 图像的分辨率 2. 像素的格式. 这些应该是配置设置的.

如何设置相机控制

设置控制的时间点:

  1. 设置相机配置时. 控制参数会随着配置存储, 所以他们会在重新请求配置时重新使用. 参数在相机工作前启用.
  2. 在配置相机之后, 并在相机开始工作之前. 控制在相机启动前生效, 但是并不会被存储, 所以并不会自动被重用.
  3. 在相机启动之后. 通常是在一些延迟之后才会应用控制设置.

可以通过set_controls来设置控制. 同样这里也可以有一个对象类型的变量来控制.

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.configura(picam2.create_preview_configuration())  # 在配置之后
picam2.set_controls({"ExposureTime": 10000, "AnalogueGain": 1.0})
picam2.start() # 相机启动之前.

使用Controls类的方式设置控制

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.configure("preview")
picam2.controls.ExposureTime = 10000
picam2.controls.AnalogueGain = 1.0
picam2.start()

在相机开始之后设置控制

from picamera2 import Picamera2
picam2 = Picamera2()
picam2.configure("preview")
picam2.start()
with picam2.controls as controls: # 这里使用with是因为相机开始后, 需要保证两个配置同时应用到同一个帧上.
  controls.ExposureTime = 10000
  controls.AnalogueGain = 1.0

对焦控制

自动对焦和状态

自动对焦有三种模式, 三种模式都可以根据读取图像的返回值AfState来监控.

  1. Manual

  2. Auto

  3. Continuous

持续自动对焦

from picamera2 import Picamera2
from libcamera import controls

picam2 = Picamera2()
picam2.start(show_preview=True)
picam2.set_controls({"AfMode": controls.AfModeEnum.Continuous})

手动设置镜头位置

from picamera2 import Picamera2
from libcamera import controls

picam2 = Picamera2()
picam2.start(show_preview=True)         # 相机设置为手动模式    镜头位置设置为无限大
picam2.set_contols({"AfMode": contorls.AfModeEnum.Manual, "LensPosition": 0.0})

LensPosition有三个值 minimum, maximum, default三个值, 那么上面的设置设置的是哪个值.

触发自动对焦循环

自动模式下触发对焦循环, 推荐使用一个辅助函数, 它可以监控对焦算法的状态.

from picamera2 import Picamera2
from libcamera import controls

picam2 = Picamera2()
picam2.start(show_preview=True)
success = picam2.autofocus_cycle()

如果镜头成功聚焦, 函数返回True, 否则返回False. 如果应用程序希望避免在自动对焦循环运行时发生阻塞, 可以将最后一行替换.

job = picam2.autofocus_cycle(wait=False)
# 这里可以做其他事情,
success = picam2.wait(job)  # 当想确认镜头是否设置完成时.

其他对焦控制

AfRange: 调整算法搜索的焦距

AfSpeed: 尝试或快或慢地运行自动对焦算法

AfMetering/AfWindows: 尝试或快或慢地运行自动对焦算法

picam2.camera_properties  # 查看控制属性值.

捕获图像和请求

相机捕获到的图像同尝试一个numpyarrays类型. Picamera2 可以和numpy OpenCV一起工作的很好.

picamera2 使用的术语

  • arrays 通常是二维或者三维numpyarrays.
  • images 这指的是Python Image Library(PIL) 图像.
  • buffers 这里所说的缓冲区简单地指图像存储为一维numpy数组存储的整个内存块, 但二(或三维)数组形式通常更有用.

还有捕获功能, 可以将图像直接保存到文件中, 并在相机模式之间切换, 从而将快速帧率预览与高分辨率捕获相结合.

捕获arrays

capture_array 函数会捕获传入的第一个参数指定的流的数据图像, 默认流是main.

from picamera2 import Picamera2
import time
picam2 = Picamera2()
picam2.start()
time.sleep(1)
array = picam2.capture_array("main")  # 捕获的是三维numpy array.

具体是几维的数组, 取决于图像是什么格式.

(height, width, 3) 是3通道RGB格式的形式

(height, width, 4) 是4通道RGBA格式的形式

(height * 3/2, width) 是YUV420格式

捕获PIL图像

同上, 但是使用capture_image函数

from picamera2 import Picamera2
import time
picam2 = Picamera2()
picam2.start()
time.sleep(1)
array = picam2.capture_image("main")  # 捕获的是三维numpy array.

切换相机模式和捕获

一个常见的用例是在一种模式下运行相机,它可以实现快速帧率显示,但也可以切换到(较慢的帧率)高分辨率捕获模式, 以获得最佳质量的图像. 提供两个函数

switch_mode_and_capture_array
switch_mode_and_capture_image
from picamera2 import Picamera2
import time

picam2 = Picamera2()
capture_config = picam2.create_still_configuration()
picam2.start(show_preview=True)

time.sleep(1)
array = picam2.switch_mode_and_capture_array(capture_config, "main")  # 捕获图像array后会自动切回预览模式.

也可实现手动切换

from picamera2 import Picamera2
import time 

picam2 = Picamera2()
preview_config = picam2.create_preview_configuration()
capture_config = picam2.create_still_configuration()  # 创建配置
picam2 = picam2.configuration(preview_config)  # 配置好
picam2.start(show_preview=True)  # 开始相机

time.sleep(1)
picam2.switch_mode(capture_config)   # 切到捕获图像
array = picam2.capture_array("main") # 捕获图像
picam2.switch_mode(preview_config)   # 再切回预览

直接捕获到文件或者类似文件的对象中

捕获函数都有将图像直接保存到文件中的对应项

# 省略其他代码
picam2.switch_mode_and_capture_file(capture_config, "image.jpg")

具体的文件格式是根据文件名推导出来的.

另一种形式是直接将图像放在缓存里. 因为没有文件名, 所以“文件”的格式必须由format参数给出

import io

data = io.BytesIO()
picam2.capture_file(data, format="jpeg")  # 可以是jpeg png bmp gif
设置图像质量

可以通过Picamera2options属性设置图像质量的全局设置. options可以设置两个值

  • quality 90 jpeg图像的质量, 0表示质量最低, 95最高
  • compress_level 1 png压缩级别, 0表示不压缩, 1表示最快压缩, 9最慢的压缩.
from picamera2 import Picamera2
import time

picam2 = Picamrea2()
picam2.options["quality"] = 95
picam2.options["compress_level"] = 2
picam2.start()

time.sleep(1)
picam2.capture_file("test.jpg")
picam2.capture_file("test.png")

捕获的元数据

元数据描述了用于捕获该图像的相机控制的设置.

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.start()

metadata = picam2.capture_metadata()
print(metadata["ExposureTime"], metadata["AnalogueGain"])

metadata是将应用程序与相机帧同步的一种好方法. 第一次调用捕获元数据(或者实际上是任何捕获函数)通常会立即返回,因为Picamera2通常会在内部保留最后一个相机图像. 而在之后的调用中需要等待返回新的一帧数据之后才会返回.

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.start()

for i in range(30):
    metadata = picam2.capture_metadata()
    print("Frame", i, "has arrived")

对象风格访问元数据

from picamera2 import Picamera2, Metadata

picam2 = Picamera2()
picam2.start()

metadata = Metadata(picam2.capture_metadata())
print(metadata.ExposureTime, metadata.AnalogueGain)

一次捕获多个图像

希望通过和获取一个图像一样, 只执行一行命令就获取多个图像.之前的一些函数都有一次获取多个图像的版本

单个图像 多个图像
Picamera2.capture_buffer Picamera2.capture_buffers
Picamera2.switch_mode_and_capture_buffer Picamera2.switch_mode_and_capture_buffers
Picamera2.capture_array Picamera2.capture_arrays
Picamera2.switch_mode_and_capture_array Picamera2.switch_mode_and_capture_arrays

需要注意的是:

要使用流名称的列表来代替, 之前使用的流的名字.

返回值是一个元组, 第一个元素是数组的列表(按照给出名称的顺序), 第二个元素是图像的元数据.

from picamera2 import Picamera2

picam2 = Picamera2()
config = picam2.create_preview_configuration(lores={})
picam2.configure(config)
picam2.start()

(main, lores), metadata = picam2.capture_arrays(["main", "lores"])

为了方便使用这些图像,Picamera2有一个小的助手库,它可以将数组转换为PIL图像,并将它们保存到一个文件中,等等.

函数 描述
picam2.helpers.make_array 从一维数组返回一个二维或者三维数组
picam2.helpers.make_image 从一维数组中返回一个PIL图像
picam2.helpers.save 保存一个PIL图像到文件
picam2.helpers.save_dng 保存一个PIL图像到dng图像

这些助手函数可以直接从Picamera2对象中访问. 如果我们想捕获单个缓冲区并使用这些助手之一来保存文件, 我们可以使用

from picamera2 import Picamera2

picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration())
picam2.start()

(buffer, ), metadata = picam2.capture_buffers(["main"])
img = picam2.helpers.make_image(buffer, picam2.camera_configuration()["main"])
picam2.helpers.save(img, metadata, "file.jpg")

当我们捕获数组或图像时,图像数据会被复制,这样相机系统就可以保持它所使用的所有内存缓冲区,并以相同的方式继续运行.然而,当我们捕获一个请求时,我们只从相机系统中借用了它和所有的内存缓冲区,而且还没有复制任何东西.当我们完成了请求时,它必须使用请求的释放方法返回回相机系统.

from picamera2 import Picamera

picam2 = Picamera2()
picam2.start()

request = picam2.capture_request()
request.save("main", "image.jpg")
print(request.get_metadata())  # 这个图像的元数据
request.release()

所有Picamera2的捕获方法都是通过在已完成的请求类中的方法来实现的,一旦我们有了这个请求,我们就可以直接调用它们.通信内容如下表所示

picamera2 函数 CompletedRequest等效值
Picamera2.capture_buffer CompletedRequest.make_buffer
Picamera2.capture_array CompletedRequest.make_array
Picamera2.capture_file CompletedRequest.save/CompletedRequest.save_dng
Picamera2.capture_image CompletedRequest.make_image

从相机的线程中移除处理逻辑

通常, 当我们使用像Picamera2.capture_file这样的函数时, 捕获图像、压缩为(例如)JPEG, 并将其保存到文件的处理发生在通常的相机处理循环中. 当这种情况发生时, 相机事件的处理被阻塞, 相机系统可能会丢弃一些帧. 通常这无关紧要, 但在有些情况下, 我们可能更希望所有的处理都发生在其他地方.

举个例子, 如果我们在录制视频, 想要同时捕获JPEG, 同时将丢弃任何视频帧的风险降到最低, 那么将该处理从相机循环中移出将是有益的.

者通过捕获请求和调用request.save就很容易完成. 相机事件依然可以并行处理. 唯一的缺点是,在这个请求最终被释放之前, 相机系统必须少使用一组缓冲区. 但是, 通过相机配置的buffer_count参数分配一个或多组额外的缓冲区, 可以缓解这种情况.

异步捕获

有时候在捕获一张图片的时候不阻塞相机的线程执行,也是非常有用的.

所有的captureswitch_mode_and_capture函数有两个额外的参数:

  • wait 在操作完成之前是否阻塞.
  • 当操作完成时, 将调用的函数.

两者的默认值都是None, 设置不同值, 会有不同结果

  • waitsignal_function都是None: 这就是通常的情况, 函数将阻塞,知道操作完成.
  • waitNone, 提供signal_function: 该函数(设置wait,signal_function的函数)将不会阻塞, 但即使操作没有完成, 也会立即返回. 当操作完成时, 调用者应该使用所提供的signal_function来通知应用程序
  • waitsignal_function都不是None: 有wait决定该函数是不是阻塞, signal_function函数依然会执行.
  • 设置waitFalse 不提供signal_function函数: 该函数会立即返回, 同时你可以稍后阻塞该函数完成.

通常以阻塞的方式调用一个函数时, 该函数会返回其"正常"结果. 当以非阻塞的方式调用时, 该函数将返回作业的句柄, 如果需要, 通过这个句柄阻止稍后作业的完成.

完成异步请求

当使用上述的函数进行异步任务时, 接收到返回的句柄, 可以使用Picamera2.wait(job)来完成程序.

result = picam2.wait(job)

等待函数返回最初阻止操作时本应返回的结果. 不必等待一个作业完成后再提交另一个作业.他们将按照提交的顺序完成工作.

from picamera2 import Picamera2
import time

picam2 = Picamera2()
still_config = picam2.create_till_configuration()
picam2.configure(picam2.create_preview_configuration())
picam2.start()
time.sleep(1)
job = picam2.switch_mode_and_capture_file(still_config, "test.jpg", wait=False)

# 继续进行其他任务
for i in range(20):
    time.sleep(0.1)
    print(i)

# 取回操作的结果
metadata = picam2.wait(job)

高级捕获API

这些接口就是不用了解内部细节, 开箱即用, 基本不用配置.

Picamera2.start_and_capture_file

自动配置开始相机, 然后返回捕获的结果. 可以配置的参数:

  • name 默认image.jgp. 保存文件的名字
  • delay 默认1 捕获图像前延迟的秒数. 值为0 是不延迟
  • preview_mode 默认preview 要用于该操作的预览阶段的照相机配置. 默认值表示要使用Picamera2对象的preview_configuration字段中的配置. 只有延迟大于0时才有预览.
  • capture_mode 默认still 捕获图像设置的相机配置. 默认值指使用till_configuration的配置.也可使用其他配置.
  • show_preview 默认True 是否显示预览窗口. 默认情况下, 预览图像仅显示在操作的预览阶段时显示,除非使用显示参数提供的相机配置覆盖此行为. 如果后续调用更改了该参数的值, 我们注意到应用程序应该在调用Picamera2.stop_preview方法.
from picamera2 import Picamera2
picam2 = Picamera2()
picam2.start_and_capture_file("test.jpg")  # jpeg, png, bmp, gif

Picamera2.start_and_capture_files

配置参数

  • name 默认image{:03d}.jpg 保存已经捕获的图像名. 应该包括格式化的内容,否则图像会互相覆盖.
  • initial_delay 默认值 1 捕获第一张图像之前延迟的秒数. 0 不延迟.
  • preview_mode 默认值 preview 同上
  • capture_mode 默认值 still 同上
  • num_files 默认值 1 捕获图像的数量
  • delay 默认值 1 在第一张图片之后捕获每张图片之间的间隔. 如果为0 那就没有延迟了
  • show_preview 默认值 True 同上
picam2.start_and_capture_files("test{:d}.jpg", initial_delay=5, num_files=10)
picam2.start_and_capture_files("test{:d}.jpg", initial_delay=0, delay=0, num_files=10)

在实践中,捕获的速率将受到编码和保存JPEG文件所需的时间的限制. 为了更快地捕捉,可能值得将视频保存为MJPEG格式.

捕获视频

Picamera2中,捕获和编码视频的过程基本上是自动的. 应用程序只需要定义它要使用什么来压缩图像数据的编码器,以及它希望如何输出这个压缩的数据流.

例子

from picamera2.encoders import H264Encoder
from picamera2 import Picamera2
import time

picam2 = Picamera2()
video_config = picam2.create_video_configuration()
picam2.configure(video_config)

encoder = H264Encoder(bitrate=10000000)
output = "test.h264"

picam2.start_recording(encoder, output)
time.sleep(10)
picam2.stop_recording()

在这个例子中, 我们使用H.264编码器. 对于输出对象,为了方便,我们可以使用字符串. 这将被解释为一个简单的输出文件.对于配置相机, 创建create_video_configuration是一个很好的起点, 因为它将使用更大的buffer_count来减少丢帧的风险.

我们还使用了方便的start_recordingstop_recording功能, 它同时启动和停止编码器和相机. 有时, 分离这两个操作是很有用的. 例如, 可能希望在多次启动和停止录制的同时, 让摄像机始终运行. 因此,start_recording可以用:

picam2.start_encoder(encoder, output)
picam2.start()

picam2.stop()
picam2.stop_encoder()

编码

所有的视频编码器都可以由决定输出的质量的参数构建, 比如H.264比特率的编码器. 这些参数可以省略, 而给start_encoderstart_recording提供质量参数替代:

  • Quality.VERY_LOW
  • Quality.LOW
  • Quality.MEDIUM 如果未指定参数,则这是这两个函数的默认值
  • Quality.HIGH
  • Quality.VERY_HIGH

这个质量参数只有在编码器没有通过显式的编解码器特定的参数时才有影响.它可以这样使用:

from picamera2.encoders import H264Encoder, Quality
from picamera2 import Picamera2
import time

picam2 = Picamera2()
picam2.configure(picam2.create_video_configuration())

encoder = H264Encoder()
picam2.start_recording(encoder, "test.h264", Quality.HIGH)
time.sleep(10)
picam2.stop_recording()

编码器将根据所提供的质量参数进行适当的调整, 这属于努力去提供最好的. 如果质量不达标, 显示的提供质量的参数.

H264Encoder

通过V4L2内核驱动程序访问编码器,支持多达1080p30帧的数据.

  • bitrate 默认值 None 指定比特率. 默认值None将导致编码器在启动时根据质量来选择适当的比特率.
  • repeat 默认值 False 是否在每一个内帧重复流序列开始的头.
  • iperiod 默认值 None 从一个i帧到下一个i帧的帧数. None将由硬件自行决定, 默认为60帧.

JpegEncoder

这个编码器实现了多线程软件JPEG编码, 它也可以用作 motion JPEG (MJPEG)编码器

  • num_threads 默认值 4 多少线程用于编码
  • q 默认值 None JPEG质量数, 默认值None将导致编码器在启动时根据质量值选择一个合适的值.
  • colour_space 默认值 None 软件将为被编码的流选择正确的“颜色空间”, 所以这个参数通常应该保持为空.
  • colour_subsampling 默认值 420 这是YUV的形式, 编码器将在编码之前将RGB像素转换到内部. 因此, 它决定了一个JPEG解码器是否会看到一个YUV420图像或其他一些东西. 有效值 444(YUV444), 422(YUV422), 440(YUV440), 420(YUV420), 411(YUV411)或者Gray

这个编码器可以接受 三通道 RGB(RGB888 or BGR888) 四通道 RGBA(XBGR8888 or XRGB8888)数据. 但是不能接受YUV格式的数据.

MJPEGEncoder

MJPEGEncoder 类使用树莓派的内置硬件实现了一个MJPEG编码器,可以通过V4L2内核驱动程序访问.

  • bitrate 默认值 None 要使用的比特率(以每秒比特为单位).默认值None将导致编码器在启动时根据质量来选择适当的比特率.

这个编码器可以接受 三通道 RGB(RGB888 or BGR888) 四通道 RGBA(XBGR8888 or XRGB8888) 或者 YUV格式的数据.

NULL Encoder

基编码器类可以用作“空”编码器, 也就是说, 一个什么都不做的编码器. 它输出的帧与传递给它的帧完全相同, 而不需要进行任何压缩或处理. 它可以用于记录YUVRGB帧(根据被记录的流的输出格式), 甚至,如下面的示例,由图像传感器输出的原始拜耳帧.

from picamera2 import Picamera2
from picamera2.encoders import Encoder
import time

picam2 = Picamera2()
config = picam2.create_video_configuration(raw={}, encode="raw")
picam2.configure(config)

encoder = Encoder()
picam2.start_recording(encoder, "test.raw")
time.sleep(5)
picam2.stop_recording()

输出对象

输出对象直接从编码器接收编码的视频帧,并通常将它们转发到文件或网络套接字. 输出对象通常是用它的构造函数来生成的,尽管一个简单的字符串可以传递给start_encoderstart_recording函数, 这将导致自动生成FileOutput.

FileOutput

FileOutput 由单个file参数构建:

  • None 将导致丢弃数据
  • 一个字符串 通常会打开一个文件
  • 类文件对象 它可能是一个使用io创建的内存缓冲区io.BytesIO() 或者网络套接字.
from picamera2.outputs import FileOutput
output = FileOutput("test.h264")  # 文件对象

import io
buffer = io.Bytes()
output = FileOutput(buffer)  # 内存缓冲

import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.connect(("REMOTEIP", 10001))
    stream = sock.makefile("wb")
    output = FileOutput(stream)  # 网络套接字

FfmpegOutput

FfmpegOutput类将编码过的帧转发给FFmpeg程序. 这为一些相当复杂的新输出类型打开了大门,包括MP4文件,甚至是音频,但可能需要关于FFmpeg本身的大量知识.

类构造函数有一个必需的参数,即输出文件名,其他所有参数都是可选的

  • output_filename 通常可以传给类似"test.mp4", 但是,它也可以被用作FFmpeg命令行的输出部分,因此同样可以包含"test.ts"(记录MPEG-2传输流), 甚至-f mpegts udp://<ip-addr>:>端口,将MPEG-2传输流转发到给定的网络套接字.
  • audio 默认值 False 如果有麦克风, 可以打开它
  • audio_device 默认值 default 麦克风的名字
  • audio_sync 默认值 -0.3 音频和视频之间的以秒为单位的时间转移. 这可能需要进行调整,以改善音频/视频的同步性
  • audio_samplerate 默认值 48000 要使用的音频采样率.
  • audio_codec 默认值 aac 音频编解码器
  • audio_bitrate 默认值 128000 音频编解码器的比特率

CircularOutput

CircularOutput出类来自于FileOutput, 并增加了用几秒钟前的视频帧开始录制的能力. 这是理想的运动检测和安全应用程序. 循环输出构造函数接受以下可选参数

  • file 默认值 None 用于构造文件输出的字符串(表示文件名)或类似文件的对象, 这是将写入循环缓冲区输出的地方. 值None表示, 当创建循环缓冲区时, 它将在循环缓冲区中积累帧,但不会将它们写入到任何地方
  • buffersize 默认值 150 此设置将设置希望在当前时间之前访问的视频秒数, 乘以帧率. 所以150个缓冲区足以播放5秒的视频, 在30fps的情况下.

要使循环输出开始将帧写入文件应用程序应该:

  • 设置CircularOutput对象的fileoutput属性.
  • 调用 start()
from picamera2.encoders import H264Encoder
from picamera2.outputs import CircularOutput
from picamera2 import Picamera2

picam2 = Picamera2()
picam2.configure(picam2.create_video_configuration())

encoder = H264Encoder()
output = CircularOutput()

picam2.start_recording(encoder, output)

# 现在该开始录制输出,包括之前的5秒钟
output.fileoutput = "file.h264"
output.start()
# 然后可以停止记录
output.stop()

视频记录高级API

正如我们在静态捕获中看到的, 视频录制也有一个方便的API, 对于那些希望较少了解使其工作的编码器和输出对象的人.我们有start_and_record_video函数,它将文件名作为必需的参数和更多的可选参数:

  • output 必须 要记录的文件名. 或者一个输出对象. 当提供以.mp4结尾的字符串时, 将创建一个ffmpegOut输出而不是FileOutput, 一个有效的mp4文件将被创建.
  • encoder 默认值None 指定要使用的编码器. 如果为指定, 则会自主选择. 如果文件名后缀是mjpgmjpeg则使用MJPEG 否则使用H.264
  • config 默认值None 如果不是None, 相机配置则被使用. 如果设置了None, 相机配置也未配置, 则将根据视频配置来进行配置(Picamera2.video_configuration)
  • quality 默认Quality.MEDIUM 要生成的视频质量, 除非在编码器对象中被覆盖
  • show_preview 默认值 False 是否要显示一个预览窗口.如果更改了此值,除非事先调用stop_preview,否则将不起作用
  • duration 默认值 0 记录持续时间. 在停止录音之前,该功能将阻塞很长时间. 当该值为零时,函数立即返回,应用程序将不得不调用stop_recording
  • audio 默认值 False 是否要录制一个音频流. 这只适用于录制到一个MP4文件时, 以及当一个麦克风被附加作为默认的脉冲音频输入时的情况.
from picamera2 import Picmera2
picam2 = Picamera2()
picam2.start_and_record_video("test.mp4", duration=5)

高级主题

显示覆盖

所有的Picamera2预览窗口都支持覆盖层. 这是一个带有alpha通道的位图, 可以叠加在实时相机图像上. alpha通道允许覆盖图像逐像素显示为不透明,部分透明或完全透明.

添加覆盖层需要使用Picamera2.add_overlay函数. 他只有一个参数, 就是一个三维的numpy数组. 前两个维度是高度,然后是宽度, 最后一个维度的值应该是4, 因为所有像素都有R,G,B和A(alpha)值.

注意点:

  • 覆盖层的宽度和高度不必与所显示的相机图像相匹配, 因为覆盖层的大小将被调整, 以精确地适合在相机图像上.
  • 只有在相机配置后才能调用set_overlay,因为此时Picamera2才知道显示的相机图像会有多大.
  • 覆盖总是由set_overlay调用复制,因此应用程序在之后覆盖覆盖是安全的.
  • 叠加被设计为在相机图像上提供简单的效果或GUI元素. 它们不是为复杂的或快速移动的动画而设计的
  • 覆盖层将忽略在创建预览时指定的任何显示变换.
from picamera2 import Picamera2
import numpy as np

picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration())
picam2.start(show_preview=True)

overlay = np.zeros((300, 400, 4), dtype=np.uint8)
overlay[:150, 200:] = (255, 0, 0, 64)  # 浅红
overlay[150:, :200] = (0, 255, 0, 64)  # 浅绿
overlay[150:, 200:] = (0, 0, 255, 64)  # 浅蓝
picam2.set_overlay(overlay)

请记住, 如果用OpenCV加载一个RGBA映像, 您需要使用IMREAD_UNCHANGED标志

overlay = cv2.imread("overlay.png", cv2.IMREAD_UNCHANGED)

事件循环

使用事件循环回调

在关于预览窗口的讨论中,我们看到预览窗口如何提供事件循环, 以请求的形式将拉取的图像缓冲到libcamera中. 并在请求完成后再次接收回这些内容. 即使没有实际的预览窗口, 我们也会启动零预览窗口来提供这个事件循环.

有时能在事件循环中做一些处理很有用, 因为可以无条件用到所有帧中. 例如, 应用程序可能希望监控图像元数据, 或者注释图像, 这些功能无需在应用代码中编写显示循环来做.

尽量不要在处理上花费太多时间, 有可能会导致丢帧, 也不要使用异步请求, 肯定会导致死锁.

在有两个地方,可以将用户处理插入到事件循环中:

  • pre_callback中,处理是在图像被提供给应用程序之前, 在它们被传递到任何视频编码器之前, 以及在它们被传递到任何预览窗口之前进行的.
  • post_callback, 即处理发生在图像被传递到任何视频编码器之前,在它们被传递到任何预览窗口之前,但在图像被提供给应用程序之后.

不可能对将被记录为视频的帧进行处理, 但要避免在显示帧时进行相同的处理,反之亦然,因为这两个进程是并行运行的. 尽管我们注意到应用程序可以显示与它编码的流不同的流(它可能显示“主”流并编码“lores”版本), 并且只将处理应用于其中一个可以模拟这种效果的流.

下面的示例使用OpenCV对每幅图像应用日期和时间戳.

from picamera2 import Picamera2, MappedArray
import cv2

picam2 = Picamera2()

colour = (0, 255, 0)
origin = (0, 30)
font = cv2.FONT_HERSHEY_SIMPLEX
scale = 1
thickness = 2

def apply_timestamp(request):
    timestamp = time.strftime("%Y-%m-%d %X")
    with MappedArray(request, "main") as m
    	cv2.putText(m.array, timestamp, origin, font, scale, colour, thickness)
        
picam2.pre_callback = apply_timestamp
picam2.start(show_preview=True)

因为我们使用的是pre_callback,这意味着所有图像都将有时间戳, 无论使用的是何种capture方法, 以及是否被编码和记录为视频, 还是被显示出来.

但是如果使用post_callback代替, 通过capture方法获得的图像将不会有时间戳.

需要注意的是MappedArray 类, 这个类提供一个便利的方法就地访问相机的缓存. 而capture方法通常返回的是拷贝的副本.

MappedArray需要一个请求和我们想要其访问图像缓冲区的流的名称. 然后它将内存映射到用户空间, 并将其作为常规的numpy数组呈现给我们, 就像我们通过capture_array获得它一样. 一旦离开with语句块, 内存映射将取消, 所有一切将被清理.

将任务分配到事件循环中

除了使用pre/post-callbacks之外, 使用事件循环的另一种方法是分派函数调用, 当相机图像到达时,它将完成这些调用.事实上,所有的capture类型的函数都是以这种方式在内部实现的.

其想法是, 可以将一个函数列表提交给事件循环, 其行为如下:

  1. 每次从相机接收到一个已完成的请求时, 它就会调用列表中的第一个函数.
  2. 这些函数必须始终返回一个由两个值组成的元组. 第一个值应该是一个布尔值, 表示该函数是否已完成. 在这种情况下,它将从列表中弹出, 否则它将保持在列表的前面, 下次将再次调用(需要注意, 我们将不会再前进, 并且不会再以相同的请求调用下一个函数).
  3. 如果该列表现在为空,则该任务列表已完成,并将向调用者发出信号. 元组中的第二个值是作为操作的结果(通常通过wait方法)传递回调用者.

举个例子

通常,当我们调用Picamera2.switch_mode_and_capture_file()时,相机系统从预览模式切换到捕捉模式,捕捉图像,然后切换回预览模式, 重新开始相机运行. 如果我们想在拍摄结束后尽快停止照相机怎么办? 在这种情况下,需要花费一些时间在预览模式下重新启动相机, 然后才能从应用程序中调用Picamera2.stop()(并再次等待这种情况发生).

from picamera2 import Picamera2

picam2 = Picamera2()
capture_config = picam2.create_still_configuration()
picam2.start()

def switch_mode_capture_file_and_stop(camera_config, file_output, name="main"):
    def capture_and_stop_(file_output):
        picam2.capture_file_(file_output, name)
        picam2.stop_()
        return (True, None)
    
    functions = [(lambda: picam2.switch_mode_(camera_config)), (lambda: capture_and_stop_(file_output))]
    return picam2.dispatch_functions(functions, wait=True)
switch_mode_capture_file_and_stop(capture_config, "test.jpg")

重点:

  • 创建了两个函数的列表, 并将其分派给事件循环.
  • 其中的第一个函数(picam2.switch_mode_)将把相机切换到捕捉模式, 然后返回True作为其第一个值, 并将其从列表中删除.
  • 当捕获模式下的第一帧到达时, 本地捕获和停止功能将运行, 捕获文件并停止相机.
  • 这个函数也返回True,因此它将从列表中弹出.该列表现在为空,因此事件循环将表示它已完成.

在这里, 应用程序也必须注意它在事件循环中调用的什么函数. 例如,大多数常见的Picamera2功能都很可能会导致死锁. 按照约定,明确安全的函数应以_(下划线)结束.

像素格式和内存注意事项

通常比较难判断哪个图像可以工作的更好. 其中有一些比另一些会占用更多内存, 而有一些会在第三方库中获得更好的支持.通常,这些约束会相互对抗-最广泛使用的格式是最需要内存的.

下表列出了我们建议用户选择的图像格式, 即单个全分辨率的大小12MP的映像, 以及它们是否适用于某些其他模块.

XRGB8888/XBGR8888 RGB888/BGR888 YUV420/YVU420
12MP size 48MB 36MB 18MB
Qt GL preview YES NO YES
Qt preview YES,慢 YES,慢 需要OpenCV,很慢
DRM preview YES YES YES
Null preview YES YES YES
JPEG encode YES YES NO
Video encode YES YES YES
OpenCV 通常 YES 仅转换为RGB

CMA代表连续内存分配器, 在树莓派上, 它提供的内存可以直接用于摄像头系统及其所有硬件设备. main、lores和raw流的所有内存缓冲区都被分配在这里,并且与Linux操作系统的其他部分共享, 它可能会面临压力并受到影响而碎片化. 当CMA区域空间不足时,这可以通过Picamera2错误来识别,该错误称V4L2(Linux视频内核子系统)无法分配缓冲区. 缓解措施可能包括分配更少的缓冲区(相机配置中的buffer_count参数), 选择使用较少内存或使用较低内存的图像格式, 也可以尝试以下解决方法:

增加CMA区域的大小

CMA默认的大小是: 如果整个系统的内存小于或等于1GB, 则CMA的大小是256MB. 否则是320MB. 当"常规"内存开始不足时, 统仍然可以使用CMA内存,因此增加其大小通常不会使操作系统的其余部分挨饿.根据经验,如果遇到问题, 所有系统通常都应该能够将大小增加到320MB. 1GB系统可能达到384MB, 2GB或更大的系统可能达到512MB.

要更改CMA区域的大小, 需要编辑/boot/config.txt文件.找到dtoverlay=vc4-kmsv3d这一行,并将其替换为:

  • dtoverlay=vc4-kms-v3d,cma-320 对应 320MB
  • dtoverlay=vc4-kms-v3d,cma-384 对应 384MB
  • dtoverlay=vc4-kms-v3d,cma-512 对应 512MB

不要在上面提供的内容中添加任何空格或更改任何格式

使用YUV420图像

减少图像的内存使用,可以使用YUV420格式. 第三方模块通常不太支持这种格式, 所以使用某种软件将其转换为更熟悉的RGB格式可能是必要的. 在应用程序中进行这种转换的好处是, RGB占用的大量缓冲区是虚拟内存中的用户空间, 而CMA区域只分配较小的YUV420版本需要的空间.

幸运的是,OpenCV提供了从YUV420RGB的转换功能. 执行所需时间大大减少,例如, 与JPEG编码器相比, 因此对于某些应用程序来说,这可能是一个很好的折衷方案.以下示例显示如何将YUV420图像转换为RGB.

from picamera2 import Picamera2
import cv2

picam2 = Picamera2()
picam2.create_preview_configuration({"format": "YUV420"})
picam2.start()

yuv420 = picam2.capture_array()
rgb = cv2.cvtColor(yuv420, cv2.COLOR_YUV420p2RGB)

然而,此函数似乎不允许应用程序选择YUV/RGB转换矩阵.

缓冲区分配和队列

内存使用的通性问题是, 我们怎么知道分配多少缓冲区给相机系统(buffer_count参数). 以下是Picamera2的默认配置所使用的启发式方法.

  • 默认情况下,预览配置有四个缓冲区. 这通常足以保持相机系统即使在进行适度的附加处理时也能平稳运行.
  • 默认情况下, 静态捕获配置只提供一个缓冲区. 这是因为它们可能非常大, 这对于512MB平台来说是特别的问题. 因为从图像传感器是流水线式, 我们肯定至少每隔一帧就会掉一帧的. 如果有可用内存, 并且想要快速,全分辨率的连拍, 可能需要增加这个数字.
  • 视频配置分配六个缓冲区. 录制视频时, 系统可能会更忙, 所以额外的缓冲区减少了丢帧的机会.

坚持请求

我们已经看到了应用程序如何保留请求以供自己使用, 在此期间, 相机系统不可用. 如果这导致不可接受的帧下降, 甚至使相机系统完全停滞, 那么答案是在配置时为相机分配更多的缓冲区. 一般来说, 如果您的应用程序只保留一个请求,那么只需要分配一个额外的缓冲区就可以恢复原状.

Picamera2内的缓冲队列

通常, Picamera2总是试图保持最新到达的相机帧. 例如,如果应用程序执行一些通常适合一个帧周期内的处理,但偶尔需要更长的时间(通常是多任务操作系统上的特定风险), 那么就不太可能丢帧. Picamera2中该队列的长度仅为一帧. 这确实意味着, 当你要求拍摄一帧或元数据, 函数可能会立即返回, 除非您之前已经发出了类似的请求.

例外情况是, 当相机配置了单缓冲区, 相机无法保存上一帧图像, 因为没有备用的缓冲区可以使用. Picamera2 不保存前一帧的数据, 并且捕获帧的请求, 总是等待相机发出的下一帧. 这也就意味着预览窗口存在撕裂图像的风险(当新图像取代旧图像时).

Picamera2保存最后一个帧, 可通过queue参数在相机配置中设置. 如果你想保证每一个发出的捕获请求都是在相机系统收到请求后返回的, 这个参数应该设置为False.

在Qt应用中使用相机

创建在GUI中嵌入相机窗口的推荐方式是是使用Qt. 事实上, camera2的预览窗口就是使用Qt实现的, 但是我们不推荐复制这些代码. 下面将展示更加标准的Qt应用.

Qt 小部件

Picamera2提供两个Qt小部件:

  • QGlPicamera2 通过pi的GPU硬件加速渲染相机图像.

  • QPicamera2 一个软件渲染, 但是在其他地方是等效的. 这个部件非常慢, 所以PGlPicamera2应该是首选选项, 除非它不可用(例如通过远程调用).

两个部件都有一个add_overlay函数, 他们实现了Picamera2预览窗口的覆盖方法. 这个函数接受一个三维numpy数组, 作为一个RGBA图像. 使得此功能也能适用于Qt应用.

创建部件, 还有一个可选的参数keep_ar, 默认值是True. 这允许应用选择是否根据字母框或者柱形框来调整部件的大小(keep_ar=True), 或者拉伸完全填充它, 可能会扭曲图像的相对高度和宽度(keep_ar=False).

两个组件都支持transform参数, 以支持图像在绘制到屏幕时完成水平或者垂直的翻转.

事件循环

当写一个在python解释器主线程中运行的Picamera2脚本时, 驱动相机系统的事件循环由预览窗口提供(即使是Null窗口这种什么都不展示的). 在这种情况下, Qt事件循环成为主线程, 并驱动相机应用.

from PyQt5.QtWidgets import QApplication
from picamera2.previews.qt import QGlPicamera2
from picamera2 import Picamera2

picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration())

app = QApplication([])
qpicamera2 = QGlPicamera2(picam2, width=800, height600, keep_ar=False)
qpicamera2.setWindowTitle("Qt Picamera2 App")

picam2.start()
qpicamera2.show()
app.exec()

在真实的应用中, qpicamera2 部件将被嵌入到更复杂的父部件或窗口的布局中.

调用相机函数

相机函数, 可以分为三类:

  1. 立即返回, 并且可以安全调用
  2. 在函数操作完成之前, 必须等待相机事件循环完成一些事情, 所以必须以非阻塞的方式调用它.
  3. 不需要调用的函数.

下表展示了API函数, 并指明是什么种类.

函数名 状态
create_preview_configuration 安全可直接调用
create_still_configuration 安全可直接调用
create_video_configuration 安全可直接调用
configure 安全可直接调用
start 安全可直接调用
stop 安全可直接调用
start_encoder 安全可直接调用
start_recording 安全可直接调用
switch_mode 以非阻塞方式调用
capture_file 以非阻塞方式调用
capture_buffer 以非阻塞方式调用
capture_array 以非阻塞方式调用
capture_request 以非阻塞方式调用
capture_request_and_stop 以非阻塞方式调用
capture_image 以非阻塞方式调用
capture_metadata 以非阻塞方式调用
switch_mode_and_capture_file 以非阻塞方式调用
switch_mode_and_capture_buffer 以非阻塞方式调用
switch_mode_and_capture_array 以非阻塞方式调用
switch_mode_and_capture_image 以非阻塞方式调用
switch_mode_capture_request_and_stop 以非阻塞方式调用
start_and_capture_file 不需要调用
start_and_capture_files 不需要调用
start_and_record_video 不需要调用

以非阻塞的方式调用阻塞相机函数

当我们在python解释器线程中运行脚本, 相机的事件循环异步运行, 这意味着我们能够调用它做一些事情并等待他们完成.

我们能完成这些, 是因为这些函数有waitsignal_function参数. 默认值将导致函数阻塞, 直到他们完成. 但是如果传递signal_function则会立即返回, 并且依赖signal_function将控制权返回给我们.

在Qt中, 相机线程和Qt线程, 所以不能阻塞相机的完成, 因为将会死锁. 相反的, 我们需要告诉这些函数, 他们可能不会阻塞, 并为他们提供一种Qt友好的方式来通知我们他们已经完成. 因此这些部件提供:

  • done_signal 参数 这是一个Qt信号, 应用程序可以将自己的回调函数链接到该信号.

  • signal_done 函数, 发出done_signal

所以应用的步骤是:

  • 链接回调函数到done_signal .

  • 必须为Picamera2.switch_mode_and_capture_file类似的函数提供signal_done 在函数完成时调用.

  • 由非阻塞调用启动的作业, 将被传递给链接到done_signal 的函数. 应用程序可以通过此作业调用Picamera2.wait 来获取原始调用的返回值.

举个例子

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QApplication, QWidget

from picamera2.preview.qt import QGlPicamera2
from picamera2 import Picamera2

picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration())

def on_button_clicked():
    button.setEnabled(False)
    cfg = picam2.create_still_configuration()
    picam2.switch_mode_and_capture_filet(cfg, "test.jpg", signal_function=qpicamera2.signal_done)

def capture_done(job):
    result = picam2.wait(job)
    button.setEnabled(True)
    
app = QApplication([])
qpicamera2 = QGlPicamera2(picam2, width=800, height=600, keep_ar=False)
button = QPushButton("Click to capture JPEG")
window = QWidget()
qpicamera2.done_signal.connect(capture_done)
button.clicked.connect(on_button_clicked)

layout_v = QVBoxLayout()
layout_v.addWidget(qpicamera2)
layout_v.addWidget(button)
window.setWindowTitle("Qt Picamera2 App")
window.resize(640, 480)
window.setLayout(layout_v)

picam2.start()
window.show()
app.exec()

调试日志

Picamera2 调试

Picamera2 使用的是python的日志模块. 他使用picame2的名称创建一个looger, 但是没有配置日志级别和或者handler, 这通常是由应用程序决定.

熟悉logging模块的可以按照通常的习惯设置日志记录和分配handler. 不熟悉logging模块, 只想简单看看一些日志打印的可以使用:

from picamera2 import Picamera2
Picamera2.set_ligging(Picamera2.DEBUG)

除了日志登记, set_loggin函数还允许指定:

  • output 消息的目的地, 默认设置 sys.stderr

  • msg 日志的格式化格式, 默认格式是 %(name)s %(levelname)s: %(message)s

libcamera 调试

libcamera是picamera2的基础C++库, 他有自己的日志系统. 这里给出一点简短的说明.

libcamera的日志系统 由两个主要的环境变量控制:

  • LIBCAMERA_LOG_FILE 日志文件的输出路径. 如果未指定, 则默认输出stderr

  • LIBCAMERA_LOG_LEVELS 日志输出级别

日志级别设置

  • DEBUG 或者 0
  • INFO 或者 1
  • WARN 或者 2
  • ERROR 或者 3
  • FATAL 或者 4