Libdc1394 库对 IEEE 1394 相机的支持 HOWTO

Rohit Agarwal



            

Vikram B



            

2006-01-23

修订历史
修订版 1.22007-04-14修订者:RA
由 Ian Peikon 贡献的 DMA 相关章节的添加和 Tim Hanson
修订版 1.02006-01-23修订者:TMM
初始版本,由 TLDP 审核
修订版 0.42005-12-18修订者:TMM
标记修正
修订版 0.32005-11-27修订者:TMM
转换为 DocBook XML
修订版 0.22005-10-15修订者:VB
小修复。
修订版 0.12005-10-10修订者:RA & VB
初始草案。

本文档讨论如何在 Linux 上设置 libdc1394 库以支持 IEEE 1394 (FireWire) 相机。


目录
1. 引言
2. 要求
3. 安装
4. 概念和基本程序
4.1. 重要的数据结构
4.2. 函数类型
4.3. Coriander:libdc1394 库的 GUI
4.4. 示例:如何从 IEEE1394 相机获取图像
4.5. 示例:如何使用 DMA 从 IEEE1394 相机获取图像
4.6. 如何获取彩色图像:Bayer 模式概念
4.7. 使用 IEEE1394 相机时遇到的常见问题
5. 参考文献
A. 附录 A
A.1. 许可证
A.2. 免责声明
A.3. 关于作者
A.4. 致谢

1. 引言

我们认为有必要编写关于此主题的 HOWTO,因为关于 libdc1394 库的可用文档很少。 此外,IEEE1394 相机越来越受欢迎,这将导致许多人使用此库来开发 Linux 上相机的应用程序。 本 HOWTO 是我们使用 Linux 上的 Point Grey Dragonfly IEEE1394 相机的工作经验的成果。

我们以通俗易懂的语言给出了该库的概述,因此任何具有一定编程经验的人都可以轻松理解内容。


2. 要求

所需的各种库和模块包括


3. 安装

前提条件:确保内核已编译并内置了 IEEE1394 支持。 这至关重要!

您可以通过重新编译内核来添加对 IEEE1394 设备的支持。 不要惊慌! 以下是编译内核的步骤

转到内核源代码所在的目录。 通常在/usr/src。 我们建议您从 www.kernel.org 下载内核源代码 (2.6.10) 的全新副本。

因此,cd /usr/src/kernel-2.6.10 (kernel-2.6.10是源代码目录的名称。 它因系统而异。)

  1. make menuconfig

  2. 将出现菜单。 选择 设备驱动程序

  3. 将出现设备驱动程序菜单。 选择 IEEE1394 (Firewire) 支持

  4. 在显示的菜单中将以下各项标记为 <M>

    1. OHCI-1394 支持

    2. OHCI-1394 视频支持

    3. OHCI-1394 DVI/O 支持

    4. RAW IEEE1394 I/O 支持

  5. ESC 退出菜单

  6. 保存配置

安装 libraw1394-1.2.0

  1. su root

  2. tar -xvzf libraw1394-1.2.0.tar.gz

  3. cd libraw1394-1.2.0

  4. ./configure

  5. make

  6. make install

安装 libdc1394-1.1.0

  1. su root (如果您还没有这样做)

  2. tar -xvzf libdc1394-1.1.0.tar.gz

  3. cd libdc1394-1.1.0

  4. ./configure

  5. cd libdc1394

  6. make

  7. make install

安装模块

  1. modprobe ohci1394

  2. modprobe video1394

  3. modprobe ieee1394

  4. modprobe raw1394

  5. lsmod 以查看所有模块(ieee1394、raw1394、ohci1394 和 video1394)现在都已安装。

Note关于依赖项的说明
 

这些模块有一些依赖项

  • ohci1394 依赖于 video1394

  • ieee1394 依赖于 video1394、ohci1394 和 raw1394

因此,如果您使用 insmod 安装模块,则必须首先安装 video1394 和 raw1394,然后安装 ohci1394 和 ieee1394。

创建正确的 1394 设备

  1. cd /dev

  2. mknod raw1394 c 171 0

  3. chmod 666 /dev/raw1394

  4. mkdir video1394

  5. cd video1394

  6. mknod 0 c 171 16

  7. mknod 1 c 171 17

  8. chmod 666 /dev/video1394/*

Note关于自动启动模块的说明
 

每次系统重新启动时,节点都会被销毁,模块也会自动删除。 因此,我们编写了一个脚本来自动化启动时的安装任务,以消除重新启动期间造成的所有损坏!

设置用户路径

  1. su user

  2. 检查LD_LIBRARY_PATH。 它应该包含/usr/local/lib。 这是运行带有共享文件的应用程序所必需的libdc1394_cotrol.so.13目标文件。

Note关于设置路径的说明
 

您也可以通过添加/usr/local/lib/etc/ld.so.conf.

这样就完成了安装阶段。

为了确保安装正确,请将相机连接到 IEEE1394 卡(假设 IEEE1394 卡已安装在系统上),然后运行 testlibraw

$ testlibraw

这将测试主机卡、相机和其他相关参数是否存在。 以下是 testlibraw 的典型输出

Successfully got handle
current generation number: 17
1 card(s) found
nodes on bus: 2, card name: ohci1394 
using first card found: 2 nodes on bus, local ID is 0, IRM is 1

doing transactions with custom tag handler 
trying to send read request to node 0... completed with value 0x23127bac
trying to send read request to node 1... completed with value 0x60217dac

using standard tag handler and synchronous calls
trying to read from node 0... completed with value 0x04477dac
trying to read from node 1... completed with value 0xd37380ac

testing FCP monitoring on local node
got fcp command from node 0 of 8 bytes: 01 23 45 67 89 ab cd ef
got fcp response from node 0 of 8 bytes: 01 23 45 67 89 ab cd ef

polling for leftover messages

libdc1394-1.1.0库附带了一些示例程序,这些程序对于理解如何编写程序非常有帮助。 tar 文件的examples文件夹中提供了一个 Makefile。 要编译和执行程序

cd libdc1394-1.1.0/examples

make

您可以从 http://www.ptgrey.com/support/kb/data/grabdma.tgz 获取专门为 Point Grey 相机编写的程序。

幸运的是,我们有一个为 IEEE1394 相机开发的开源图形应用程序,称为 Coriander,它为 libdc1394 提供了一个很好的图形用户界面,以帮助用户更有效地使用相机。 稍后我们将讨论 Coriander 如何帮助我们进行调试。 接下来,我们将简要解释如何安装 Coriander

Coriander 的安装

  1. 确保您的 Linux 机器上安装了 libdc1394 和 Gnome 库 (Coriander 使用gnome.h)。 有关要求的更多详细信息,请参阅 Coriander 提供的用户手册:http://damien.douxchamps.net/ieee1394/coriander/manual.php

  2. 从 sourceforge.net 下载以下 tar 文件coriander-1.0.1.tar.gz,然后按如下所示进行操作

    1. su root

    2. tar -xvzf coriander-1.0.1.tar.gz

    3. cd coriander-1.0.1

    4. ./configure

    5. cd src

    6. make

    7. make install

  3. 按如下所示运行 Coriander

    cd coriander-1.0.1/coriander-1.0.1/src
    ./coriander
    

    幸运的是,libdc1394 和相关模块的安装很简单,不会带来任何重大问题。 您可能遇到的一些小问题是由于诸如未以 root 身份安装或未更改新创建的 IEEE1394 设备的权限等错误造成的。


4. 概念和基本程序

目前,可用于理解 libdc1394 的文档很少。 可用的文档包括一个 FAQ,其中仅讨论了相机的总体概念,以及一些人们发布查询的论坛。

我们必须阅读 libdc1394 源代码才能了解如何从相机中抓取帧。 对此主题进行完整解释的迫切需求促使我们编写本 HOWTO。

根据我们通过探索相机源代码所理解的内容,我们正在展示我们对该库的见解。

我们鼓励您在阅读下一节时不断参考以下文件

  1. dc1394_control.h

  2. dc1394_control.c

  3. dc1394_capture.c

  4. dc1394_format7.c

dc1394_control.h文件是最重要的,因为它包含各种函数和数据结构的声明。 应该阅读它以了解 IEEE1394 相机支持的功能以及访问和控制相机各种功能的函数。

dc1394 库与 raw1394 紧密配合,因为所有函数都使用 raw1394 的函数(raw1394_read() 和 raw1394_write())来检索或修改相机的各种参数的值。 这是在安装 libdc1394 之前必须安装 raw1394 的原因之一。

要了解库提供的函数的工作方式,我们需要了解 IEEE1394 相机的一些技术方面

  1. 任何时候都可以将多个相机连接到主机卡,因此需要唯一标识相机节点。

  2. 相机具有一些控制寄存器来设置相机功能。

  3. 根据 IEEE 规范,相机可以以不同的格式抓取图像。 图像格式由两个参数定义:格式和模式。 定义了五种模式

    1. Format_0 包含低分辨率模式,最高可达 640x480

    2. Format_1 包含中分辨率模式:800x600 和 1024x768

    3. Format_2 包含百万像素模式:1280x960 和 1600x1200

    4. Format_7 是可缩放图像格式。 使用此格式,您可以更改图像大小、颜色编码和其他参数

    相机可能不支持某些模式,因此我们在设置时需要小心。 您可以使用设置来设置各种参数,如数据速度、图像模式、帧速率,这些参数是使相机准备好抓取图像所必需的。 我们将很快讨论设置功能。

  4. 为了设置或获取相机参数/功能的值,库函数将修改或读取寄存器值。

  5. 相机可以在有或没有 DMA 的情况下工作。 我们非常感谢 Ian Peikon 和 Tim Hanson 为我们贡献了关于使用 DMA 支持的 IEEE1394 相机的部分

有了上述背景知识,我们开始讨论各种函数、数据结构以及dc1394_control.h文件中提供的其他杂项功能。

我们想提到dc1394_control.h文件的一个重要方面,它列出了可以为数据速度、帧速率、相机模式和图像格式设置的值。 所有值都采用枚举的形式,以帮助您编写可读的代码。 例如,速度枚举表明数据速度只能设置为 100、200、400、800、1600 或 3200。

dc1394_control.h还列出了您的相机支持的功能(亮度、色调、清晰度、饱和度等)。


4.1. 重要的数据结构

该库提供了一些结构,这些结构对于存储相机和图像相关信息很有用。 它们已在dc1394_control.h文件中提供的其他杂项功能。

结构体

用途

dc1394_camerainfo

有助于访问有关相机型号、供应商、ccr_offset(用于访问寄存器)的信息。

dc1394_cameracapture

包含相机设置时要操作的各种参数。 它还声明了一个int类型的缓冲区,该缓冲区将用于存储抓取的帧。

dc1394_misc_info

包含有关 ISO 通道 ID、ISO 通道速度、内存通道号的信息。

dc1394_feature_info

包含关于特定功能(亮度、色调、伽马、白平衡等)的信息,例如其可用性和值。

我们主要关注 dc1394_cameracapture 结构。


4.2. 函数类型

我们将库提供的函数分为 6 种类型。(这种分类纯粹是为了方便讨论而进行的)。每个函数都在以下文件中声明:dc1394_control.h我们建议您在阅读后续章节时手边保留一份该文件。

  1. 获取/查询函数

  2. 设置函数

  3. 打印函数

  4. 设置函数

  5. Format7 函数

  6. 释放函数


4.2.1. 获取/查询函数

这些函数用于获取摄像头的各种功能的值,关于 ISO 通道、触发模式、帧率、格式和模式的信息。 这些函数包含getquery在其名称中。

例如,

int dc1394_get_gamma (raw1394handle_t handle, node_t node, unsigned
int *gamma)
获取摄像头的伽马属性值。

大多数get函数至少需要三个参数

  1. 两者都是raw1394handle_tnode_t节点:这两个节点一起标识摄像头

  2. 用于获取值的指针,例如,int * gamma

如果我们尝试跟踪函数调用的流程,我们可以了解实际发生了什么

图 1. 获取函数调用流程

如果get您调用的函数旨在检索摄像头功能的值(功能枚举中列出的参数的值),则该get函数将调用另一个get函数(GetFeatureValue),该函数将功能的枚举值作为输入。例如,在伽马值的情况下,该函数将 422 作为值传递(该值可以从dc1394_control.h文件中给出的枚举中计算得出)。

最终,将调用GetCameraControlRegister函数,该函数的任务是从适当的控制寄存器中获取值。此函数将偏移值(octlet_t offset),即来自基本寄存器的偏移量,作为其调用的输入。函数原型在dc1394_internal.h.

GetCameraControlRegister (raw1394handle_t handle, nodeid_t node,
                         octlet_t offset, quadlet_t *value)

以上操作会将最终结果存储在value.

Note数据类型说明
 

该库经常使用类型定义的数据类型octlet_tquadlet_t这些在dc1394_internal.h中定义,分别代表 8 字节和 4 字节数据类型。

GetCameraControlRegister函数反过来将调用dc1394_get_camera_info()以获取基本寄存器的地址

camera->ccr_base = CONFIG_ROM_BASE + info->ccr_offset;

一旦知道了基本寄存器和偏移量,raw1394_read()将被GetCameraControlRegister调用以读取实际值。

现在get函数(dc1394_get_gamma)使用GetCameraControlRegister返回的值来修改参数 gamma。

int dc1394_get_gamma (raw1394handle_t handle, node_t node , unsigned int *gamma )

这样,用户就可以获得他查询的摄像头参数的值。


4.2.2. 设置函数

这些函数用于设置各种摄像头功能的值。 几乎每个 get 函数都有对应的 set 函数。您可以通过搜索set字符串来识别这些函数。

例如,

dc1394_set_gamma (raw1394handle_t handle, nodeid_t node, int
gamma)

get函数一样,此函数需要raw1394handle_tnodeid_t来进行摄像头识别。

另一个参数,gamma是用户指定的伽马参数值。

函数调用的流程对于理解实际发生的情况非常有帮助。该流程与get函数的流程完全相同。唯一的区别是这次所有的中间函数也是set函数,并且没有使用raw1394_read(),而是使用raw1394_write()将摄像头参数的值写入寄存器。

图 2. 设置函数调用流程


4.2.3. 打印函数

有三个可用的打印函数

  1. dc1394_print_camera_info: 此函数用于打印存储在 dc1394camera_info 结构中的值。打印函数通常在dc1394_get_camera_info()之后调用,以确保 dc1394camera_info 结构不为 NULL。

  2. dc1394_print_feature: 此函数用于打印任何功能的值。它将指向 dc1394_feature_info 结构的指针作为输入。

  3. dc1394_print_feature_set: 此函数用于打印摄像头中所有功能的值。它将指向 dc1394_feature_set 结构的指针作为输入。该函数调用基本上在 for 循环中重复调用 dc1394_print_feature。


4.2.4. 设置函数

顾名思义,这些函数用于准备摄像头以开始抓取图像。为了设置摄像头,必须将一些参数传递给该函数。参数的数量和类型是特定于设置函数的,但本质上必须在所有设置函数中传递三个参数:raw1394handle_t, nodeid_t和指向 dc1394_cameracapture 结构的指针(这是为了提供抓取图像的缓冲区并保持属性,例如帧的高度和宽度)。

如前所述,raw1394handle_tnodeid_t唯一地定义了摄像头,而指向 dc1394_cameracapture 的指针提供了要抓取的图像帧的缓冲区。它还存储有关帧的宽度和高度的信息,这在图像处理时非常有用。

传递的其他参数是数据速度、帧速率、图像格式、图像模式和 ISO 通道号。

如果摄像头使用 DMA,则需要提供 DMA 缓冲区的数量DMA buffers丢帧是需要提供的。在大多数应用程序中,您需要将 DMA 缓冲区的数量设置得相对较低(即 1),这可以确保您以接近实时的状态查看帧。丢帧对于实时性也很重要,因为它会导致捕获函数丢弃 DMA 环形缓冲区中缓冲的帧,除了最后一帧。我们将在本指南的后面部分讨论 DMA 函数

各种设置函数是

  1. dc1394_dma_setup_capture

  2. dc1394_setup_capture

  3. dc1394_setup_format7_capture

  4. dc1394_dma_setup_format7_capture

这些设置函数已在dc1394_capture.c.

中定义。在传递参数值(例如数据速度、格式、模式和通道)时,您可以选择自己提供值,也可以指示该函数从摄像头获取值。这可以通过传递QUERY_FROM_CAMERA来代替该函数参数的实际值来完成。

基本控制流程很容易理解

图 3. 设置函数调用流程

如图 3 所示,Setup 函数反过来调用各种 set 函数,以将参数值设置为摄像头寄存器。在 DMA 设置的情况下,在调用 set 函数后,调用 ioctl 系统调用为 DMA 缓冲区分配内存并返回指针capture_buffer在用户空间中。

如果 DMA 设置不正确,ioctl 系统调用将失败

设置函数还会为 camera _capture 缓冲区分配内存

camera->capture_buffer=(int*)malloc(camera->quadlets_per_frame*4);


4.2.5. Format7 函数

仅当摄像头设置为 Format7 时才使用这些函数。 首选此格式,因为它允许用户根据需要定义要捕获的图像的大小。 默认情况下,大小为 1024x768; 您可以将其设置为不同的尺寸,例如 960x720。

所有 Format7 函数在其函数名称中都具有format7,并且这些函数已在单独的文件dc1394_format7.c.

中定义。Format7 的设置函数与普通设置略有不同,因为它还要求帧的大小,而您不必传递格式参数作为设置函数,因为它仅用于特定格式,例如 Format7。函数调用流程与上一节中讨论的流程相同。

Format7get函数称为查询函数。该机制与普通的 get/query 函数不同:它们不调用GetCameraControlRegister; 而是调用GetCameraFormat7Register().

以下流程图将使差异更加明显

图 4. Format7 查询函数调用流程

Format7 查询函数将调用GetCameraFormat7Register,该函数应从摄像头的控制和状态寄存器中读取值。该函数反过来将调用QueryFormatCSROffset以了解已查询的特定信息的偏移量。获得偏移量后,raw1394_read用于实际读取值。

Format7 设置函数也遵循相同的逻辑,但显而易见的区别是读取函数被写入函数替换,例如,SetCameraFormat7Registerraw1394_write().


4.2.6. 释放函数

这些是我们识别的最后一组函数。这些函数的基本工作是释放由设置例程分配给捕获缓冲区的内存。这对于防止系统出现内存泄漏至关重要。

这些函数在dc1394_capture.c

dc1394_release_camera()

中定义。此函数反过来调用free (camera -> capture_buffer),释放内存。

类似地,释放函数可用于 DMA 设置。


4.3. Coriander:libdc1394 库的 GUI

Coriander 有助于轻松处理 IEEE1394 摄像头。 它使用上面讨论的函数和库,并为它们提供 GUI。 Coriander 的主要优点是它可以节省通常浪费在摄像头设置上的时间。 此外,Coriander 仅显示摄像头上存在的功能和属性,因此您可以判断摄像头对于您的应用程序开发的实用性。 Coriander 最重要的功能是它能够在运行时显示捕获的图像。

Coriander 还允许用户将 BGGR 图像转换为 RGB。 我们将在后面的章节中详细讨论这些类型的图像的含义。 在理解 Coriander 的功能方面可能有用的一些文件是:

  1. thread_iso.c

  2. Camera.c

  3. main.c

Coriander 主页包含一个出色的用户手册,如果遇到任何困难,可以使用该手册:http://damien.douxchamps.net/ieee1394/coriander/manual.php

我们对 Coriander 的使用仅限于检查摄像头是否正常工作并确认焦点是否正确。 我们将在后面的章节中提供有关 Coriander 的更多使用信息。


4.4. 示例:如何从 IEEE1394 摄像头抓取图像

在本节中,我们将演示如何编写一个小的程序来从摄像头抓取图像。 我们采用了库 tar 文件中的示例程序 (grab_gray_image.c)。 我们删除了一些行以提高代码的可读性。 我们在下面提供了对该代码的解释。 为了让您清楚地了解代码的哪个部分做什么,我们按任务将代码行分组在一起如下。

  #include <stdio.h>
  #include <libraw1394/raw1394.h>
  #include <libdc1394/dc1394_control.h>
  #include <stdlib.h>                                     (1)
  #define IMAGE_FILE_NAME "Image.pgm"

  int main(int argc, char *argv[]) 
  {

    FILE* imagefile;
    dc1394_cameracapture camera;
    int numNodes;
    int numCameras;
    raw1394handle_t handle;
    nodeid_t * camera_nodes;                              (2)

         /* Open ohci and asign handle to it */

         handle = dc1394_create_handle(0);                (3)
    if (handle==NULL)
    {
         fprintf( stderr, "Unable to aquire a raw1394 handle\n\n"
                    );
         exit(1);
    }

         /* get the camera nodes and describe them as we find them */
                                                          (4)
    numNodes = raw1394_get_nodecount(handle);
    camera_nodes = dc1394_get_camera_nodes(handle,&numCameras,1);
    fflush(stdout);
    if (numCameras<1)
    {
      fprintf( stderr, "no cameras found :(\n");
      dc1394_destroy_handle(handle);
      exit(1);
    }
    printf("working with the first camera on the bus\n");

    if( camera_nodes[0] == numNodes-1)                    (5)
    {
      fprintf( stderr, "\n"
         "Sorry, your camera is the highest numbered node\n");
      dc1394_destroy_handle(handle);
      dc1394_free_camera_nodes(camera_nodes);
      exit( 1);
    }

    /*setup capture */

    if (dc1394_setup_capture(handle,camera_nodes[0],      (6)
                             0, /* channel */ 

                             FORMAT_VGA_NONCOMPRESSED,
                             MODE_640x480_MONO,
                             SPEED_400,
                             FRAMERATE_7_5,
                             &camera)!=DC1394_SUCCESS) 
    {
      fprintf( stderr,"unable to setup camera-\n"
         "check line %d of %s to make sure\n"
         "that the video mode,framerate and format are\n"
         "supported by your camera\n",
         __LINE__,__FILE__);
      dc1394_release_camera(handle,&camera);
      dc1394_destroy_handle(handle);
      dc1394_free_camera_nodes(camera_nodes);
      exit(1);
    }
    dc1394_free_camera_nodes(camera_nodes);

    /* set trigger mode */

    if( dc1394_set_trigger_mode(handle, camera.node, TRIGGER_MODE_0)
        != DC1394_SUCCESS)
    {                                                     (7)
      fprintf( stderr, "unable to set camera trigger mode\n");
#if 0
      dc1394_release_camera(handle,&camera);
      dc1394_destroy_handle(handle);
      exit(1);
#endif
    }

    /* have the camera start sending us data*/

    if (dc1394_start_iso_transmission(handle,camera.node)
        !=DC1394_SUCCESS) 
    {                                                     (8)
      fprintf( stderr, "unable to start camera iso transmission\n");
      dc1394_release_camera(handle,&camera);
      dc1394_destroy_handle(handle);
      exit(1);
    }

    /* capture one frame */

    if (dc1394_single_capture(handle,&camera)!=DC1394_SUCCESS) 
    {
      fprintf( stderr, "unable to capture a frame\n");    (9)
      dc1394_release_camera(handle,&camera);
      dc1394_destroy_handle(handle);
      exit(1);
    }

    /* Stop data transmission */

    if (dc1394_stop_iso_transmission(handle,camera.node)!=DC1394_SUCCESS)
    {
      printf("couldn't stop the camera?\n");              (10)
    }

    /* save image as 'Image.pgm' */

    imagefile=fopen(IMAGE_FILE_NAME, "w");

    if( imagefile == NULL)
    {
      perror( "Can't create '" IMAGE_FILE_NAME "'");
      dc1394_release_camera(handle,&camera);
      dc1394_destroy_handle(handle);
      exit( 1);
    }

    /* Adding the pgm file header */

    fprintf(imagefile,"P5\n%u %u 255\n", camera.frame_width,
         camera.frame_height );
                                                          (11)
    /* Writing to the file */

    fwrite((const char *)camera.capture_buffer, 1,
         camera.frame_height*camera.frame_width, imagefile);
    fclose(imagefile);                                    (12)
    printf("wrote: " IMAGE_FILE_NAME "\n");

    /* Close camera */

    dc1394_release_camera(handle,&camera);
    dc1394_destroy_handle(handle);
    return 0;                                             (13)
}
(1)
包含头文件,这意味着包含

  • libraw1394/raw1394.h

  • libdc1394/dc1394_control.h

这些是访问库的函数所必需的。

(2)
声明以下数据类型中的三个变量

  • dc1394_cameracapture

  • raw1394handle_t

  • nodeid_t *

raw1394handle_tnodeid_t *是唯一标识摄像头所必需的。 此外,raw1394handle_t用于保存为符合 OHCI 标准的主机卡创建的句柄。

(3)
打开 ohci 并为其分配一个句柄。

这通过以下方式完成:

handle=dc1394_create_handle(0)

其中handleraw1394handle_ttype> 类型。参数0指主机卡上摄像头的位置(主机卡上可能有多个插槽;0 表示摄像头在第一个插槽上)。

如果传递了错误的数字,则不会创建句柄。

(4)
获取摄像头节点

可以有多个摄像头节点,因为 IEEE1394 支持在单个端口上连接多个设备。但为了便于讨论,我们假设只有一个摄像头存在。以下是如何获取节点:

int numNodes = raw1394_get_nodecount(raw1394handle_t handle)
camera_nodes=dc1394_getcamera_nodes(handle,&numCameras,1)

如果返回的摄像头数量是numCameras < 1,这意味着没有检测到摄像头。

1函数中的 意味着将在控制台上显示找到的摄像头节点的打印描述。

(5)
检查以下条件:camera_nodes[0]==numNodes - 1。它必须为 false。对此的解释超出了本文档的范围。
(6)
调用 setup 函数并传递各种参数。例如,让我们检查一下上面的示例的 setup 函数:
     dc1394_setup_capture(

/* handle and camera_nodes[0] uniquely identifies the camera */
     handle,
    camera_nodes[0],
    0, /* channel */

/*format of the Image */
    FORMAT_VGA_NONCOMPRESSED, MODE_640x480_MONO, /* mode of the image */
    SPEED_400, /* data speed */
    FRAMERATE_7_5, /*Frame rate */
    &camera /*dc1394_cameracapture type pointer *./
    )!=DC1394_SUCCESS) 

我们的建议是首先从摄像头查询各种参数,然后再传递。 这有助于成功设置,因为用户通常不知道必须在 setup 函数调用中传递的各种参数的实际值,最终传递了错误的值。 在这种情况下,setup 没有正确完成,摄像头也没有被初始化。

我们正在列出适当的get函数,这些函数应首先被调用,以便获得设置参数的正确值:

  1. dc1394_get_iso_channel_and_speed(handle,camera_nodes[0], &channel,&speed);/* 获取通道和数据速度 */

  2. dc1394_get_video_format(handle,camera_nodes[0],&format);/* 获取格式 */

  3. dc1394_get_video_framerate(handle,camera_nodes[0],&framerate);/* 获取帧速率 */

  4. dc1394_get_video_mode(handle,camera_nodes[0],&mode);/* 获取模式 */

因此,上面的dc1394_setup_capture函数调用看起来像这样:

     dc1394_setup_capture(

/* handle and camera_nodes[0] uniquely identifies the camera */
     handle, 
     camera_nodes[0],

   /*we pass the variables instead of actual values */
     channel ,
     format, 
     mode, 
     speed,
     framerate,
     &camera /*dc1394_cameracapture type pointer *./
     )!=DC1394_SUCCESS) 
(7)
设置触发模式。 这通常不是必需的。 这就像亮度一样。

dc1394_set_trigger_mode(handle,camera.node,TRIGGER_MODE_0)将触发模式设置为 0。

Note关于节点的说明
 

我们已经传递了camera.node这表明正在使用 dc1394_cameracapture 结构,并且仅引用已设置摄像头的特定节点,但是我们也可以使用camera_nodes[0].

(8)
让摄像头开始向用户发送数据。 这是通过启动 ISO 传输来完成的。 使用以下函数:

dc1394_start_iso_transmission(handle,camera.node)

(9)
通过调用以下函数捕获一帧:

dc1394_single_capture(handle,&camera)

其中camera是指向 dc1394_cameracapture 结构的指针。 此函数将抓取图像并将其存储在结构提供的缓冲区 (capture_buffer) 中。

为了捕获多帧,请使用 for 循环并将该函数放入其中。

for( i=0;i<100 ;i++)/* to capture 100 images*/
dc1394_single_capture(handle,&camera)
(10)
抓取图像后,通过调用以下函数停止数据传输:

dc1394_stop_iso_transmission(handle,camera.node)

(11)
将 PGM 文件头添加到捕获的缓冲区,以便使用 gimp 查看图像。
(12)
使用 fwrite 保存捕获的图像,方法是将缓冲区 (camera.capture_buffer) 写入文件。 图像的高度和宽度等其他参数可以从同一结构中提取:camera.frame_width, camera.frame_height.
(13)
关闭摄像头。 此步骤对于防止内存泄漏是必要的。
dc1394_release_camera(handle,&camera);
dc1394_destroy_handle(handle);

为了编译程序,请使用:gcc -o grabImage grabImage.c -ldc1394_control -lraw1394,其中grabImage.c是您的程序。

我们希望在了解了这种算法方式的解释后,您可以轻松地理解示例代码。

如果我们使用 Format7 图像格式,则只需要更改setup_capture函数。 让我们看一下 setup 函数:

      dc1394_setup_format7_capture(
  /* handle and camera_nodes[0] uniquely identifies the camera */
            handle,
            camera_nodes[0],
            channel, /* channel */
            mode , /*mode */
            bytes_per_packet , 
            left ,/*area of interest start column */
            right, /*area of interest start row */
            width,/* area of interest width */
            height /* area of interest height */
           &camera /* dc1394_cameracapture type pointer *./
            )!=DC1394_SUCCESS) 
其中通道、速度、bytes_per_packet、速度、模式的参数值是通过以下函数找到的:

  1. dc1394_get_iso_channel_and_speed(handle,camera_nodes[0], &channel,&speed)/* 获取通道和数据速度 */

  2. dc1394_get_video_mode(handle,camera_nodes[0] &mode);/* 获取模式 */

  3. dc1394_query_format7_byte_per_packet(handle, camera_nodes[0], mode ,&bytes_per_packet);/* 获取每个数据包的字节数,这取决于模式 */

  4. left 和 top 的值可以设置为QUERY_FROM_CAMERA,也可以由用户直接指定。

  5. 宽度和高度的值取决于用户想要的帧的大小。例如,如果想要 960x720,则将 960 作为宽度,720 作为高度传递。


4.5. 示例:如何使用 DMA 从 IEEE1394 摄像头抓取图像

本节由 Ian Peikon 贡献。和 Tim Hanson现在,我们将展示一个示例,说明如何通过使用 DMA 调用从摄像头抓取帧。该示例很容易理解,因为逻辑与之前的示例相同。稍后,我们将比较先前示例(没有 dma)和此示例(有 dma)中使用的函数调用,以便更好地理解。

#include <stdio.h>
#include <libraw1394/raw1394.h>
#include <libdc1394/dc1394_control.h>
#include <stdlib.h>  
#include <iostream>
#define IMAGE_FILE_NAME "Image.pgm"
using namespace std;

int main(int arc, char *argv[]){
        FILE * imagefile;
        dc1394_cameracapture camera;
        int numNodes;
        int numCameras;
        raw1394handle_t handle;
        nodeid_t * camera_nodes;
        unsigned int channel, speed, format, framerate, mode;
        
        /*Step 1: Open ohci and assign a handle to it.*/
	/*=======================================================*/
	handle = dc1394_create_handle(0);
        if(handle==NULL){
                fprintf(stderr, "Unable to acquire a handle. \n\n");
        }
        else{
		 cout <<"dma1394: Handle aquired successfully-" <<handle<<"\n";
        }
        
/*Step 2: Get the camera nodes and describe them as we find them.*/
	/*=========================================================================*/
	numNodes = raw1394_get_nodecount(handle);
        camera_nodes = dc1394_get_camera_nodes(handle, &numCameras, 1);
        fflush(stdout);
        if (numCameras<1){
                fprintf(stderr, "No cameras found :( \n");
                dc1394_destroy_handle(handle);
                return -1;
        }
        else{
                cout<<"dma1394:"<< numCameras<<" cameras found. \n";
        }
        printf("dma1394: Working with the first camera on the bus.\n");
        if(camera_nodes[0] == numNodes-1){
                fprintf(stderr, "\n" "dma1394: Sorry, your camera is the highest numbered node.\n");
                dc1394_destroy_handle(handle);
                dc1394_free_camera_nodes(camera_nodes);
                return -1;
        }
        
/*Step 3: Setup Capture*/
	/*=====================================================================*/
	/*Using camera functions to get the params by querying them*/
	cout<<"INFO FOR DEBUG: \n"
                        "num_dma_buffers: "<< camera.num_dma_buffers<<"\n";
        dc1394_get_iso_channel_and_speed(handle, camera_nodes[0], &channel, &speed); /*get channel and speed*/
	dc1394_get_video_format(handle, camera_nodes[0], &format); /*get format*/
	dc1394_get_video_framerate(handle, camera_nodes[0], &framerate); /*get framerate*/
	dc1394_get_video_mode(handle, camera_nodes[0], &mode); /*get mode*/
	cout<<"dc1394: Got parameters from the camera.\n"
                "=======================================\n"
                "Channel: "<< channel<< " \n"
                "Speed: " <<speed <<" \n"
                "Format: "<< format <<" \n"
                "Framerate: " <<framerate<< "\n"
                "Mode: "<< mode <<"\n";
        camera.num_dma_buffers = 8; /* set the dma buffers */
        camera.drop_frames = 1; /* set the number of drop frames */
        camera.dma_device_file = NULL;
        if(dc1394_dma_setup_capture(handle, camera_nodes[0], channel, format, mode, speed, framerate, camera.num_dma_buffers, camera.drop_frames, camera.dma_device_file, &camera) !=DC1394_SUCCESS){
                fprintf(stderr, "dma1394: Unable to setup camera.\n" 
                                        "Check line %d of %s to ensure that the options set are supported by your camera.\n", __LINE__, __FILE__);
                dc1394_destroy_handle(handle);
                dc1394_free_camera_nodes(camera_nodes);
                return -1;
        }
        else{
                printf("dma1394: Capture has been setup.\n");
        }
        dc1394_free_camera_nodes(camera_nodes);

        //Set Trigger Mode -- Generally not required thus I will comment it out.
	/*if(dc1394_set_trigger_mode(handle, camera.node, TRIGGER_MODE_0) != DC1394_SUCCESS){
                fprintf(stderr, "dma1394: Unable to set the camera trigger mode. Refer to line %d in %s.\n", __LINE__, __FILE__);
                dc1394_release_camera(handle, &camera);
                dc1394_destroy_handle(handle);
        }
        else{
                printf("dma1394: Successfully set trigger mode.\n");
        }*/
        
        /*Step 4: Start sending data */
	/*=======================================================*/
	if(dc1394_start_iso_transmission(handle, camera.node) != DC1394_SUCCESS){
                fprintf(stderr, "dma1394: Unable to start the data transmission.\n");
                dc1394_dma_done_with_buffer(&camera);
				dc1394_dma_release_camera(handle, &camera);
                dc1394_destroy_handle(handle);
                return -1;
        }
        else{
                printf("dma1394: Success.  Data Transmission started.\n");
        }
        
        /*Step 5: Capture Something...Anything...PLEASE*/
	/*===============================================================*/
	if(dc1394_dma_single_capture(&camera) != DC1394_SUCCESS){
                fprintf(stderr, "dma1394; DAIM, can't capture a single frame.\n");
				dc1394_dma_done_with_buffer(&camera); /*important step */
                dc1394_dma_release_camera(handle, &camera);
                dc1394_destroy_handle(handle);
                return -1;
        }
        else{
                printf("dma1394: GREAT SUCCESS! Captured a single frame.\n");
        }
        
        /*Step 6: Stop sending data*/
	/*==================================================*/
	if(dc1394_dma_unlisten(handle, &camera) != DC1394_SUCCESS){
        		fprintf(stderr, "Can't unlisten iso channel! \n");
        }
        else{
                printf("dma1394: DMA unlistened. \n");
        }
        
	if(dc1394_stop_iso_transmission(handle, camera.node) != DC1394_SUCCESS){
                fprintf(stderr, " Can't stop the camera!! \n");
        }
        else{
                printf("dma1394: Data transmission terminated. \n");
        }
        
        /*Step 7: Save our image*/
	/*===============================================================*/
	imagefile=fopen(IMAGE_FILE_NAME, "w");
        if(imagefile==NULL){
                perror("dma1394: Can't create' "IMAGE_FILE_NAME" ' ");
                dc1394_dma_done_with_buffer(&camera);
				dc1394_dma_release_camera(handle, &camera);
                dc1394_destroy_handle(handle);
                return -1;
        }
        else{
                cout<<"dma1394: Saved image in "<<IMAGE_FILE_NAME<<".\n";
        }
         /*Add pgm file header*/
	fprintf(imagefile, "P5\n%u %u 255\n", camera.frame_width, camera.frame_height);
       
		/*write to file*/
	fwrite((const char *)camera.capture_buffer, 1, camera.frame_height*camera.frame_width, imagefile);
        fclose(imagefile);
        printf("dma1394: wrote: " IMAGE_FILE_NAME "\n");
        
        /*Step 8: Close camera*/
	/*=============================================================*/
	dc1394_dma_done_with_buffer(&camera);
	dc1394_dma_release_camera(handle, &camera);
        dc1394_destroy_handle(handle);
        printf("dma1394: Camera released.  Exiting. \n");
        
        //Step END
	return 0;

}

  

就像之前的示例(第 4.4 节)一样,该程序包括必要的头文件(libraw1394/raw1394.hlibdc1394/dc1394_control.h),然后声明以下数据类型的三个变量。

  • dc1394_cameracapture

  • raw1394handle_t

  • nodeid_t *

此后,将执行以下步骤,这些步骤与之前的示例(第 4.4 节)非常相似。

  1. 打开 ohci 并使用函数为其分配句柄:dc1394_create_handle(0)
  2. 使用函数获取摄像头节点:dc1394_get_camera_nodes
  3. 找出在摄像头设置期间需要传递的各种参数。这是通过使用以下函数查询摄像头来完成的:

    • dc1394_get_iso_channel_and_speed():获取通道和速度信息
    • dc1394_get_video_format():获取摄像头格式
    • dc1394_get_video_framerate():获取帧速率信息
    • dc1394_get_video_mode():获取摄像头模式
    我们还需要设置dma 缓冲区为 8,并且丢帧数为 1。

    一旦获得了参数的值(帧速率、格式、模式、通道、速度等),它们就会被传递到dc1394_dma_setup_capture,然后设置摄像头并启用它以进行视频捕获。对于 DMA,您必须传递 dma 缓冲区数量和丢帧参数。

  4. 设置好摄像头后,调用dc1394_start_iso_transmission()。此函数启动从摄像头到总线的数据传输。
  5. 现在使用dc1394_dma_single_capture()捕获一帧。此函数会将帧捕获到 dma 缓冲区中。

    抓取帧后,使用dc1394_dma_done_with_buffer()释放 dma 缓冲区。这是必需的,以便可以重复使用内存。至关重要的是dc1394_dma_done_with_buffer()始终遵循dma_capture函数调用。

    因此,为了捕获多个帧,代码将是:
    for( i=0;i<100 ;i++)/* to capture 100 images*/
    	{
    	dc1394_dma_single_capture(&camera);
    	dc1394_dma_done_with_buffer(&camera);
    	}
    
  6. 通过调用以下函数停止侦听 ISO 通道:dc1394_dma_unlisten()

    通过调用以下函数终止数据传输:dc1394_stop_iso_transmission()

  7. 将图像保存在文件中,并添加 PGM 标头以使用 gimp 进行查看。
  8. 最后调用dc1394_release_camera以释放摄像头。

如果 DMA 设置不正确,您将收到以下错误消息:

VIDEO1394_IOC_LISTEN_CHANNEL ioctl failed

出现此错误的原因有很多,将在“问题”部分(第 4.7 节)中进一步解释。

因此,我们可以看到使用 DMA 进行图像捕获与非 DMA 方法没有太大区别。 我们在下表中比较了两种方法中使用的函数。

非 DMA 步骤

DMA 步骤

评论

dc1394_create_handle

dc1394_create_handle

创建句柄

dc1394_get_camera_nodes

dc1394_get_camera_nodes

获取摄像头节点

dc1394_setup_capture

dc1394_dma_setup_capture

调用设置函数以设置摄像头的各种参数。

dc1394_start_iso_transmission

dc1394_start_iso_transmission

开始将数据从摄像头发送到总线

dc1394_single_capture

dc1394_dma_single_capture, dc1394_dma_done_with_buffer

捕获一帧

dc1394_stop_iso_transmission

dc1394_dma_unlisten, dc1394_stop_iso_transmission

停止将数据从摄像头发送到总线

dc1394_release_camera, dc1394_destroy_handle

dc1394_dma_done_with_buffer, dc1394_dma_release_camera, dc1394_destroy_handle

关闭摄像头


4.6. 如何获取彩色图像:拜耳模式概念

前一节中示例代码抓取的图像不是彩色的(我们有意使用了“不是彩色的”一词,因为图像也不是灰度图)。 实际上是拜耳模式。 在本节中,我们将概述拜耳模式以及如何使用它们来获取彩色图像。

数码相机使用一种称为图像传感器的固态设备。 这些指甲大小的硅芯片包含数百万个称为感光点的光敏二极管。 当您使用数码相机拍照时,照射到传感器上每个感光点的光强度会记录为信号。 根据相机,记录 12 位或 14 位数据。 在 12 位时,相机可以记录 4,096 个亮度级别。 在 14 位时,相机可以记录 16,384 个亮度级别。 这被称为位深度。 位深度越高,细节越精细,色调之间的过渡越平滑,动态范围越高(相机在突出显示区域和阴影区域中保持细节的能力)。 但是在捕获时,数字图像是灰度的,而不是彩色的。 为了记录颜色信息,传感器上的每个像素都覆盖有红色、绿色或蓝色滤镜,颜色交替。 颜色滤镜的常见排列方式是 拜耳模式阵列,该阵列交替颜色,但使用的绿色滤镜数量是红色和蓝色的两倍。 使用两倍的绿色滤镜是因为我们的眼睛对绿色更敏感。 这种滤镜模式或序列可能会有所不同,但是柯达发明的被广泛采用的拜耳模式是一种重复的 2x2 排列方式。 每个像素仅对一种颜色(一个光谱带)敏感。

典型的拜耳模式看起来像这样:

图 5. 拜耳模式

标记为 B 的瓦片正方形(像素)表示此特定瓦片仅对蓝光敏感,依此类推。

拜耳模式可以分为 4 种类型,具体取决于我们如何排列颜色。 拜耳模式的命名是通过从模式最顶角获取 2x2 矩阵,并按 (0,0)、(0,1)、(1,0)、(1,1) 顺序读取颜色来完成的。 因此,对于上面的拜耳模式,如果我们将 2x2 矩阵作为

图 6. BGGR 模式

那么该模式被称为 BGGR。

其他可能的模式是:

图 7. 其他模式

我们在前一个示例中获得的图像也是拜耳模式图像,也称为 RAW 图像。 它存储在camera.capture_buffer。 为了查看我们捕获的内容,我们将此 RAW 图像转换为 .PGM,方法是添加标头(请参见第 4.4 节中的说明)。

为了获得彩色图像,拜耳模式图像被转换为 RGB 图像。 RGB 图像是拜耳模式图像的增强版本; 我们尝试找到每个像素中两个缺失颜色的值(请记住,传感器的每个像素都被拜耳模式滤镜覆盖,因此我们默认情况下在任何像素处都获得单一颜色)。 这是通过使用不同的算法完成的,例如最近邻边缘感知等等。

图 8. RAW 到 RGB

其中阴影部分的值需要通过算法计算得出。下标表示 R、G 和 B 的值属于拜耳模式上的哪个瓦片。请注意,图像大小将变为拜耳模式的 3 倍。为了查看 RGB 图像,我们通过添加位图头将其转换为位图,或 .BMP 图像。

为了更清楚地了解发生了什么,我们提供了以下图表

图 9. 拜耳到位图的转换

让我们了解 RAW 到 RGB 转换算法的工作原理。我们将详细研究最近邻算法。其他算法在以下网址有详细解释:http://www-ise.stanford.edu/~tingchen/main.htm

这些算法中的大多数都使用某种插值。


4.6.1. 最近邻算法

在这种插值方法中,每个插值后的输出像素都被分配输入图像中最接近的像素的值。最近的邻居可以是上方、下方、左侧或右侧的像素中的任何一个。

一个例子将使逻辑清晰。我们尝试找到 3x3 块(显示为阴影区域)的 R、B 瓦片的 G 值。空白方格要么具有 R 值,要么具有 B 值。我们没有显示它们,只是为了使图更容易理解)。这里我们假设使用左侧相邻像素值来填充缺失的像素值。

图 10. 最近邻

左侧的表格显示了拜耳模式图像的 G 值。为了找出最初只包含 R 或 B 的其他方格中缺失的 G 值,我们使用以下方法

找到最近的 G 值方格,并将该方格的 G 值复制到 R (B) 方格上。这已在上图中说明。G7 旁边的方格具有 R 或 B 的值。因此,为了获得 G8(方格 8 的 G 值),我们复制了方格 7 的 G 值,因为它最接近,所以 G8 = G7。类似地,我们填充了其他非 G 值方格。

当找到绿色方格的 R 和 B 值时,应用相同的逻辑。


4.6.2. 理解如何使用 IEEE1394 相机抓取彩色图像的示例程序

现在我们已经介绍了 RAW、RGB 和转换算法的基本概念,我们认为您可以理解一个可以获取彩色图像的示例程序。

我们选择的格式是 Format7,因为我们使用的相机只响应此格式。我们将使用另一个示例代码,该代码提供了算法的实现。该程序是conversions.cpp位于grabdma文件夹中,可从 http://www.ptgrey.com/support/kb/data/grabdma.tgz 下载。

要运行该代码,请确保您具有以下文件

  • conversions.h

  • conversions.cpp

  • grabcolor.cpp- 同样来自grabdma文件夹。我们根据我们的需求修改了代码,并删除了一些行。由于大部分代码与之前讨论的代码相同,因此我们只解释不同的部分。这些部分已经强调

#include <stdio.h>
#include <libraw1394/raw1394.h>
#include <libdc1394/dc1394_control.h>
#include <stdlib.h>
#include <onversions.h>

#define IMAGE "Image.rgb"

int main(int argc, char *argv[]) 
{
  FILE* imagefile;
  dc1394_cameracapture camera;
  int numNodes;
  int numCameras;
  raw1394handle_t handle;
  nodeid_t * camera_nodes;
  int channel,speed,mode,bytes_per_packet;

   /* Open ohci and asign handle to it */

    handle = dc1394_create_handle(0);
  if (handle==NULL)
  {
    fprintf( stderr, "Unable to aquire a raw1394 handle\n\n"
       "Please check \n"
         " - if the kernel modules `ieee1394',`raw1394' and `ohci1394' are loaded \n"
         " - if you have read/write access to /dev/raw1394\n\n");
exit(1);
  }

  /* get the camera nodes and describe them as we find them */

  numNodes = raw1394_get_nodecount(handle);
  camera_nodes = dc1394_get_camera_nodes(handle,&numCameras,1);
  fflush(stdout);
  if (numCameras<1)
  {
    fprintf( stderr, "no cameras found :(\n");
    dc1394_destroy_handle(handle);
    exit(1);
  }
  printf("working with the first camera on the bus\n");

  if( camera_nodes[0] == numNodes-1)
  {
    fprintf( stderr, "\n"
           "Sorry, your camera is the highest numbered node\n");
    dc1394_destroy_handle(handle);
    dc1394_free_camera_nodes(camera_nodes);
    exit( 1);
  }

  /*obtain the values of the parameter from the camera */

dc1394_get_video_mode(handle,camera_nodes[0],(unsigned int *)&mode); 

dc1394_get_iso_channel_and_speed(handle,camera_nodes[0],
(unsigned int *)&channel, (unsigned int *)&speed);

dc1394_query_format7_byte_per_packet(handle,camera_nodes[0],(unsigned)
mode,&bytes_per_packet);

  /*setup capture */

if (dc1394_setup_format7_capture(handle,
           camera_nodes[0],
         channel , /* channel */ 
         mode,
         speed,
         bytes_per_packet,
         0,
         0,
         960,
         720, 
         &camera)!=DC1394_SUCCESS) 
  {
    fprintf( stderr,"unable to setup camera-\n"
         "check line %d of %s to make sure\n"
         "that the video mode,framerate and format are\n"
         "supported by your camera\n",
         __LINE__,__FILE__);
    dc1394_release_camera(handle,&camera);
    dc1394_destroy_handle(handle);
    dc1394_free_camera_nodes(camera_nodes);
    exit(1);
  }
  dc1394_free_camera_nodes(camera_nodes);

  /* have the camera start sending us data*/

  if (dc1394_start_iso_transmission(handle,camera.node)
      !=DC1394_SUCCESS) 
  {
    fprintf( stderr, "unable to start camera iso transmission\n");
    dc1394_release_camera(handle,&camera);
    dc1394_destroy_handle(handle);
    exit(1);
  }

  /* capture one frame */

  if (dc1394_single_capture(handle,&camera)!=DC1394_SUCCESS) 
  {
    fprintf( stderr, "unable to capture a frame\n");
    dc1394_release_camera(handle,&camera);
    dc1394_destroy_handle(handle);
    exit(1);
  }

  /*query the camera to determine the Bayer pattern*/

quadlet_t qValue;
   GetCameraControlRegister( handle, 
              Camera_nodes[0],
              0x1040,/* Bayer Tile Mapping register */
              &qValue );

   bayer_pattern_t pattern = BAYER_PATTERN_BGGR;
   switch( qValue )
   {
      case 0x42474752:/* BGGR */
       pattern = BAYER_PATTERN_BGGR;
       break;
      case 0x47524247:/* GRBG */
       pattern = BAYER_PATTERN_GRBG;
       break;
      case 0x52474742: /* RGGB */
       pattern = BAYER_PATTERN_RGGB;
       break;
      case 0x47425247:/* GBRG */
       pattern = BAYER_PATTERN_GBRG;
       break;
      case 0x59595959:/* YYYY = BW */
       fprintf( stderr, "Camera is black and white\n" );
       cleanup();
       return 1;
      default:
      fprintf(stderr,
          "Camera BAYER_TILE_MAPPING register has an unexpected value:\n"
          "\t0x%x\n", qValue );

      return 1;
   }

   int bufferSize = camera.frame_width*camera.frame_height;

   /* assign a buffer of size three time the original image */

   unsigned char* rgbBuffer = new unsigned char[3 * bufferSize];

   unsigned char* src = (unsigned char*)camera.capture_buffer;

   unsigned char* captureBuffer=
            (unsigned char*)camera.capture_buffer;
   for ( int i = 0; i < bufferSize; i++ )
   {
       src[i] = captureBuffer[ i * bytesPerPixel ];
   }

   /* convert to color image */

   BayerNearestNeighbor (src, 
             rgbBuffer, 
             camera.frame_width,
             camera.frame_height,
             pattern );


   /* Stop data transmission */

  if (dc1394_stop_iso_transmission(handle,camera.node)!=DC1394_SUCCESS) 
  {
  printf("couldn't stop the camera?\n");
  }

  /* save image as 'Image.rgb' without adding any pgm header */

  printf( "Saving the image...\n" );
  imagefile = fopen( IMAGE, "w" );

  fwrite( rgbBuffer, 3,
         bufferSize, imagefile );

  fclose( imagefile );

  /* Close camera */

  dc1394_release_camera(handle,&camera);
  dc1394_destroy_handle(handle);
  return 0;
}

正如我们已经讨论过的GetCameraControlRegister的使用,您可以理解它已被用于找出包含在0x1040处的值。Libdc1394 不提供任何函数来查询此地址,因此我们显式地使用此调用来获取该值。

了解上述函数调用的效用非常重要。请参阅我们在上一节中关于拜耳模式滤波器的讨论。我们知道模式可以是 BGGR、RGGB、GRBG 和 GRBG。我们用于将拜耳模式转换为 RGB 的算法需要了解相机滤波器的模式类型,以便它可以执行一些初始化(请参阅conversions.cpp了解详细信息)。函数调用中的第四个参数

   BayerNearestNeighbor (src, 
             rgbBuffer, 
             camera.frame_width,
             camera.frame_height,
             pattern )

指的是这个值。

BayerNearestNeighbor是我们在上一节中讨论的插值算法的函数调用。它在conversions.cpp.

中实现。 了解了这一点,我们继续讨论 switch-cases。为拜耳模式获得的值 (qvalue) 是十六进制形式,需要解码才能设置变量pattern.

的值。案例语句指示可以由各种相机返回的与拜耳模式相关的各种十六进制值。这些令人望而生畏的值实际上很容易解码。这里有一个技巧

各种颜色的十六进制代码如下:

  • 42h -> B

  • 47h -> G

  • 52h -> R

现在如果qvalue包含 0x42474752,则表示:B (42h) G (47h) G (47h) R (52h) 或 BGGR。因此,我们可以类似地解码所有案例语句。

最后,我们需要声明另一个图像缓冲区,该缓冲区将包含 RGB 图像。请记住,RGB 的大小是拜耳模式图像大小的 3 倍。rgbbuffer因此分配了 3 倍的缓冲区大小 (camera.capture_buffer)。这个缓冲区 (rgbbuffer) 将作为目标缓冲区传递 (BayerNearestNeighbor).

中的第二个参数)。 在缓冲区填充 RGB 值后,我们将其写入文件image.rgb.

为了使用 gimp 查看此图像,我们需要附加一个位图头。我们采用的方法如下

  1. 将 RGB 图像缓冲区保存在文件中,例如,image.rgb.

  2. 创建一个位图头并将其保存在单独的文件中,bmpheader。有关标题详细信息,请参阅 http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html

  3. cat bmpheader image.rgb > bmpfile

  4. 打开bmpfile使用 gimp

要运行该程序,请使用以下步骤

g++ -c conversions.cpp
g++ -o grabcolor conversions.o grabcolor.cpp -lraw1394
-ldc1394_control

Note关于可用算法的说明
 

中有很多可用的算法conversions.cpp,因此,根据要求,您可以调用必要的函数。

Coriander 应用程序可用于了解 RGB 图像在经过不同的转换算法后的外观。例如,它提供了最近邻、边缘感知和降采样转换算法之间的选择。可以通过单击鼠标来观察差异。


4.7. 使用 IEEE1394 相机时遇到的常见问题

现在我们来看看使用相机时遇到的一些常见问题,但我们只列出了我们遇到的那些问题。任何使用该库和相机的人都可能遇到这些问题。一些问题通过适当的推理得到解决,但有些只是尝试性的。问题及其解决方案列出如下。

  • 问题 #1. 安装库和模块后,相机没有响应

    解决方案:我们断开相机并重新连接它。可能需要多次重复此操作。我们无法找出合适的理由,但它有效。

  • 问题 #2. 程序无法编译

    解决方案:如果忘记将可执行文件与 raw1394 和 dc1394_control 链接,则程序将无法编译。 正确的方法是

    gcc -o xx xx.c -lraw1394 -ldc1394_control /*for C files*/
    g++ -o xx xx.cpp -lraw1394 -ldc1394_control /* for Cpp files */
    

  • 问题 #3. 编译失败,并显示错误libdc1394_control.so.13: 无法打开共享对象文件.

    解决方案:检查环境变量 LD_LIBRARY_PATH。它应该包含共享库的路径。在我们的系统上,路径是 /usr/local/lib。必须相应地设置路径。

  • 问题 #4. 程序在执行时挂起。

    解决方案:如果传递给 setup 函数的参数值不受相机支持,则主要会发生这种情况。例如,设置不支持的 mode_640x480_MONO16 模式。我们的建议是,应该始终查询这些值,然后将其传递给该函数(如 第 4.4 节中的示例代码中所述)

  • 问题 #5。IDEO1394_IOC_LISTEN_CHANNEL ioctl 失败正在显示错误消息。

    解决方案:(由 Ian 和 Tim 提供)。导致此错误消息的原因有很多。在命令提示符中键入 dmesg 以更好地了解问题。以下是我们遇到的问题列表 [1]

  • 问题 #6. 使用 CTRL+C 终止图像抓取程序,但下次运行该程序时,它只是挂起。

    解决方案:通常发生此错误是因为相机的功能类似于管道,如果在没有刷新内存通道的情况下终止,则会遇到管道断裂的情况。诀窍是断开相机并重新连接它。也可以尝试通过重新安装 ohci1394、video1394、raw1394、video1394 模块来调试该问题,这有时有效。

  • 问题 #7. 每个参数都正常,安装也很好,但即使这样,当程序执行时,仍然未检测到相机。

    解决方案:这是一个奇怪的问题,我们也经常遇到。该解决方案基于反复试验,但它始终有效:

    1. 首先断开相机,然后重新连接它。

    2. 现在运行 Coriander(这就是我们建议您在开始使用相机之前安装 Coriander 的原因)。

    3. 进行所有必要的调整;例如,选择相机的模式、帧大小等等。

    4. 关闭 Coriander

    5. 执行您的程序。

    宾果!它起作用了。

    Note关于上述第一步的说明
     

    第一步至关重要,因为如果我们未在运行 Coriander 之前断开并重新连接相机,则我们从 Coriander 收到一条关于未找到相机的错误消息。


5. 参考文献

  1. http://kauri.auck.irl.cri.nz/~johanns/libdc1394/libdc1394_FAQ.htmllibdc1394 FAQ,作者 Johann Schoonees。

  2. http://www.ptgrey.com/

  3. http://www-ise.stanford.edu/~tingchen

  4. http://damien.douxchamps.net/ieee1394/coriander/manual.php

  5. Point Grey Dragonfly 相机技术参考手册,版本 2.1.2.1.3

  6. 原始捕捉:重新来一次的机会,作者:Charlotte K. Lowrie,2005-09-01

  7. http://www.linuxfocus.org

  8. http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html


A. 附录 A

A.1. 许可

本文档,《Libdc1394 库对 IEEE 1394 相机的支持 HOWTO》,版权所有 © 2005 Rohit Agarwal & Vikram B。 根据 GNU 自由文档许可证 1.2 版或自由软件基金会发布的任何后续版本,允许复制、分发和/或修改本文档; 没有不变部分,没有封面文本,没有封底文本。 许可证副本可在 https://gnu.ac.cn/copyleft/fdl.html 获取。


A.2. 免责声明

尽管我们已尽最大努力以良好的形式发布此 HOWTO,但对于因根据本文档中包含的信息采取的行动而造成的任何损害,我们概不负责。 不可能在所有配置下测试所有内容,因此本文档中给出的一些提示可能不正确,并且可能无法在您的系统上运行。 如果您发现任何错误,请先告知我们。 我们将尽快重写文档。 本文档按"原样"提供。 我们尽了最大的努力来尽可能准确地编写它,但是您需要自行承担使用此处包含信息的风险。 在任何情况下,我们均不对因使用本作品而造成的任何损害负责。


A.3. 关于作者

Rohit Agarwal 和 Vikram B 正在印度班加罗尔国际信息技术学院攻读 IT 硕士学位。

可以通过以下方式联系他们:

  • rohdimp_24@rediffmail.com <Rohit>

  • vickys_box@rediffmail.com <Vikram>


A.4. 致谢

我们感谢所有朋友的支持。 我们特别感谢我们的同学 Chinmay Narayan 提出的宝贵建议。 我们要将本文献给我们的教授 S. Nagrajan 先生,因为是他激励我们为开源社区做出贡献。

注意

[1]

  • 通道 0 未被使用 - 这意味着您正在监听 iso 通道,然后再次调用捕获函数。 确保不要调用dma_unlisten直到您准备好停止 iso 传输。
  • 通道 0 已经被使用 - 这意味着您已将两个摄像机节点设置为同一通道。 修复您的dma_setup_capture()。 这也可能意味着您尝试多次调用设置函数,但没有在调用之间释放相机。 当您使用多个摄像机时,此错误更有可能发生。
  • 缓冲区 0 已经被使用 - 这意味着您在尝试再次写入 DMA 缓冲区之前没有释放它。 确保交替进行dma_done_with_buffer 调用与 dma_capture_calls