libcamera 简介

libcamera 是一个开源的多媒体库,用于在 Linux 操作系统上支持多种摄像头硬件的访问,libcamera 是一个用户空间库,可用于开发基于摄像头的应用程序,如相机应用程序或视频通信应用程序。

基础概念

CameraManager

CameraManager 负责列举系统中的所有摄像头

  • 调用 CameraManager::cameras() 返回一组应用程序可用的摄像头
  • 调用 CameraManager::get() 通过摄像头 ID 来获取对应的摄像头
std::unique_ptr<CameraManager> cm = std::make_unique<CameraManager>();

cm->start();

for (auto const &camera : cm->cameras())
std::cout << " - " << camera.id() << std::endl;

进程空间中只能存在单一的 CameraManager

Camera

Camera 代表系统中可用的一个图像源,比如图像传感器

系统中存在多个摄像头且可以彼此独立操作的时候,libcamera 将它们暴露为多个 Camera 设备,硬件限制(比如内存带宽、CPU 占用率等)允许的情况下,支持多个摄像头的并行操作

Camera 设备在某个时刻只能被单一进程所使用

  • 应用程序使用 Camera 时要先调用 Camera::acquire() 对它上锁
  • 使用结束后要调用 Camera::release() 释放它

调用 Camera::id() 获得摄像头的 ID,它是字符串类型,保证唯一且稳定

  • 只要同一个摄像头以同样的方式(比如插到同一个 USB 接口上)连接到系统上,不管是插拔还是系统重启,ID 都是不变的
std::cout << camera->id() << std::endl;

这里我们打印摄像头的 ID,得到 /base/soc/i2c0mux/i2c@1/imx219@10

调用 Camera::properties() 获得相机的属性列表

  • properties 是描述相机功能的静态信息,在 Camera 对象的整个生命周期内保持不变
  • 比如摄像头的 Location(前置摄像头、后置摄像头或可自由移动的摄像头),Model(传感器的名字)
const ControlList &props = camera->properties();
const auto &model = props.get(properties::Model);
if (model)
name = " '" + *model + "'";

这里我们打印传感器的 Model,输出的结果是 'imx219'

Stream

libcamera 支持并发多路视频流

  • 例如,使用中等分辨率的视频流来做预览,同时使用最大分辨率的视频流来拍照
 +-------------------------------------------------------+
| Camera |
| +-----------+ |
| +--------+ | |------> [ Main output ] |
| | Image | | | |
| | |---->| ISP |------> [ Viewfinder ] |
| | Source | | | |
| +--------+ | |------> [ Still Capture ] |
| +-----------+ |
+-------------------------------------------------------+

摄像头流的数量和能力是平台相关的属性,pipeline handler 的实现有责任正确的汇报它

CameraConfiguration

Camera 会根据应用场景来创建一组默认配置模板,然后应用程序可以修改默认参数,并验证确保摄像头能支持新配的参数(某些参数组合可能是系统不支持的)

  • Camera::generateConfiguration() 根据 StreamRole 产生摄像头默认配置

    • StreamRole 就是这个流是被拿来干嘛的,包括

      • Raw

        • 从传感器获得原始帧(raw frames)
      • StillCapture
        • 高分辨率、高质量、低帧率的静态图像
        • 可以用闪光灯曝光
      • VideoRecording
        • 用来录像(recording)或在线播放(streaming)
        • 可能是高帧率,可以做视频稳定(video stabilization)
      • Viewfinder
        • 用于捕获视频,以便在本地屏幕上显示
        • 质量和系统资源使用之间的权衡是可以接受的
    • 每个 StreamRole 都对应一个默认的 StreamConfiguration
  • 应用程序可以修改配置参数
  • CameraConfiguration::validate() 验证并调整配置到最接近的合法配置
  • Camera::configure() 用来修改 Camera 配置,只接受完全有效的配置,对于非法的配置会报错
  • CameraConfiguration 是实现了迭代器,可以用它来遍历所有的 StreamConfiguration,也可以通过用下标调用 CameraConfiguration::at() 获取某个 StreamConfiguration
std::unique_ptr<CameraConfiguration> config =
camera->generateConfiguration( { StreamRole::Viewfinder } ); StreamConfiguration &viewFinderStreamConfig = config->at(0); std::cout << "Default viewfinder configuration is: "
<< viewFinderStreamConfig.toString() << std::endl;

这里我们打印 Viewfinder、StillCapture、Raw、VideoRecording 的默认配置

Default viewfinder configuration is: 800x600-NV12
Default stillcapture configuration is: 3280x2464-NV12
Default raw configuration is: 3280x2464-SBGGR10_CSI2P
Default videorecording configuration is: 1920x1080-YUV420

Buffer 分配

捕获到的图像要存储到 framebuffer 中

  • framebuffer 可以是应用程序提供给库

    • 应用程序可以把 buffer 分配到任何地方,比如可以直接 display driver 分配的内存中,就会被 display driver 用来渲染
    • 通过构建 FrameBuffer 实例
  • 也可以是 Camera 内部分配并暴露给应用程序
    • 使用 FrameBufferAllocator 实例来分配 buffer
    • 根据 Camera 配置来决定 buffer 的大小和类型
FrameBufferAllocator *allocator = new FrameBufferAllocator(camera);

for (StreamConfiguration &cfg : *config) {
int ret = allocator->allocate(cfg.stream());
if (ret < 0) {
std::cerr << "Can't allocate buffers" << std::endl;
return EXIT_FAILURE;
} auto &buffers = allocator->buffers(cfg.stream());
size_t allocated = buffers.size();
std::cout << "Allocated " << allocated << " buffers for stream" << std::endl; for (auto &buffer: buffers) {
for (auto &plane : buffer->planes()) {
std::cout << "~ " << plane.length << std::endl;
}
std::cout << std::endl;
}
}

这里我们通过 FrameBufferAllocator 分配了缓冲区,采用的像素格式是 NV12,所以缓冲区有两个 planes

Allocated 4 buffers for stream
~ 480000
~ 240000 ~ 480000
~ 240000 ~ 480000
~ 240000 ~ 480000
~ 240000

捕获帧

libcamera 的视频捕获基于 Request 的概念

  • 每一帧的请求必须在摄像头中排队等待处理
  • 请求至少要和一个 Stream 关联起来
  • Buffer 要被添加到请求中
    • 请求完成后,图像数据会被写到 Buffer 里,等待应用程序消费
    • 可以给每个流都配置一个 Buffer,这样就可以同时处理 viewfinder 和 still image,或通过获取原始图像和 ISP 处理后的图像
  • 请求会有一系列 Controls,它们是可调节的参数,可以以帧为单位做调整
  • 请求完成后,可以查看元数据以确定应用于图像的捕获参数
std::unique_ptr<Request> request = camera->createRequest();
if (!request)
{
std::cerr << "Can't create request" << std::endl;
return EXIT_FAILURE;
} int ret = request->addBuffer(stream, buffer.get());
if (ret < 0)
{
std::cerr << "Can't set buffer for request"
<< std::endl;
return EXIT_FAILURE;
} ControlList &controls = request->controls();
controls.set(controls::Brightness, 0.5);

Signal&Slots

libcamera 采用了类似于 QT 的信号槽机制

  • Signals 代表类实例产生的事件,Slots 是关联到某个 Signal 上的回调函数
  • Camera 暴露出的信号
    • requestCompleted

      • 在摄像头中排队的请求完成了
    • bufferCompleted
      • 请求中的某个 Buffer 完成了
    • disconnected
      • 设备从系统上断开了
  • 应用程序必须在摄像头开始运行前,将回调函数注册到这些信号上,才能处理对应的事件
camera->requestCompleted.connect(requestComplete);

开始捕获

启动摄像头,并让请求在其中排队,摄像头管线深度被填满后,其他请求必须排队等待,直到摄像头开始交付帧

  • 对于被交付的帧,在 requestCompleted 上绑定的 Slot 会被调用
camera->start();
camera->queueRequest(request.get());

消费帧

我们可以在 Slot 中消费 Buffer 中的数据和请求的元数据

元数据

请求完成后,元数据列表中包含已完成请求的各种属性,包括传感器捕获的时间戳、传感器增益(gain)和曝光值(exposure values),或来自 IPA 的属性,如 3A 算法的状态

const ControlList &requestMetadata = request->metadata();
for (const auto &ctrl : requestMetadata) {
const ControlId *id = controls::controls.at(ctrl.first);
const ControlValue &value = ctrl.second; std::cout << "\t" << id->name() << " = " << value.toString()
<< std::endl;
}

这里我们打印所有的元数据

Request completed: Request(35:C:0/1:0)
ExposureTime = 66653
AnalogueGain = 8.000000
ColourCorrectionMatrix = [ 1.684340, -0.523863, -0.160477, -0.489361, 1.884680, -0.395319, -0.084126, -0.957826, 2.041952 ]
FrameDuration = 66729
Lux = 4.425883
AeLocked = true
ColourGains = [ 1.286008, 1.913542 ]
DigitalGain = 1.000183
ColourTemperature = 3446
SensorBlackLevels = [ 4096, 4096, 4096, 4096 ]
ScalerCrop = (0, 2)/3280x2460
SensorTimestamp = 1463504108407000

Buffer 数据

FrameBuffer::metadata() 可以获得帧的动态元数据,比如帧的状态、序号、时间戳

const Request::BufferMap &buffers = request->buffers();
for (auto bufferPair : buffers) {
// (Unused) Stream *stream = bufferPair.first;
FrameBuffer *buffer = bufferPair.second;
const FrameMetadata &metadata = buffer->metadata(); /* Print some information about the buffer which has completed. */
std::cout << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence
<< " timestamp: " << metadata.timestamp
<< " bytesused: ";
}

FrameBuffer::Plane 是存储帧单一平面的内存区域

  • FrameBuffer 有一个或多个 Plane,取决于它的像素格式

    • planar 像素格式用多个内存区域来存储帧的不同颜色分量
    • 相对应的是 packed 像素格式,所有分量交织存储,只需要一个内存区域即可
  • 用 dmabuf 文件描述符、偏移量和长度来描述这样一块内存区域
    • 多个 plane 可以用同一个 dmabuf fd 来引用,这时候就需要通过偏移量和长度来区分它们
  • 应用程序应该调用 mmap() 来映射 plane 内存以访问它的内容
size_t buffer_size = 0;
for (unsigned i = 0; i < buffer->planes().size(); i++)
{
const FrameBuffer::Plane &plane = buffer->planes()[i];
buffer_size += plane.length;
if (i == buffer->planes().size() - 1 || plane.fd.get() != buffer->planes()[i + 1].fd.get())
{
void *memory = mmap(NULL, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, plane.fd.get(), 0);
buffer_size = 0;
}
}

复用 Request

将 Buffer 设置为可复用,然后重新入队

request->reuse(Request::ReuseBuffers);
camera->queueRequest(request);

释放资源

关掉摄像头、释放资源并停止 CameraManager

camera->stop();
allocator->free(stream);
delete allocator;
camera->release();
camera.reset();
cm->stop();

参考资料

libcamera 简介的更多相关文章

  1. ASP.NET Core 1.1 简介

    ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...

  2. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  3. Cassandra简介

    在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了一种非常流行的图形数据库Neo4J的使用方法.而在本文中,我们将对另外一种类型的NoSQL数据库——Cassandra进行简单地介 ...

  4. REST简介

    一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...

  5. Microservice架构模式简介

    在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...

  6. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

  7. HTTPS简介

    一.简单总结 1.HTTPS概念总结 HTTPS 就是对HTTP进行了TLS或SSL加密. 应用层的HTTP协议通过传输层的TCP协议来传输,HTTPS 在 HTTP和 TCP中间加了一层TLS/SS ...

  8. 【Machine Learning】机器学习及其基础概念简介

    机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...

  9. Cesium简介以及离线部署运行

    Cesium简介 cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎,一款开源3DGIS的js库.cesium支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区 ...

  10. 1.Hibernate简介

    1.框架简介: 定义:基于java语言开发的一套ORM框架: 优点:a.方便开发;           b.大大减少代码量;           c.性能稍高(不能与数据库高手相比,较一般数据库使用者 ...

随机推荐

  1. 计算机保研,maybe this is all you need(普通双非学子上岸浙大工程师数据科学项目)

    写在前面 9.28接收了拟录取通知,也终究是尘埃落定了,我人生的又一个阶段也终于结束.面对最终录取结果,或多或少会有所遗憾,但也还是基本达到了预期的目标了. 作为在今年严峻的保研形势下幸存的我,一直想 ...

  2. NVIDIA Isaac Gym安装与使用

    NVIDIA做的Isaac Gym,个人理解就是一个类似于openai的Gym,不过把环境的模拟这个部分扔到了GPU上进行,这样可以提升RL训练的速度. 官网:https://developer.nv ...

  3. C语言两个升序递增链表逆序合并为一个降序递减链表,并去除重复元素

    #include"stdafx.h" #include<stdlib.h> #define LEN sizeof(struct student) struct stud ...

  4. Visual Studio(VS)修改C语言scanf等报错

    1.在程序最前面加:#define_CRT_SECURE_NO_DEPRECATE 2.按照vs规定进行修改,例如把scanf改为scanf_s: 3.在"项目" -> &q ...

  5. Go | 基本数据类型的相互转换

    基本数据类型的相互转换 Go在不同类型的变量之间赋值时需要显示转换,不能自动转换 基本语法 表达式 T(v): 将值v转换成类型T T就是数据类型: int32, int64, float32... ...

  6. fastjson反序列化漏洞历史CVE学习整理

    fastjson 1.2.24反序列化漏洞复现 先写一个正常的使用 fastjson的web服务 我们使用 springboot创建 主要是pom.xml 里面要添加fastjson fastjson ...

  7. java反序列化_link_six

    cc_link_six 0x01前言 经过cc链一的学习,然后jdk的版本一更新那两条链子就不能用了,然后这种反序列化的话就很不不止依赖于cc包的引入还有jdk版本,于是就出现了cc_link_six ...

  8. C#与Halcon联合编程之用PictureBox控件替代HWindowControl控件

    在使用HALCON和C#联合编程,有时候要使用halcon的HWindowControl控件,但是我发现,HWindowControl的图片显示控件,不能使用GDI+绘制ROI,不知道为什么,反正我测 ...

  9. .net随笔——Web开发config替换到正式config appSettings

    前言(废话) 查了一些资料,总体来说呢,就是坑,而且顺带吐槽下百度,一个内容被copy那么多遍还排在最前面.同一个内容我点了那么多次,淦. 正题: 实现目的:开发的时候使用system.debug.c ...

  10. win7使用onedrive右键托盘图标中文不显示问题

    前言 win7 用的 onedrive不能在微软官网下载,用不了,所以需要下载 win7可以使用的版本. onedrive_for_win7.exe 解决问题 重启电脑解决 其他 我看贴吧说是文本放大 ...