跳转到内容

siso_genicam#

siso_genicam 库用于管理符合 GenICam 标准的相机的发现、相机参数的访问以及相机命令的执行。在本文档中,参数和命令统称为特征 (features)。

要使用该库,应将包含文件 siso_genicam.h 添加到源代码中。

#include <siso_genicam.h>

此外, siso_genicam.lib 应添加到您的 Microsoft Visual Studio 项目中,或者 libsiso_genicam.so 到您的 Linux 项目中。如果您使用 CMake,则包名称为 SisoGenicam,并且库存储在变量中 ${SisoGenicam_LIBRARIES}。请参阅 前提条件 了解有关项目以及如何使用 CMake 的更多详细信息。

该库始终与帧抓取器库 fglib5 结合使用,有关如何将 fglib5 库添加到您的项目中的详细信息,请参阅 The Frame Grabber Library fglib5。

相机控制库中的错误处理#

const char * Sgc_getErrorDescription(
    int result);

API 的大多数函数都会返回一个 int 结果代码。如果函数调用成功执行,则返回值将为 SGC_OK 或者对于某些函数,返回值等于或大于零。在大多数情况下,负值表示出现错误状况。Error Codes 定义在头文件 siso_genicam_error.h,该文件会自动包含在 siso_genicam.h.

函数 Sgc_getErrorDescription() 可用于获取特定结果代码的字符串表示形式。

本文档其余部分的代码示例将不包含错误处理,因为这是针对应用程序特定需求的。不过,在可行的情况下,将对返回代码进行检查,以确保函数成功执行。

相机控制库初始化#

int Sgc_initBoard(
    Fg_Struct * fg,
    unsigned int flags,
    SgcBoardHandle ** board);

void Sgc_freeBoard(
    SgcBoardHandle * board);

若要开始使用 siso_genicam 库,必须首先按照以下所述初始化帧抓取器: The Frame Grabber Library fglib5。接下来,该函数 Sgc_initBoard() 应针对应用程序使用的每个采集卡进行调用。参数 flags 保留用于将来扩展,应用程序应始终传递 0。如果初始化成功,则板卡句柄将存储在传递给参数的变量中 board.

如果应用程序已完成对 siso_genicam 库的使用,则应通过调用以下函数释放所有成功获取的板卡句柄: Sgc_freeBoard() 分别针对它们中的每一个。

以下示例展示了如何获取板卡句柄以开始使用该库:

SgcBoardHandle * board = nullptr;
int result = Sgc_initBoard(fg, 0, &board);
if (result != SGC_OK) {
    // handle error ...
}

// use board, start discovery ...

Sgc_freeBoard(board);

相机发现#

int Sgc_scanPorts(
    SgcBoardHandle * board,
    unsigned int portMask,
    int timeout,
    int speed);

int Sgc_getCameraCount(
    SgcBoardHandle * board);

int Sgc_getCameraByIndex(
    SgcBoardHandle * board,
    unsigned int index,
    SgcCameraHandle ** camera);

int Sgc_getCamera(
    SgcBoardHandle * board,
    unsigned int port,
    SgcCameraHandle ** camera);

信息

在 Framegrabber API 5.9 版本中,函数 Sgc_scanPorts() 以及函数的返回值 Sgc_getCameraCount() 的行为均已更改。

在应用程序能够使用相机之前,必须先发现它们。发现过程会扫描采集卡的物理端口以查找连接。当在多个端口上检测到连接时,下一步是弄清楚连接了多少台相机,以及每台相机正在使用多少个物理连接。物理连接及其属性(例如使用的传输速度)被称为链路 (link)。相机支持一个或多个链路配置。例如,CoaXPress 相机可能支持各种链路配置。例如,具有四个物理端口的相机可以支持使用 CoaXPress 2.0 链路速度子集的一个、两个或四个链路的配置:3.125、5、6.25、10 和 12.5 Gbit/s。因此,该相机总共支持 15 种不同的链路配置。

要启动相机发现程序,该函数 Sgc_scanPorts() 应当被调用。该函数期望来自先前调用的板卡句柄 Sgc_initBoard() 在参数 board,一个用于指定执行发现操作的物理端口的位掩码,在参数 portMask 以及在参数中等待发现至少一台相机的毫秒数 timeout。参数 speed 应当为 LINK_SPEED_NONE 在大多数情况下。它还可以选择性地用于为发现过程提供提示,即预期以何种链路速度发现相机。

在为一台或多台采集卡使用单一进程的应用程序中,端口掩码应始终为 0xf。通过这种方式,将扫描帧抓取器的所有端口以查找相机。如果应用程序针对某个帧抓取器使用了多个进程,请阅读章节 支持从多个进程中进行相机发现.

发现相机后,将根据使用的 Applet 和相机默认配置来协商最终的链路配置。了解此过程的局限性有助于理解向应用程序呈现被发现相机时所处的状态。发现算法不使用相机提供的 GenICam XML。相反,默认的相机配置决定了发现过程可用于该相机的最大链路速度和链路数量。对于某些相机,默认配置并不是相机能使用的最高链路速度或最大链路数量!另一方面,Applet 布局决定了 Applet 的最大相机数量,以及每个相机可用于发现过程的最大链路数量。发现算法试图在找到的相机与 Applet 布局之间实现最佳匹配。请查阅 Applet 文档和相机文档,以找到最符合您打算使用的相机设置和功能的 Applet。

(另请参见章节 Understanding Image Data Flow,其中包含说明小程序布局的图表。对于连接到帧抓取器的每个相机,小程序都需要一个相应的相机算子。相机算子的数量是每个小程序的一个固定 Property。您可能还想查看部分 The Frame Grabber Library fglib5 的章节 Frame Grabber Initialization 末尾的注释,以了解如何为连接到帧抓取器的相机选择小程序。)

如果对 Sgc_scanPorts() 返回 SGC_OK,则在指定时间内至少找到了一台相机,并且小应用程序与相机可以成功匹配。如果返回值是 ERR_SGC_TOPOLOGY_MISMATCH的调用,则表示至少找到了一台相机,但 Applet 与相机无法匹配;虽然与相机的通信仍然可行,但图像数据无法正确从相机传输到 Applet。

在成功调用之后 Sgc_scanPorts()中,函数 Sgc_getCameraCount() 可以调用以请求找到的相机数量,并使用 Sgc_getCameraByIndex() 或 Sgc_getCamera() 以获取相机句柄。这两个函数的工作方式类似,且都需要三个参数。第一个参数是板卡句柄。对于 Sgc_getCameraByIndex(),第二个参数通过一个介于 0 和报告的相机数量之间的数字来标识相机 Sgc_getCameraCount() 减一。因此,该函数可用于方便地迭代所有已发现的相机,但相机被发现的顺序(从而索引)在两次调用之间可能会发生变化 Sgc_scanPorts()。对于 Sgc_getCamera(),第二个参数通过相机连接的物理端口来标识相机。物理端口从 0 开始编号,最高到所用采集卡的物理端口总数减一。如果始终使用相同的物理布线,则可使用此函数来避免前面提到的有关 Sgc_getCameraByIndex()。第三个参数是指向用于存储相机句柄的变量的指针。

以下示例展示了如何查找相机最多 10 秒,然后获取相应的相机 handle:

const unsigned int portMask = 0xf;
const int timeoutInMs = 10000;

int result = Sgc_scanPorts(board, portMask, timeoutInMs, LINK_SPEED_NONE);
if (result != SGC_OK) {
    // handle error ...
}

SgcCameraHandle * camera = nullptr;
result = Sgc_getCameraByIndex(board, index, &camera);
if (result == SGC_OK) {
        // use camera ...
}

上述示例中概述的简单方法仅适用于单相机设置。

对于使用一台以上相机的应用程序,处理发现的推荐方法是注册回调函数(如章节中所述) 为发现事件注册回调函数,然后调用 Sgc_scanPorts() 通过将超时设置为 0 来启动发现过程。在回调处理程序中,应用程序可以在发现新相机时启动独立的采集线程等。

或者,应用程序可以调用 Sgc_scanPorts() 重复此操作,直到找到所有预期的相机。以下示例在继续处理之前等待发现四台相机:

const unsigned int portMask = 0xf;
const int timeoutInSeconds = 10;
const int numberOfCamerasExpected = 4;

int result = SGC_OK;
int numberOfCamerasFound = 0;
auto start = std::chrono::steady_clock::now();
do {
    result = Sgc_scanPorts(board, portMask, 1000, LINK_SPEED_NONE);
    if (result == SGC_OK) {
        numberOfCamerasFound = Sgc_getCameraCount(board);
    }
} while ((numberOfCamerasFound < numberOfCamerasExpected) && (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeoutInSeconds)));
if (numberOfCamerasFound < numberOfCamerasExpected) {
    // handle error ...
}

// use cameras ...

重新启动相机发现#

int Sgc_scanPortsEx(
    SgcBoardHandle * board,
    unsigned int portMask,
    int timeout,
    int speed,
    unsigned int flags);

信息

函数的默认行为 Sgc_scanPortsEx() 已在 Framegrabber API 的 5.9 版本中更改。

这些函数 Sgc_scanPorts() 和 Sgc_scanPortsEx() 在参数中传递 0 调用时 flags 两者都会启动一个后台线程来执行发现程序,然后与该线程进行同步,以检查在调用中指定的时间内是否找到了任何相机。对该函数的任何后续调用将仅执行同步以检查相机。要完全重新启动相机发现程序, Sgc_scanPortsEx() 通过调用 SGC_SP_FLAG_RESET_DISCOVERY_INFOS 在参数 flags.

使用相机 Property#

发现相机后,应用程序即可开始使用相机 Property。这些 Property、相机参数和命令在 XML 文档中定义,该文档可由相机提供以便通过标准化的 Property 接口进行下载,或者由相机制造商通过其他方式提供(例如,某些相机制造商可能会提供一个可下载 XML 文档的网站)。XML 文档的结构在 GenICam 标准中定义。要详细了解 GenICam,请访问欧洲机器视觉协会的 GenICam 网站。要详细了解相机提供的 Property,请参阅相机文档。

GenICam 标准还定义了一种通过 GenApi 控制相机 Property 的方法。章节 Using the GenICam Reference Implementation 将讨论如何将 siso_genicam 库与 GenApi 连接。

本章记录了一组函数,允许直接从 Framegrabber API 使用相机功能,并对 GenApi 进行封装以便更易于使用。虽然所描述的 API 可用于在已知这些功能时获取和设置相机参数以及执行命令,但不支持 XML 的高级功能,这需要应用程序使用 GenApi。

相机连接#

int Sgc_connectCamera(
    SgcCameraHandle * camera);

int Sgc_connectCameraWithExternalXml(
    SgcCameraHandle * cameraHandle,
    const char * file);

int Sgc_disconnectCamera(
    SgcCameraHandle * camera);

在发现过程中,已建立与所发现相机的物理连接。若要使用相机功能,必须建立逻辑连接。该逻辑连接将通过读取 GenICam XML 并准备 Framegrabber SDK 内部使用的 GenApi 环境来建立。若要使用相机提供的 XML 建立逻辑连接,可使用函数 Sgc_connectCamera() 应该被调用。如果应使用从制造商网站下载或通过其他方式获取的 XML 文件,则该函数 Sgc_connectCameraWithExternalXml() 。这两个函数都需要在参数中提供相机句柄 camera开始。函数 Sgc_connectCameraWithExternalXml() 期望在参数中提供 XML 文件的路径 file 此外。

当应用程序使用完相机功能后,可以通过调用来关闭逻辑连接 Sgc_disconnectCamera().

获取和设置相机 Property#

int Sgc_getIntegerValue(
    SgcCameraHandle * camera,
    const char * feature,
    int64_t * value);

int Sgc_getBooleanValue(
    SgcCameraHandle * camera,
    const char * feature,
    unsigned int * value);

int Sgc_getFloatValue(
    SgcCameraHandle * camera,
    const char * feature,
    double * value);

int Sgc_getStringValue(
    SgcCameraHandle * camera,
    const char * feature,
    const char ** value);

int Sgc_getEnumerationValue(
    SgcCameraHandle * camera,
    const char * feature,
    int64_t * value);

int Sgc_getEnumerationValueAsString(
    SgcCameraHandle * camera,
    const char * feature,
    const char ** value);

int Sgc_clearStringCache(
    SgcCameraHandle * camera);
int Sgc_setIntegerValue(
    SgcCameraHandle * camera,
    const char * feature,
    int64_t value);

int Sgc_setBooleanValue(
    SgcCameraHandle * camera,
    const char * feature,
    unsigned int value);

int Sgc_setFloatValue(
    SgcCameraHandle * camera,
    const char * feature,
    double value);

int Sgc_setStringValue(
    SgcCameraHandle * camera,
    const char * feature,
    const char * value);

int Sgc_setEnumerationValue(
    SgcCameraHandle * camera,
    const char * feature,
    const char * value);

若要请求具有数字表示形式的相机参数的值,可以使用函数 Sgc_getIntegerValue(), Sgc_getBooleanValue(), Sgc_getFloatValue() 和 Sgc_getEnumerationValue() 可以使用。这些函数期望在参数中传入相机句柄 camera,参数中的功能名称 feature 以及一个指向相应类型变量的指针,该变量的值将存储在参数中 value.

请求具有字符串表示形式的相机参数值的函数, Sgc_getStringValue() 和 Sgc_getEnumerationValueAsString(),期望一个指向以下类型的变量的指针 char * 在参数 value。在此变量中,将存储一个指向字符串值的指针。

字符串值将被分配并存储在内部缓存中。每当应用程序请求字符串值且无法直接从缓存中释放单个字符串值时,缓存就会增长。但是,可以通过调用来清除缓存 Sgc_clearStringCache()。这将释放并使所有先前分配的字符串值失效。

要设置值,函数 Sgc_setIntegerValue(), Sgc_setBooleanValue(), Sgc_setFloatValue(), Sgc_setStringValue() 和 Sgc_setEnumerationValue() 可以调用。这些函数期望参数中包含相机句柄 camera,参数中的功能名称 feature 以及参数中的值 value.

以下示例展示了如何通过相机 XML 连接相机并设置要传输图像的宽度和高度:

int result = Sgc_connectCamera(camera);
if (result == SGC_OK) {
    result = Sgc_setIntegerValue(camera, "Width", 1920);
}
if (result == SGC_OK) {
    result = Sgc_setIntegerValue(camera, "Height", 1080);
}

执行相机命令#

int Sgc_executeCommand(
    SgcCameraHandle * camera,
    const char* feature);

int Sgc_isCommandDone(
    SgcCameraHandle * camera,
    const char* feature,
    unsigned int * value);
int Sgc_startAcquisition(
    SgcCameraHandle * camera,
    unsigned int start);

int Sgc_stopAcquisition(
    SgcCameraHandle * camera,
    unsigned int stop);

要执行命令,函数 Sgc_executeCommand() 可以被调用。该函数在参数中需要相机句柄 camera 以及参数中的 Feature 名称 feature开始。函数 Sgc_isCommandDone() 可用于检查命令是否已执行完成。它在参数中需要相机句柄 camera,参数中的功能名称 feature 和一个指向该类型变量的指针 unsigned int 在参数 value。如果命令已执行完毕,则变量中将存储值 1;如果命令仍在执行中,则存储值 0。

这些函数 Sgc_startAcquisition() 和 Sgc_stopAcquisition() 是标准 Feature 的快捷方式 AcquisitionStart 和 AcquisitionStop。只有当参数满足时,该函数才会执行 start 或 stop 求值为 true:

int Sgc_startAcquisition(SgcCameraHandle * camera, unsigned int start)
{
    int result = SGC_OK;
    if (start) {
        result = Sgc_executeCommand(camera, "AcquisitionStart");
    }
    return result;
}

相机信息#

int Sgc_getCameraPropertyWithType(
    SgcCameraHandle * camera,
    const char * property,
    void * value,
    unsigned int * type,
    void * additionalValue);

int Sgc_updateCameraInfos(
    SgcCameraHandle * camera);

信息

在 Framegrabber API 的 5.9 版本中,通过函数获取的信息 Sgc_getCameraPropertyWithType() 已扩展,并且函数 Sgc_updateCameraInfos() 已添加。

在发现程序中建立与相机的物理连接后,即可请求获取有关相机和连接的各种信息。

函数 Sgc_getCameraPropertyWithType() 可以被调用以请求关于相机连接的信息。该函数在参数中需要相机句柄 camera,参数中的连接 Property property,一个指向将存储值的变量的指针,一个指向类型的变量的指针 unsigned int 必须对其进行初始化以反映 Property 的类型。参数 additionalValue 对于某些 Property 是必需的,如下文所述:

Property 描述 键入
CAM_PROP_MASTERID 相机的主 IDa uint32_t
CAM_PROP_NROFLINKS 用于图像传输的物理链路数量 uint32_t
CAM_PROP_LINKSPEED 当前链路速度(以 Mbit/s 为单位) uint32_t
CAM_PROP_DISCOVERYSPEED 发现相机时的链路速度(以 Mbit/s 为单位) uint32_t
CAM_PROP_FGPORT 链路连接到的物理端口(additionalValue:链路索引) uint32_t
CAM_PROP_MASTERPORT 主链路连接到的物理端口(与链路 0 的 FGPORT 相同) uint32_t
CAM_PROP_APPLETMASTERID Applet 相机端口的主 IDa uint32_t
CAM_PROP_APPLETOPERATORINDEX Applet 相机算子的索引 uint32_t
CAM_PROP_APPLETOPERATORSIZE Applet 相机算子的链路数量 uint32_t
CAM_PROP_APPLETPORT 链路连接到的逻辑端口(additionalValue:链路索引) uint32_t
CAM_PROP_CONTROLPACKETSIZE 控制数据包的最大大小 uint32_t
CAM_PROP_STREAMPACKETSIZE 流数据包的最大大小 uint32_t
CAM_PROP_ISCONNECTED 0:无物理连接
1:已建立物理连接
uint32_t
CAM_PROP_ISGENICAMCONNECTED 0:无逻辑连接
1:已建立逻辑连接
uint32_t
CAM_PROP_ISTRANSCEIVERLOCKED 0:传输错误过多
1:传输稳定
uint32_t
CAM_PROP_XML_DATA 以以零结尾的字符串形式表示的相机 XML 数据(additionalValue:缓冲区大小) char *
CAM_PROP_VENDOR_NAME 以零结尾的字符串形式表示的相机厂商名称(additionalValue:缓冲区大小) char *
CAM_PROP_MODEL_NAME 以零结尾的字符串形式表示的相机型号名称(additionalValue:缓冲区大小) char *
CAM_PROP_FAMILY_NAME 以零结尾的字符串形式表示的相机系列名称(additionalValue:缓冲区大小) char *
CAM_PROP_VERSION 以零结尾的字符串形式表示的相机版本(additionalValue:缓冲区大小) char *
CAM_PROP_FIRMWARE_VERSION 以零结尾的字符串形式表示的相机固件版本(additionalValue:缓冲区大小) char *
CAM_PROP_MANUFACTURER_INFO 以零结尾的字符串形式表示的相机制造商信息(additionalValue:缓冲区大小) char *
CAM_PROP_SERIAL_NUMBER 以零结尾的字符串形式表示的相机序列号(additionalValue:缓冲区大小) char *
CAM_PROP_USER_ID 以零结尾的字符串形式表示的相机 User ID(additionalValue:缓冲区大小) char *

上面的列表并不完整,仅包含应用程序所需信息的 Property。有关该 API 的更多用例,请参阅 Framegrabber API 参考手册。

如果某个 Property 使用了参数 additionalValue 对于链路索引,如果未指定,默认链路索引为 0 nullptr 传递给 additionalValue 在调用中。Link 索引 0 是相机的主 Link。

应使用以下值来请求相应类型的值:

Property 类型 C/C++ 类型
SGC_PROPERTY_TYPE_UINT uint32_t
SGC_PROPERTY_TYPE_DOUBLE double
SGC_PROPERTY_TYPE_STRING char *

以下示例展示了如何获取相机的连接速度和 Link 数量:

unsigned int numberOfLinks = 0;
unsigned int linkSpeed = 0;
int type = SGC_PROPERTY_TYPE_UINT;
int result =
    Sgc_getCameraPropertyWithType(camera, CAM_PROP_NROFLINKS,
                                  &linkSpeed, &type, nullptr);
if (result == SGC_OK) {
    result =
        Sgc_getCameraPropertyWithType(camera, CAM_PROP_LINKSPEED,
                                      &numberOfLinks, &type, nullptr);
}
if (result == SGC_OK) {
    std::cout << "Link configuration: " << numberOfLinks
              << " x " << (1e-3 * linkSpeed)
              << " Gbit/s" << std::endl;
}

要请求类型的 Property SGC_PROPERTY_TYPE_STRING,对以下内容的两次调用 Sgc_getCameraPropertyWithType() 是必需的。将 nullptr 传递给参数 value 和一个指向该类型变量的指针 unsigned int 传递给参数 additionalValue,包括终止符在内的字符串所需的缓冲区大小 \0 可以请求字符并将其存储在变量中。在第二次调用中,分配的缓冲区的指针通过参数传入 value 以及参数中指向大小变量的指针 additionalValue。如果分配的缓冲区空间大于实际需要,在返回时,缓冲区大小的变量将包含数据的长度,包括终止符 \0 字符。

以下示例展示了如何使用以下方式请求设备型号名称 Sgc_getCameraPropertyWithType():(该示例需要 C++ 头文件 memory 对于 std::unique_ptr 类型。)

// get size of model name
unsigned int size = 0;
unsigned int propertyType = SGC_PROPERTY_TYPE_STRING;
result =
    Sgc_getCameraPropertyWithType(camera, CAM_PROP_MODEL_NAME,
                                  nullptr, &propertyType, &size);
if (result == SGC_OK && size > 0) {
    // create buffer and request camera model name
    std::unique_ptr<char[]> modelName(new char[size]);
    result =
        Sgc_getCameraPropertyWithType(camera, CAM_PROP_MODEL_NAME,
                                      modelName.get(), &propertyType, &size);
    if (result == SGC_OK) {
        std::cout << "Camera model name: " << modelName.get() << std::endl;
    }
}

当应用程序使用会更改通过以下方式获取的信息的相机功能时 Sgc_getCameraPropertyWithType()例如通过设置 User ID 字符串或更新相机固件版本,函数 Sgc_updateCameraInfos() 应调用以请求更新 Framegrabber API 中的数据。

了解图像数据流#

为了理解图像数据如何从相机经由采集卡中的 Applet 流入应用程序 Memory,我们首先来看一个简单的设置,其中两台相机以直接的方式连接到 Applet 中的相应相机算子:

Data Flow Straight

示例中的 applet 可以是 Acq_DualCXP12Area或类似 Applet。来自相机索引为 0、Master ID 为 1 的相机的图像数据,流经采集卡的物理端口 0 和 1,到达算子索引为 0、Master ID 为 1 的相机算子,之后在特定于该 Applet 的图像管线中进行处理,并通过索引为 0 的 DMA 算子发送到应用程序 Memory。(由于 Master ID 基于相机或算子连接的端口,因此第二个相机和算子的 Master ID 为 3,而不是 2。其索引为 1。)

从应用程序的角度来看,重要的信息是可以在哪个 DMA 通道上期望获取相机的图像数据。对于示例中的 applet 布局,相机的只有一个 Property CAM_PROP_APPLETOPERATORINDEX,对于了解正确的 DMA 通道是必不可少的。Framegrabber SDK 安装中包含的所有 Applet 均根据此原则设计。对于使用 VisualApplets 设计的 Applet,强烈建议采取类似方法并记录布局。

让我们看一个更复杂的示例,其中相机是通过交叉线缆连接的:

Data Flow Crossed

为了让图像传输正常工作,发现过程必须按照上方图表所示的方式,将采集卡物理端口连接到相机算子的逻辑端口。(观察图表,还有第二种可能性,即主 ID 为 2 的相机连接到了主 ID 为 0 的相机算子。如果在首次运行发现过程时只连接了一台相机,而第二台相机是在后期添加的,则可能会出现这种情况。)

不过,同一个相机 Property, CAM_PROP_APPLETOPERATORINDEX,足以了解正确的 DMA 通道。

为发现事件注册回调函数#

struct SgcEventInfo {
  uint16_t eventID;
  union SgcEventData data;
}

typedef int (* Sgc_BoardEventCallbackFunc_t)(
    SgcBoardHandle * board,
    const struct SgcEventInfo * info,
    void * data);

int Sgc_registerBoardEventCallback(
    SgcBoardHandle * board,
    Sgc_BoardEventCallbackFunc_t func,
    void * data);
int Sgc_scanPortsEx(
    SgcBoardHandle * board,
    unsigned int portMask,
    int timeout,
    int speed,
    unsigned int flags);

信息

函数 Sgc_registerBoardEventCallback() 在 Framegrabber API 的 5.9 版本中添加。

使用 Sgc_registerBoardEventCallback(),一个类型为 SgcEventCallback_t 可以注册以便在库生成事件时进行回调。该函数将针对给定的板卡句柄进行注册,并在被调用时传入三个参数。回调函数的第一个参数是板卡句柄。第二个参数是指向 struct SgcEventInfo 用于存储有关该事件的信息。最后一个参数是随对以下内容的调用一起提供的指针: Sgc_registerBoardEventCallback() 且可用作指向上下文结构或类的指针,例如 this 实现图像处理的类实例的指针。

每个板卡句柄只能注册一个回调函数。

相机的发现信息仅在调用以下函数时更新: Sgc_scanPorts() 或 Sgc_scanPortsEx() 被调用。若只需更新信息, Sgc_scanPortsEx() 可以使用以下方法从回调函数中调用: SGC_SP_FLAG_UPDATE_DISCOVERY_INFOS_ONLY 在参数 flags.

在不再需要回调函数之后,可以通过调用将其注销 Sgc_registerBoardEventCallback(),传递 nullptr 传递给参数 func 和 data.

即: struct SgcEventInfo 实例在其成员中包含该事件的标识: eventID 以及联合体成员中的附加数据 data。库生成的事件使用以下事件标识符:

Event 描述
SGC_EVENT_DISCOVERY_STATE_CAMERA_ADDED 发现了一台新相机
SGC_EVENT_DISCOVERY_STATE_CAMERA_LOST 与相机的连接已断开
SGC_EVENT_DISCOVERY_STATE_CAMERA_PROPERTY_CHANGE 相机的连接 Property 已更改

以下示例展示了如何使用一个简单的结构体注册回调函数,该结构体可保存对应用程序有用的信息:

struct DiscoveryUserCallbackData
{
    // ...
};

int DiscoveryUserCallback(SgcBoardHandle * board,
                          const struct SgcEventInfo * info, void * data)
{
    auto context = reinterpret_cast<DiscoveryUserCallbackData *>(data);

    // update discovery infos
    Sgc_scanPortsEx(board, 0xf, 0, LINK_SPEED_NONE,
                    SGC_SP_FLAG_UPDATE_DISCOVERY_INFOS_ONLY);

    // process event ...

    return 0;
}

void SetupDiscoveryUserCallback(SgcBoardHandle * board,
                                DiscoveryUserCallbackData * context)
{
    // register callback function
    int result =
        Sgc_registerBoardEventCallback(board, &DiscoveryUserCallback, context);
    if (result != SGC_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

上下文字结构的分配和管理未在示例中显示。只要回调函数保持注册状态,该指针就必须有效。一种解决方案是将所有内容保存在一个 C++ 类中。为了在 C++ 类上下文中使用回调函数,可以使用静态函数来注册回调处理器,并且 this 指针应用作上下文数据指针,该指针可以转换回类指针并作相应使用。

发现状态更改信息#

struct SgcEventData_DiscoveryStateChanged {
    uint32_t id;
    uint32_t properties_int_count;
    char ** properties_int_key;
    uint32_t * properties_int_value;
    uint32_t properties_string_count;
    char ** properties_string_key;
    char ** properties_string_value;
};

信息

发现状态更改事件是在 Framegrabber API 的 5.9 版本中添加的。

对于板卡事件, struct SgcEventData_DiscoveryStateChanged 用于提供有关受事件影响的相机连接的信息。该结构在其成员中包含相机 master id: id 以及两个键值映射,一个用于整数值,另一个用于字符串值。这些键与通过以下方式获取的属性的键相同: Sgc_getCameraPropertyWithType(),但根据事件和相机接口技术,并非所有 Property 都会出现在事件信息中。应用程序可以期望映射中存在与生成事件的更改相关的 Property。除此之外,应用程序不应假设 Property 的存在或映射中 Property 的顺序。

结构体中的所有数据仅在事件回调函数中有效。如果需要保留信息,必须复制相关部分。

以下示例展示了如何在以下对象中提取链路速度和链路数量: SGC_EVENT_DISCOVERY_STATE_CAMERA_PROPERTY_CHANGE 事件:

int DiscoveryUserCallback(SgcBoardHandle * board,
                          const struct SgcEventInfo * info, void * data)
{
    auto context = reinterpret_cast<DiscoveryUserCallbackData *>(data);

    // update discovery infos
    Sgc_scanPortsEx(board, 0xf, 0, LINK_SPEED_NONE,
                    SGC_SP_FLAG_UPDATE_DISCOVERY_INFOS_ONLY);

    // process event ...
    if (info->eventID == SGC_EVENT_DISCOVERY_STATE_CAMERA_PROPERTY_CHANGE) {
        int newNumOfLinks = 0;
        int newLinkSpeed = LINK_SPEED_NONE;
        for (int i = 0; i < info->data.discovery_state.properties_int_count; ++i) {
            if (strcmp(info->data.discovery_state.properties_int_keys[i],
                       CAM_PROP_NROFLINKS) == 0) {
                newNumOfLinks = info->data.discovery_state.properties_int_values[i];
            }
            if (strcmp(info->data.discovery_state.properties_int_keys[i],
                       CAM_PROP_LINKSPEED) == 0) {
                newLinkSpeed = info->data.discovery_state.properties_int_values[i];
            }
        }

        // ...
    }

    return 0;
}

使用 GenICam 参考实现#

使用 GenICam 参考实现是使用 Framegrabber API 提供的 GenApi 包装器(如第 使用相机 Property章所述)的另一种选择。如果应用程序使用 GenICam 参考实现,则 Sgc_connectCamera() 或 Sgc_connectCameraWithExternalXml() 严禁调用。相反,应用程序应包含 GenApi 头文件 GenICam.h,链接到 GenICam 库 GCBase 和 GenApi 并初始化相应的 GenApi 上下文。

#include <GenICam.h>

要使用随 Framegrabber SDK 安装附带的 GenICam 参考实现,应执行以下步骤:

  • 如果应用程序在 Windows 上使用 64 位 Framegrabber SDK:
  • 添加 %BASLER_FG_SDK_DIR%\bin 到系统环境变量 PATH
  • 添加 %BASLER_FG_SDK_DIR%\include 到项目包含目录
  • 添加 %BASLER_FG_SDK_DIR%\library\visualc 到项目库目录
  • 添加库 GCBase_MD_VC141_v3_1_Basler_pylon_v3.lib 和 GenApi_MD_VC141_v3_1_Basler_pylon_v3.lib 到您的项目链接库
  • 如果应用程序在 Linux 上使用 64 位 Framegrabber SDK:
  • 添加 $BASLER_FG_SDK_DIR\lib64 到系统环境变量 LD_LIBRARY_PATH 以及项目库目录
  • 添加 $BASLER_FG_SDK_DIR\include 到项目包含目录
  • 添加库 libGCBase_gcc_v3_1_Basler_pylon_v3.so 和 libGenApi_gcc_v3_1_Basler_pylon_v3.so 到您的项目链接库
  • 添加 #define GENICAM_NO_AUTO_IMPLIB 在……之前 #include <GenICam.h>

如果您使用 CMake 生成项目文件,SisoGenicam 软件包还将查找随 Framegrabber SDK 安装附带的 GenICam 库,并将其包含在变量中 ${SisoGenicam_LIBRARIES}。GenICam 包含路径存储在变量中 ${GENICAM_INCLUDE_DIR} 应将其添加至 INCLUDE_DIRECTORIES.

创建 IPort 包装器#

int Sgc_memoryReadFromCamera(
    SgcCameraHandle * camera,
    void * buffer,
    uint64_t address,
    size_t length);

int Sgc_memoryWriteToCamera(
    SgcCameraHandle * camera,
    const void * buffer,
    uint64_t address,
    size_t length);

要使用 GenICam 参考实现, GENAPI_NAMESPACE::IPort 需要一个包装器来转换 Framegrabber API 和 GenApi 之间的相机寄存器访问。函数 Sgc_memoryReadFromCamera() 和 Sgc_memoryWriteToCamera() 可用于创建简单直接的包装类:

class DevicePort: virtual public GENAPI_NAMESPACE::IPort
{
    SgcCameraHandle * camera;

public:
    DevicePort(SgcCameraHandle * camera)
        : camera(camera)
    {}

    GENAPI_NAMESPACE::EAccessMode GetAccessMode() const override
    {
        return GENAPI_NAMESPACE::RW;
    }

    void Read(void * buffer, int64_t address, int64_t length) override
    {
        int result = Sgc_memoryReadFromCamera(camera, buffer, address, length);
        if (result != SGC_OK) {
            // handle error ...
        }
    }

    void Write(const void * buffer, int64_t address, int64_t length) override
    {
        int result = Sgc_memoryWriteToCamera(camera, buffer, address, length);
        if (result != SGC_OK) {
            // handle error ...
        }
    }
};

初始化并连接节点映射#

int Sgc_loadCameraXml(
    SgcCameraHandle * camera);

将 GenICam 参考实现连接到 Framegrabber API 的最后步骤包括:获取相机的 GenICam XML 文档、创建节点映射并将其连接到 XML。

要从相机下载并提取 XML 文档,可以使用函数 Sgc_loadCameraXml() 和 Sgc_getCameraPropertyWithType() 可以被使用。首先,函数 Sgc_loadCameraXml() 应调用以从相机下载 XML 文档。然后该函数 Sgc_getCameraPropertyWithType() 可以通过传递 CAM_PROP_XML_DATA 传递给参数 property 和 nullptr 传递给参数 value 请求提取 XML 文档所需的缓冲区大小。在分配了所需大小的缓冲区之后, Sgc_getCameraPropertyWithType() 可以再次调用,并在参数中传入指向缓冲区的指针 value 来提取 XML 文档。

以下示例展示了如何从相机下载并提取 XML 文档、初始化并连接节点映射以及设置要传输的图像宽度和高度:(该示例需要 C++ 头文件 memory 对于 std::unique_ptr 类型和 stdexcept 对于 std::runtime_error 和 std::exception 类型。)

try {
    // download camera XML
    int result = Sgc_loadCameraXml(camera);
    if (result != SGC_OK)
        throw std::runtime_error("Failed to download camera XML");

    // get size of XML
    unsigned int size = 0;
    unsigned int propertyType = SGC_PROPERTY_TYPE_STRING;
    result =
        Sgc_getCameraPropertyWithType(camera, CAM_PROP_XML_DATA,
                                      nullptr, &propertyType, &size);
    if (result != SGC_OK || size == 0)
        throw std::runtime_error("Failed to determine size of camera XML");

    // create buffer and extract camera XML
    std::unique_ptr<char[]> xml(new char[size]);
    result =
        Sgc_getCameraPropertyWithType(camera, CAM_PROP_XML_DATA,
                                      xml.get(), &propertyType, &size);
    if (result != SGC_OK)
        throw std::runtime_error("Failed to extract camera XML");

    // create device port and node map
    DevicePort port(camera);
    GENAPI_NAMESPACE::CNodeMapRef nodeMap;

    // initialize and connect node map
    nodeMap._LoadXMLFromString(xml.get());
    if (!nodeMap._Connect(&port, "Device"))
        throw std::runtime_error("Failed to connect node map to device port");

    // use node map to access camera features ...

    // set width
    GENAPI_NAMESPACE::CIntegerPtr width = nodeMap._GetNode("Width");
    if (width != nullptr) {
        width->SetValue(1920);
    }

    // set height
    GENAPI_NAMESPACE::CIntegerPtr height = nodeMap._GetNode("Height");
    if (height != nullptr) {
        height->SetValue(1080);
    }

} catch (GENICAM_NAMESPACE::GenericException & x) {
    // handle GenICam exceptions ...
} catch (std::exception & x) {
    // handle C++ standard exceptions ...
}

更改相机连接#

int Sgc_setCameraPropertyWithType(
    SgcCameraHandle * camera,
    const char * property,
    const void * value,
    unsigned int type,
    void * additionalValue);

要更改链路速度或相机使用的链路数量,可使用 Sgc_setCameraPropertyWithType() 可以被调用。它的工作方式类似于函数 Sgc_getCameraPropertyWithType() 章中描述的函数 相机信息.

有两个可以更改的 Property:

Property 描述 键入
CAM_PROP_LINKSPEED 当前链路速度(以 Mbit/s 为单位) uint32_t
CAM_PROP_NROFLINKS 用于图像传输的物理链路数量 uint32_t

支持从多个进程中进行相机发现#

int Sgc_initBoardEx(
    Fg_Struct * fg,
    unsigned int flags,
    SgcBoardHandle ** board,
    unsigned int portMask,
    unsigned int mode);
int Sgc_scanPortsEx(
    SgcBoardHandle * board,
    unsigned int portMask,
    int timeout,
    int speed,
    unsigned int flags);

信息

Framegrabber API 的 5.9 版本中增加了对多进程相机发现的支持。

如果应用程序使用多个进程来访问单个采集卡上的相机,则 Framegrabber API 仅支持每个物理端口对应一个进程。虽然单个进程可以访问多个物理端口,但绝不能通过 Framegrabber API 从多个进程访问任何单个物理端口。

在多进程应用程序中,可以通过将对以下内容的调用替换为 Sgc_initBoard() 和 Sgc_scanPorts() 并分别带有 Sgc_initBoardEx() 和 Sgc_scanPortsEx() 并通过使用端口掩码。此外, SGC_SP_FLAG_SKIP_AUTO_PORTADJUSTMENT 应在对以下内容的调用中使用 Sgc_scanPortsEx() 在参数 flags 以禁止重新映射物理端口。

端口掩码(port mask)每个物理端口使用一个位,当该位为 1 时允许访问端口,当该位为 0 时禁用访问。位 0 对应物理端口 0,依此类推。在默认端口掩码中 0xf 所有四个端口的位均为 1,因此允许访问所有四个端口。

最容易理解的场景是单个采集卡连接四台相机的设置,每台相机连接到一个单独的物理端口。第一个进程可以使用端口掩码 0x1,第二个 0x2,第三个 0x4 第四个 0x8。每个进程将负责单个物理端口上的初始化和发现。

当我们考虑连接了多条链路的相机时,情况会变得更复杂,因为相机必须始终连接到正确的物理端口。例如,如果第一个进程使用端口掩码 0x3 第二个进程使用端口掩码 0xc,则在任何情况下,第一台相机的电缆都必须连接到端口 0 和 1,第二台相机的电缆必须连接到端口 2 和 3。如果在任何时候相机的连接方式不同,发现过程将会失败。


  1. Applet 中的相机和相机算子均可通过其 master id 进行识别。有效的最小 master id 为 1。相机 master id 是根据采集卡板卡索引以及相机主端口连接的物理端口号生成的值。相机算子 master id 是根据算子主端口连接到的逻辑端口生成的值。 ↩↩