跳至內容

fglib5#

fglib5 程式庫可用於管理電腦中擷取卡的初始化與控制。

若要使用此函式庫,應將標頭檔 basler_fg.h 新增至原始程式碼中。

#include <basler_fg.h>

此外, fglib5.lib 應新增至您的 Microsoft Visual Studio 專案中,或將 libfglib5.so 到您的 Linux 專案中。如果您使用 CMake,套件名稱為 FgLib5。CMake 會將 include 目錄儲存在變數 ${FgLib5_INCLUDE_DIR} 以及函式庫在 ${FgLib5_LIBRARIES}中。如需有關專案及如何使用 CMake 的更多詳細資訊,請參閱 先決條件 。

擷取卡程式庫中的錯誤處理#

const char * Fg_getErrorDescription(
    Fg_Struct * fg,
    int result);

int Fg_getLastErrorNumber(
    Fg_Struct * fg);

API 的大多數函式皆會傳回一個 int 結果代碼。如果函式呼叫成功執行,則傳回值將會是 FG_OK中。在大多數情況下,負值表示發生錯誤狀況。Error Codes 定義於標頭檔 basler_fg.h.

函數 Fg_getErrorDescription() 可用於取得特定結果代碼的字串表示。此函數的第一個引數(擷取卡的 handle)不會被使用,僅為了 API 向後相容性而包含。您應該一律傳遞 nullptr.

函數 Fg_getLastErrorNumber() 將一律傳回在相同執行緒內容中呼叫的最後一個函數的結果代碼。當函數本身未傳回結果代碼時,這最為有用。該函數接受一個擷取卡 handle 作為引數,該 handle 可以是 nullptr 以防最後呼叫的函數未收到擷取卡 handle。若要請求需要擷取卡 handle 的函數的結果代碼,必須將相同的 handle 傳遞至 Fg_getLastErrorNumber().

資訊

Error codes 儲存在執行緒區域儲存區中。這意味著透過從某個執行緒呼叫 Fg_getLastErrorNumber() ,應用程式無法請求發生在其他執行緒中的 error codes。

以下代碼將列印出發生在非特定畫面擷取卡代碼實例中的最後一個錯誤及其描述:

int result = Fg_getLastErrorNumber(nullptr);
if (result != FG_OK) {
    const char * description = Fg_getErrorDescription(nullptr, result);

    std::cout << "Error " << result
              << ": " << description
              << std::endl;
}

本說明文件其餘部分的程式碼範例將不包含 error handling,因為這取決於應用程式的特定需求。不過,在可行範圍內,仍會檢查傳回代碼以確保函式成功執行。

擷取卡程式庫初始化#

int Fg_InitLibraries(
    const char *);

void Fg_FreeLibraries();

在使用 fglib5 庫之前,必須透過呼叫下列函數進行初始化: Fg_InitLibraries()。該函數接受一個僅用於內部目的的引數,應設為 nullptr。

當應用程式不再使用該庫時,請呼叫函數 Fg_FreeLibraries() 以釋放先前初始化所配置的任何資源。

int result = Fg_InitLibraries(nullptr);
if (result != FG_OK) {
    // handle error ...
}

// use frame grabber ...

Fg_FreeLibraries();

系統資訊#

在初始化畫面擷取卡之前,可以查詢有關所使用的 Framegrabber API 版本資訊、電腦中安裝的畫面擷取卡數量與類型以及其他資訊。

Framegrabber API 版本#

const char * Fg_getSWVersion();

可以使用以下方式查詢 Framegrabber API 版本的字串表示形式 Fg_getSWVersion().

const char * rtVersion = Fg_getSWVersion();
std::cout << "Runtime SDK version: " << rtVersion
          << std::endl;

一般系統資訊#

int Fg_getIntSystemInformationGlobal(
    Fg_Info_Selector information,
    FgProperty propertyId,
    int * value);

資訊

函數 Fg_getIntSystemInformationGlobal() 是在 Framegrabber API 5.9 版本中新增的。請參閱章節 Plain C 中的系統資訊 以了解舊的介面。

可以透過函數請求以下資訊 Fg_getIntSystemInformationGlobal() 使用屬性 PROP_ID_VALUE:

資訊 描述 Type
INFO_NR_OF_BOARDS 電腦中找到的基板數量 int32_t
INFO_MAX_NR_OF_BOARDS 支援的最大基板數量 int32_t
INFO_SERVICE_ISRUNNING 0:服務未執行;1:服務正在執行 int32_t

例如,可以查詢電腦中安裝的畫面擷取卡數量,如下所示:

int numBoards = 0;
int result = Fg_getIntSystemInformationGlobal(INFO_NR_OF_BOARDS, PROP_ID_VALUE, &numBoards);
if (result == FG_OK) {
    std::cout << "Number of boards: " << numBoards
              << std::endl;
}

特定應用的系統資訊#

int Fg_getIntSystemInformationForBoardIndex(
    unsigned int board,
    Fg_Info_Selector information,
     FgProperty propertyId,
     int * value);

int Fg_getInt64SystemInformationForBoardIndex(
    unsigned int board,
    Fg_Info_Selector information,
    FgProperty propertyId,
    int64_t * value);

int Fg_getStringSystemInformationForBoardIndex(
    unsigned int board,
    Fg_Info_Selector information,
    FgProperty propertyId,
    std::string & value,
    const std::string & arg = "");

int Fg_getIntSystemInformationForFgHandle(
    Fg_Struct * fg,
    Fg_Info_Selector information,
    FgProperty propertyId,
    int * value);

int Fg_getInt64SystemInformationForFgHandle(
    Fg_Struct * fg,
    Fg_Info_Selector information,
    FgProperty propertyId,
    int64_t * value);

int Fg_getStringSystemInformationForFgHandle(
    Fg_Struct * fg,
    Fg_Info_Selector information,
    FgProperty propertyId,
    std::string & value,
    const std::string & arg = "");

資訊

本章記錄的函數是在 Framegrabber API 5.9 版本中新增的。請參閱舊介面的 System Information in Plain C 章節。

對於任何指定的畫面擷取卡,大多數其他資訊都可以透過以下函數請求 Fg_getIntSystemInformationForBoardIndex(), Fg_getInt64SystemInformationForBoardIndex() 進行明確的生命週期管理即呼叫 Fg_getStringSystemInformationForBoardIndex() 透過使用基板索引或畫面擷取卡代碼以及屬性 PROP_ID_VALUE:

資訊 描述 Type
INFO_TIMESTAMP_FREQUENCY 用於影像 Timestamp 的 Timestamp 頻率 int64_t
INFO_BOARDNAME 如 microDiagnostics 中所示的機板名稱 string
INFO_BOARDTYPE sisoboards.h 中定義的機板類型 int32_t
INFO_BOARDSERIALNO 機板序號 int32_t
INFO_FIRMWAREVERSION 機板的韌體版本 string
INFO_HARDWAREVERSION 機板的硬體版本 string
INFO_CAMERA_INTERFACE 機板提供的相機介面('CameraLink' 或 'CXP') string
INFO_DRIVERVERSION 用於機板的驅動程式版本 string
INFO_DRIVERARCH 用於機板的驅動程式架構 string
INFO_DRIVERFULLVERSION 用於機板的完整驅動程式版本(包含架構) string
INFO_DRIVERGROUPAFFINITY 驅動程式 IRQ 群組親和性(請參閱 Support for Non-Uniform Memory Access) int32_t
INFO_DRIVERAFFINITYMASK 驅動程式 IRQ 處理器親和性遮罩(請參閱 Support for Non-Uniform Memory Access) int64_t
INFO_LICENSE_GROUP_CODE 擷取卡授權群組代碼(必須是小程序授權群組代碼的超集a) int32_t
INFO_LICENSE_USER_CODE 擷取卡授權使用者代碼(必須與小程序授權使用者代碼相符) int32_t
INFO_IS_POCL 0:主機板不支援 PoCL;1:主機板支援 PoCL int32_t
INFO_NR_OF_CXP_PORTS 具備 'CXP' 介面之主機板上的連接埠數量 int32_t
INFO_NR_OF_CL_PORTS 具備 'CameraLink' 介面之主機板上的連接埠數量 int32_t
INFO_NR_OF_PORTS 具備 'CameraLinkHS' 介面之主機板上的連接埠數量 int32_t
INFO_NR_OF_GIGE_PORTS 具備 'GigE' 介面之主機板上的連接埠數量 int32_t
INFO_APPLET_DESIGN_ID Applet 的 HAP ID(必須在引數 arg 中傳入 Applet 路徑) string
INFO_APPLET_BITSTREAM_ID Applet 的位元串流 ID(必須在引數 arg 中傳入 Applet 路徑) string
INFO_STATUS_PCI_LINK_WIDTH 擷取卡所使用的 PCIe 通道數 int32_t
INFO_STATUS_PCI_EXPECTED_LINK_WIDTH 擷取卡所支援的 PCIe 通道數 int32_t
INFO_STATUS_PCI_LINK_SPEED 擷取卡所使用的 PCIe 世代 int32_t
INFO_STATUS_PCI_EXPECTED_LINK_WIDTH 擷取卡所支援的 PCIe 世代 int32_t
INFO_STATUS_PCI_PAYLOAD_SIZE 擷取卡所使用的 PCIe 酬載大小 int32_t

上方清單並不完整,僅包含對應用程式實用的資訊屬性。如需更多 API 使用案例,請參閱 Framegrabber API reference。

例如,以下程式碼會請求主機板類型與名稱。

const int boardIndex = 0;

int boardType = 0;
int result =
    Fg_getIntSystemInformationForBoardIndex(boardIndex, INFO_BOARDTYPE,
                                            PROP_ID_VALUE, &boardType);

std::string boardName;
if (result == FG_OK) {
    result =
        Fg_getStringSystemInformationForBoardIndex(boardIndex, INFO_BOARDNAME,
                                                   PROP_ID_VALUE, boardName);
}

if (result == FG_OK) {
    std::cout << "Board #" << boardIndex
              << " is a " << boardName
              << " (type " << std::hex << boardType
              << std::dec << ")" << std::endl;
}

一旦在初始化後取得 frame grabber handle,即可使用相關函式 Fg_getIntSystemInformationForFgHandle(), Fg_getInt64SystemInformationForFgHandle() 進行明確的生命週期管理即呼叫 Fg_getStringSystemInformationForFgHandle() 來根據上述清單取得關於該應用的特定資訊。(請參閱章節 擷取卡初始化。)可以使用 frame grabber handle 和 Property PROP_ID_VALUE:

資訊 描述 Type
INFO_APPLET_CAPABILITY_TAGS 描述 applet 功能的鍵值對清單 string
INFO_OWN_BOARDINDEX frame grabber 的相應張卡索引 (Board Index) int32_t
INFO_FPGA_BITSTREAM_ID FPGA 中作用中 applet 的 bit stream ID string
INFO_APPLET_FULL_PATH Applet 完整路徑 string
INFO_APPLET_FILE_NAME Applet 檔案名稱 string
INFO_APPLET_TYPE 0:HAP 檔案;1:DLL/SO 檔案(請參閱 Applets) int32_t

上述清單並不完整,僅包含對應用程式有用的資訊Property。有關該 API 的更多使用案例,請參閱 Framegrabber API 參考手冊。

使用應用的 Index#

Frame grabber 是透過相應張卡索引來選取的。如果您知道系統中現有的張卡數量,在預設情況下,第一張卡的索引為 0,第二張卡的索引為 1,以此類推。

Framegrabber API 允許使用者透過可從 microDiagnostics 產生的設定檔,為每張卡指定唯一的索引。在這種情況下,使用者應該了解其指定的對應張卡索引。

Applet#

若要操作 frame grabber,需要使用 applet。Applet 是包含數個項目的集合,通常包括:

  • 針對 frame grabber 上 FPGA 的設計,該設計實現了從接收相機裝置的影像到將處理後的影像傳送至電腦 Memory 中的影像處理功能
  • 設計的軟體描述,其中包含設計中使用的 VisualApplets operators 以及將參數對應至 FPGA 暫存器的資訊
  • 一組軟體介面函式庫,用作類別實例的產生器,以處理 operator 參數值與 FPGA 暫存器內容之間的轉換
  • 頂層函式庫 VAS,負責處理所有 operator 類別的實例化、初始化以及與 applet 的介面連接

如果 Applet 是使用 VisualApplets 設計的,則會以 HAP 檔案的形式提供;如果是由 Framegrabber SDK 預先安裝的,則會以封裝的 applet 函式庫檔案形式提供。HAP 檔案通常儲存在 Hardware Applets 目錄(位於 Framegrabber SDK 安裝資料夾中)的特定相應張卡子目錄內,而封裝的 applet 函式庫檔案則安裝在 dll 目錄的特定相應張卡子目錄中。在本文檔中,這些子目錄被稱為對應類型 applet 的標準位置。

每個 applet 都提供了一組參數來設定所提供的功能。請參閱特定 applet 的說明文件,以深入了解該 applet 所提供的功能與參數。

選擇正確的 Applet#

可透過 FramegrabgetDescription SDK 安裝的 applet 通常以封裝的程式庫檔案形式提供。這些檔案的命名慣例遵循以下規則:

  • 檔案名稱通常開頭為 Acq_。(這些 applet 稱為 Advanced Acquisition Applet,上一代則稱為 Standard Applet 且採用不同的命名慣例。)
  • 接下來,applet 支援的相機數量會以以下形式表示: Single, Dual 或 Quad.
  • 然後會指出支援的相機介面。例如, CXP12 表示符合 CoaXPress Standard Version 2.0 規範、最高可達 12 Gbit/s 的 CoaXPress 相機介面。
  • 在較舊的擷取卡平台上,支援的最大 Camera Link 連線數會以以下形式表示: x1, x2 或 x4。(這項命名慣例已取消,因為 x4 包含對 x2 進行明確的生命週期管理即呼叫 x1, x2 支援 x1 ,且支援的最大連線數通常可以從支援的相機數量以及擷取卡提供的實體連接埠數量清楚得知。)
  • 然後會指出支援的感測器類型。這會是 Area 或 Line。對於同時支援面型和線型感測器的 applet,則會省略此項。
  • 名稱的最後一部分是 applet 支援的資料格式,例如 Gray8, Bayer16, RGB24。對於支援多種資料格式的 applet,則會省略此項。

例如,applet Acq_SingleCXP12Area 是一個 Advanced Acquisition Applet,支援一個高達 12 Gbit/s 的 CXP 相機與面型感測器。此 applet 支援多種資料格式。若要了解支援的資料格式與感測器尺寸,必須參閱 applet 說明文件。

例如,如果應用程式需要兩個 12 Gbit/s 的 CXP 相機、一個具有 10 位元灰階輸出的線型感測器,請尋找名為 Acq_DualCXP12Line, Acq_DualCXP12LineGray10 或 Acq_DualCXP12LineGray16 的 applet 並閱讀隨附的說明文件。如果相機僅使用單一實體連接線連線,也可以考慮 Quad 類型的 applet。

啟動行為#

當 Framegrabber SDK 開始初始化 fglib5 程式庫時,如 Frame Grabber Initialization 所述,甚至在載入 applet 之前就需要存取 frame grabber。例如,這項操作是允許根據機板的序號進行排序,或是甚至查看所有可供載入的 applet。

用於此初始化的 applet 取決於您所使用的 frame grabber。您可以利用以下章節中的資訊來設定您的 frame grabber,使其以預先定義的 applet 啟動。當使用與預先定義的 applet 不同的 applet 時 呼叫 Fg_Init 會導致較長的啟動時間。

在啟動時,Framegrabber SDK 預設會載入最後使用的 applet Fg_Init.

您可以透過設定系統環境變數來停用此行為 FGSDK_LOAD_LAST_APPLET_ON_INIT=Off.

microEnable 5 marathon Frame Grabbers 的啟動行為#

在 microEnable 5 marathon frame grabber 上,您必須將 applet 燒錄到機板的其中一個可用分割區中。Framegrabber SDK 的預設行為是將使用的 applet 設定在 Fg_Init 作為開機分割區。在啟動時,會使用開機分割區中的 applet 在系統上電時初始化機板。這個 applet 隨後也會在 Framegrabber SDK 的第一個初始化階段中使用。

microEnable 6 Frame Grabbers 的啟動行為#

在 CXP-12 Interface Card、imaWorx CXP-12 Quad、imaFlex CXP-12 Quad 或 imaFlex CXP-12 Penta frame grabber 上,Framegrabber SDK 會嘗試開啟最後載入的 applet。最後載入的 applet 定義於 LastApplet 區段中的 FG 鍵,位於名為 me6_<n>_init.config的組態檔中,其中 <n> 是機板的驅動程式索引。驅動程式索引與章節中所述的機板索引非常相似 使用應用的 Index,但它是驅動程式專屬的,且不會受到機板重新排序的影響。在 Windows 上, driver index 0 是指驅動程式找到的第一張 CXP-12 Interface Card、imaWorx CXP-12 或 imaFlex CXP-12 frame grabber, driver index 1 是指第二張機板,以此類推。在所有其他作業系統上,驅動程式索引與機板索引相同,不會套用任何重新排序。

在 Windows 系統上,組態檔通常位於目錄 %APPDATA%\basler中。在所有其他作業系統上,組態檔位於 $HOME/.config/basler 目錄中。如果找不到組態檔,則 Framegrabber SDK 會在 Framegrabber SDK 安裝的根資料夾中尋找。

如果找不到 frame grabber 的組態檔,或者初始化失敗,則會使用預設的 applet。預設的 applet 通常是每個實體連接埠支援一個面陣感測器類型相機的 applet。

如果預設 applet 的初始化也失敗,則 Framegrabber SDK 會開啟任何可使用的 applet。

列舉 Applet#

int Fg_getAppletIterator(
    int board,
    FgAppletIteratorSource source,
    Fg_AppletIteratorType * iter,
    int flags);

Fg_AppletIteratorItem Fg_getAppletIteratorItem(
    Fg_AppletIteratorType iter,
    int index);

int64_t Fg_getAppletIntProperty(
    Fg_AppletIteratorItem item,
    FgAppletIntProperty property);

const char * Fg_getAppletStringProperty(
    Fg_AppletIteratorItem item,
    FgAppletIntProperty property);

int Fg_freeAppletIterator(
    Fg_AppletIteratorType iter);

通常,應用程式會使用一個或幾個特定的 applet,其功能可以透過 applet 文件來了解,且以下函式對大多數應用程式而言並無用處。在應用程式設計得更為動態的情況下,Framegrabber API 提供了相關函式,透過這些函式可以列舉 applet 並請求關於 applet 的各種資訊。

若要列舉 Applet,函式為 Fg_getAppletIterator() 即可呼叫。如果您想要列舉 Framegrab_ber SDK 安裝中可用的 Applet,則來源 FG_AIS_FILESYSTEM 應予使用。若要僅列舉可載入指定機板上的 Applet,請傳遞 FG_AF_IS_LOADABLE 作為旗標。這可確保 API 傳回的任何 Applet 皆可供後續初始化圖形擷取卡的呼叫使用。該函數會傳回迭代器中的項目數量。

Applet 迭代器的項目可以透過呼叫 Fg_getAppletIteratorItem().

可以透過呼叫從項目中要求以下資訊 Fg_getAppletIntProperty() 或 Fg_getAppletStringProperty():

Property 描述 Type
FG_AP_INT_FLAGS 適用於 Applet 的旗標(FG_AF_… 常數) int32_t
FG_AP_INT_INFO 適用於 Applet 的標籤(FG_AI_… 常數) int32_t
FG_AP_INT_PARTITION 燒錄 Applet 的磁碟分割區(僅限 mE5) int32_t
FG_AP_INT_NR_OF_DMA Applet 提供的最大 DMA 通道數 int32_t
FG_AP_INT_NR_OF_CAMS 使用 Applet 時可存取的最大相機數量 int32_t
FG_AP_INT_GROUP_CODE Applet 授權群組代碼(必須是圖形擷取卡授權群組代碼的子集a) int32_t
FG_AP_INT_USER_CODE Applet 授權使用者代碼(必須與圖形擷取卡授權使用者代碼相符) int32_t
FG_AP_INT_DESIGN_VERSION FPGA 設計的主要版本 int32_t
FG_AP_INT_DESIGN_REVISION FPGA 設計的版本修訂版 int32_t
FG_AP_STRING_APPLET_UID 識別 Applet 檔案的 UID string
FG_AP_STRING_BITSTREAM_UID 識別 FPGA 設計的 UID string
FG_AP_STRING_DESIGN_NAME FPGA 設計的名稱 string
FG_AP_STRING_APPLET_NAME Applet 名稱 string
FG_AP_STRING_DESCRIPTION Applet 說明 string
FG_AP_STRING_CATEGORY Applet 類別 string
FG_AP_STRING_APPLET_PATH Applet 的完整路徑 string
FG_AP_STRING_SUPPORTED_PLATFORMS 支援平台的逗號分隔清單 string
FG_AP_STRING_TAGS 標籤的逗號分隔清單 string
FG_AP_STRING_VERSION Applet 版本 string
FG_AP_STRING_APPLET_FILE Applet 檔案名稱 string
FG_AP_STRING_RUNTIME_VERSION 所需的 Framegrabber SDK 版本 string

上述清單並不完整,僅包含對應用程式有用的資訊Property。有關該 API 的更多使用案例,請參閱 Framegrabber API 參考手冊。

使用 applet iterator 後,應透過呼叫來釋放它 Fg_freeAppletIterator().

只要名稱是唯一的且 applet 位於標準位置之一,Applet 名稱就足以用於後續的初始化擷取卡呼叫。如果多個 applet 使用相同的名稱,或者 applet 位於標準位置之外,則應使用完整路徑。以下範例顯示如何從標準位置中找到的所有 applet 中擷取 applet 名稱:

const in boardIndex = 0;

Fg_AppletIteratorType iter = 0;
int numItems =
    Fg_getAppletIterator(boardIndex, FG_AIS_FILESYSTEM,
                         &iter, FG_AF_IS_LOADABLE);

if (numItems >= 0) {
    std::cout << "Found " << numItems << " applets";

    for (int i = 0; i < numItems; ++i) {
        auto item = Fg_getAppletIteratorItem(iter, i);

        const char * appletName =
            Fg_getAppletStringProperty(item, FG_AP_STRING_APPLET_NAME);

        std::cout << " " << appletName;
    }

    std::cout << std::endl;
    Fg_freeAppletIterator(iter);
}

擷取卡初始化#

Fg_Struct * Fg_Init(
    const char * applet,
    unsigned int board);

int Fg_FreeGrabber(
    Fg_Struct * fg);

若要使用擷取卡,必須先進行初始化。這可以透過使用下列函數載入 applet 來完成 Fg_Init()。此函式會在使用指定的 Applet 初始化擷取卡之後,傳回該指定應畫板索引之擷取卡的控制代碼。此控制代碼將用於後續章節中所描述的大多數 API 函式中。

當應用程式使用完擷取卡後,應透過呼叫下列項目來釋放它: Fg_FreeGrabber().

例如,下列程式碼會使用 Applet 初始化 CXP-12 擷取卡: Acq_SingleCXP12Area:

const int boardIndex = 0;
const char * applet = "Acq_SingleCXP12Area";

Fg_Struct * fg = Fg_Init(applet, boardIndex);
if (fg != nullptr) {
    // use frame grabber ...

    Fg_FreeGrabber(fg);
}

組態檔#

Fg_Struct * Fg_InitConfig(
    const char * config,
    unsigned int board);

int Fg_loadConfig(
    Fg_Struct * fg,
    const char * config);

int Fg_saveConfig(
    Fg_Struct * fg,
    const char * config);

如果您使用 microDisplay X 來初次設定擷取卡並測試您的設定,則可以從程式內部儲存設定。主要的設定檔副檔名為 .mcf 而第二個副檔名為 .mfs 的檔案將會在旁邊一併建立。有了這兩個檔案,應用程式即可直接使用這兩個檔案初始化擷取卡,而不需透過 API 呼叫來複製設定。

在此內容中,設定是指初始化擷取卡時所使用的 Applet,以及該 Applet 的參數設定。如需詳細資訊,請參閱使用 Applet 參數一章。

資訊

只有副檔名為 .mcf 的主要設定檔會用於函式的參數中。只要副檔名之前的檔名相同,副檔名為 .mfs 的檔案將會自動被使用。

若要從設定檔初始化擷取卡,可以呼叫函式 Fg_InitConfig() 來取代 Fg_Init()。此函式會在根據指定的的主要設定檔初始化擷取卡之後,傳回該指定應畫板索引之擷取卡的控制代碼。

當應用程式使用完擷取卡後,應透過呼叫下列項目來釋放它: Fg_FreeGrabber().

如果應用程式需要不同的設定組合,可以透過在呼叫 Fg_loadConfig() 之後的任何時間點呼叫 Fg_Init() 或 Fg_InitConfig().

將這些設定套用到擷取卡。可以透過呼叫 Fg_saveConfig() 之後的任何時間點呼叫 Fg_Init() 或 Fg_InitConfig().

將目前的設定寫入設定檔:

const char * config = "SavedState.mcf";

int result = Fg_saveConfig(fg, config);
if (result != FG_OK) {
    // handle error ...
}

// change frame grabber configuration ...

result = Fg_loadConfig(fg, config);
if (result != FG_OK) {
    // handle error ...
}

處理 Applet 參數#

使用 Applet 初始化擷取卡後,即可讀取或操作 Applet 的參數。例如,若要從相機擷取影像,必須將 Applet 設定為根據相機傳送的影像資料使用正確的影像尺寸與影像格式。另一個例子是設定 Applet 提供的觸發模組以符合應用程式需求。

參數識別碼與參數名稱#

int Fg_getParameterIdByName(
    Fg_Struct * fg,
    const char * name);

const char * Fg_getParameterNameById(
    Fg_Struct * fg,
    unsigned int id,
    unsigned int dma);

FgParamTypes Fg_getParameterTypeById(
    Fg_Struct * fg,
    unsigned int id,
    unsigned int dma);

資訊

函數 Fg_getParameterTypeById() 是在 Framegrabber SDK 5.9 版中新增的。

Applet 的每個參數皆透過其名稱進行識別,但 API 會使用數值識別碼來存取參數值或屬性。許多參數都具有標頭檔 basler_fg.h中所指定的固定數值識別碼,然而除了最常見的參數之外,並不建議使用此方式。若要取得參數的數值識別碼,會使用函式 Fg_getParameterIdByName() 。如果在將名稱轉譯為識別碼時發生錯誤,此函式會傳回 0 或負的錯誤代碼。

若要取得已知數值識別碼之參數的名稱,則使用函式 Fg_getParameterNameById() 。

若要取得已知數值識別碼之參數的類型,則使用函式 Fg_getParameterTypeById() 。

參數類型#

參數具備類型,若要取得或設定參數的值,必須知道其類型,這可以從 Applet 的文件、使用 VisualApplets 自行設計之 Applet 的隱含方式取得,或是透過使用函式來要求類型 Fg_getParameterTypeById().

Parameter Type C/C++ 型別
FG_PARAM_TYPE_INT32_T int32_t
FG_PARAM_TYPE_UINT32_T uint32_t
FG_PARAM_TYPE_INT64_T int64_t
FG_PARAM_TYPE_UINT64_T uint64_t
FG_PARAM_TYPE_DOUBLE double
FG_PARAM_TYPE_CHAR_PTR string
FG_PARAM_TYPE_SIZE_T size_t
FG_PARAM_TYPE_STRUCT_FIELDPARAMINT FieldParameterAccess
FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64 FieldParameterAccess
FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE FieldParameterAccess

具備其中一種欄位參數類型的參數應使用以下 API 進行處理: FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS API 描述於小節 存取欄位參數 章節 使用 Plain C時的注意事項.

存取參數值#

int Fg_getParameterWithType(
    Fg_Struct * fg,
    int id,
    int32_t * value,
    unsigned int dma);

// Fg_getParameterWithType() is overloaded for:
//     int32_t * value
//     uint32_t * value
//     int64_t * value
//     uint64_t * value
//     float * value
//     double * value
//     std::string & value
int Fg_setParameterWithType(
    Fg_Struct * fg,
    int id,
    int32_t value,
    unsigned int dma);

// Fg_setParameterWithType() is overloaded for:
//     int32_t value
//     uint32_t value
//     int64_t value
//     uint64_t value
//     float value
//     double value
//     const std::string & value

若要取得或設定參數的值,應使用多載函數 Fg_getParameterWithType() 進行明確的生命週期管理即呼叫 Fg_setParameterWithType() 。

例如,給定來自先前呼叫 fg 的 Fg_Init()中繼擷取卡控制代碼,以下程式碼將 applet 的第一個 DMA 通道寬度設為 1024。

const int dma = 0;
const int width = 1024;
int result = FG_INVALID_PARAMETER;

int paramId = Fg_getParameterIdByName(fg, "FG_WIDTH");
if (paramId > 0) {
    result = Fg_setParameterWithType(fg, paramId, width, dma);
}

在大多數情況下,傳遞至函數的最後一個參數 Fg_getParameterWithType() 進行明確的生命週期管理即呼叫 Fg_setParameterWithType() 是指 DMA 通道。諸如 FG_WIDTH, FG_HEIGHT 等參數可能會因每個 DMA 通道而異,並且透過將 DMA 通道編號傳遞至函數,即可請求或變更這些參數的每個實例。

在某些情況下,最後一個參數可以指其他邏輯索引。例如,參數 FG_NR_OF_DMAS 或 FG_NR_OF_CAMS 對於 applet 的每個處理程序存在多個實例,這是在 VisualApplets 中定義的範圍。

在 applet 的上下文中,其他參數是全域的,並且函數中的最後一個參數會被忽略。其中一個參數將會是 FG_NR_OF_PROCESSES。但更顯著的是,如果應用程式開發人員使用 VisualApplets 設計自己的 applet,所有控制 applet 的參數都可以被視為全域的,因為它們是透過唯一名稱來識別的,且函數中的最後一個參數將被忽略。

某些參數可能會因擷取的每個影像而異,並且需要 DMA 通道以及影格編號或緩衝區編號。這適用於任何影像中繼資料,例如影像傳輸至電腦記憶體時的時間戳記、實際影像資料的長度以及類似資訊。這些參數無法由上述函數處理,此主題將在影像擷取一章中進行更詳細的討論。

存取參數屬性#

int Fg_getParameterPropertyWithType(
    Fg_Struct * fg,
    int id,
    FgProperty propertyId,
    int32_t * value);

// Fg_getParameterPropertyWithType() is overloaded for:
//     int32_t * value
//     uint32_t * value
//     int64_t * value
//     uint64_t * value
//     float * value
//     double * value
//     std::string & value
int Fg_getParameterPropertyWithTypeEx(
    Fg_Struct * fg,
    int id,
    FgProperty propertyId,
    int32_t * value,
    unsigned int dma);

// Fg_getParameterPropertyWithTypeEx() is overloaded for:
//     int32_t * value
//     uint32_t * value
//     int64_t * value
//     uint64_t * value
//     float * value
//     double * value
//     std::string & value

資訊

本章中記載的函數是在 Framegrabber SDK 5.9 版中新增的。有關舊介面,請參閱在純 C 中存取參數屬性一節。

除了目前的值之外,參數還具有各種屬性。使用多載函數 Fg_getParameterPropertyWithType() 是不建議的,因為函數呼叫隱式使用 DMA 通道 0,但參數屬性可能會因不同通道而異。使用多載函數 Fg_getParameterPropertyWithTypeEx() 可以請求以下屬性:

Property 描述 Type
PROP_ID_VALUE 目前參數值 與參數相同
PROP_ID_DATATYPE 參數類型
(根據 FgParamTypes 的列舉值)
int32_t
PROP_ID_NAME 描述性名稱 string
PROP_ID_PARAMETERNAME 參數名稱 string
PROP_ID_VALUELLEN 將該值編碼為字串所需的長度 int32_t
PROP_ID_ACCESS 參數的存取旗標 int32_t
PROP_ID_MIN 最小參數值b 與參數相同
PROP_ID_MAX 最大參數值b 與參數相同
PROP_ID_STEP 參數的步進大小b 與參數相同
PROP_ID_IS_ENUM 0:非列舉參數
n:PROP_ID_ENUM_VALUES 所需的緩衝區大小
int32_t
PROP_ID_ENUM_VALUES 列舉值(請參閱存取列舉值參數屬性) FgPropertyEnumValues[]
PROP_ID_FIELD_SIZE 欄位參數中的元素數量 int32_t

上述清單並不完整,僅包含未被視為已取代之資訊的屬性。有關 API 的更多使用案例,請參閱 Framegrabber API 參考手冊。

以下範例展示如何取得參數的最小值,假設 paramId 是以下類型的參數 FG_PARAM_TYPE_INT32_T:

int minVal = 0;
int result =
    Fg_getParameterPropertyWithTypeEx(fg, paramId, PROP_ID_MIN,
                                      &minVal, dma);
if (result == FG_OK) {
    // work with the property ...
}

記憶體管理#

若要從相機擷取影像,電腦中需要有記憶體,以便將這些影像傳輸至其中,並在程式內部存取影像資料。Framegrabber API 使用環形緩衝區記憶體模型。記憶體緩衝區至少包含兩個項目(通常稱為影格緩衝區或子緩衝區),這些緩衝區將重複用於從相機擷取的後續影格。應使用多少影格緩衝區取決於多種因素,其中最重要的是在處理某個影格的同時,可能還會有多少後續影格到達。這將在影像擷取一章的擷取模型一節中進行更詳細的討論。

每個影格緩衝區的大小必須相同,且必須足夠大,以便儲存符合應用程式需求之可能的最大影像,通常由寬度 FG_WIDTH、高度 FG_HEIGHT 以及 Pixel Format FG_FORMAT決定。(對於在 VisualApplets 中設計的 applet,寬度、高度和 Pixel Format 的設定通常會更為複雜。)

環形緩衝區可以是記憶體中的一個大型區塊,在虛擬位址空間中連續並細分為大小相等的影格緩衝區;或者它也可以由在記憶體中個別配置並加入至管理結構的影格緩衝區組成 dma_mem ,該結構由 API 使用。

void * Fg_AllocMem(
    Fg_Struct * fg,
    size_t totalSize,
    frameindex_t numFrames,
    unsigned int dma);

int Fg_FreeMem(
    Fg_Struct * fg,
    unsigned int dma);

有三組不同的函數可用於記憶體管理。然而,使用這些函數 Fg_AllocMem() 進行明確的生命週期管理即呼叫 Fg_FreeMem() 並不建議,因為它僅限於標準擷取模型(ACQ_STANDARD)的使用。其餘兩組函數將在此處進行更詳細的討論,因為它們可用於所有三種擷取模型(ACQ_STANDARD、ACQ_BLOCK 和 ACQ_SELECT),並且既適用於簡單的使用案例,也適用於應用程式更具體的的需求。

進階記憶體管理#

dma_mem * Fg_AllocMemEx(
    Fg_Struct * fg,
    size_t totalSize,
    frameindex_t numFrames);

int Fg_FreeMemEx(
    Fg_Struct * fg,
    dma_mem * mem);

函數 Fg_AllocMemEx() 可用於配置一個大小適中的連續記憶體緩衝區,其大小為 totalSize 該緩衝區會被細分為 numFrames 大小相等的影格緩衝區。該函式會傳回指向記憶體管理結構的代碼 (handle),若發生錯誤則傳回 nullptr。

傳回的指標並非指向記憶體緩衝區本身的指標,且不應直接使用!當不再需要該記憶體時,應使用以下方式釋放: Fg_FreeMemEx().

在以下範例中,程式碼將為 16 個影格緩衝區配置記憶體緩衝區,這些影格緩衝區可容納大小為 1024 x 1024 的 24 位元 RGB 影像:

const int width = 1024, height = 1024;
const int bytesPerPixel = 3;
const frameindex_t numFrames = 16;
const size_t frameSize = static_cast<size_t>(width) * height * bytesPerPixel;
const size_t totalSize = frameSize * numFrames;

dma_mem * mem = Fg_AllocMemEx(fg, totalSize, numFrames);
if (mem != nullptr) {
    // use memory, acquire and process images ...

    Fg_FreeMem(fg, mem);
}

當 static_cast<size_t>(width) 在計算 frameSize時。這對於確保正確計算非常大影像的大小是必要的。

彈性記憶體管理#

dma_mem * Fg_AllocMemHead(
    Fg_Struct * fg,
    size_t totalSize,
    frameindex_t numFrames);

int Fg_AddMem(
    Fg_Struct * fg,
    void * frameBuffer,
    size_t size,
    frameindex_t index,
    dma_mem * mem);

int Fg_DelMem(
    Fg_Struct * fg,
    dma_mem * mem,
    frameindex_t index);

int Fg_FreeMemHead(
    Fg_Struct * fg,
    dma_mem * mem);

如果應用程式對記憶體配置有特定要求,而不希望由 Framegrabber API 進行處理,則可以使用函式 Fg_AllocMemHead() 來準備記憶體管理結構。該函式需要與 Fg_AllocMemEx()相同的參數,但它不會配置任何記憶體。在應用程式中配置記憶體後,需要使用函式 Fg_AddMem()將每個影格緩衝區分別加入。如果應用程式需要在擷取執行期間動態變更記憶體緩衝區所使用的影格緩衝區,則可以使用函式 Fg_DelMem() 從記憶體緩衝區中移除影格緩衝區。當不再需要記憶體緩衝區時,應先透過呼叫 Fg_FreeMemHead() 來釋放管理結構,然後再釋放影格緩衝區的記憶體。

以下程式碼顯示了函式 'Fg_AllocMemEx()' 如何作為更具彈性的記憶體管理 API 的便利包裝函式:

int Fg_AllocMemEx(Fg_Struct * fg, size_t totalSize, frameindex_t numFrames)
{
    const size_t frameSize = totalSize / numFrames;
    char * buf = nullptr;

    dma_mem * mem = Fg_AllocMemHead(fg, totalSize, numFrames);
    if (mem != nullptr) {
        try {
            buf = new char[totalSize];
            for (frameindex_t frame = 0; frame < numFrames; ++frame) {
                const int result =
                    Fg_AddMem(fg, buf + frameSize*frame,
                              frameSize, frame, mem);
                if (result != FG_OK) {
                    throw std::runtime_error("Failed to add frame buffer");
                }
            }
        }
        catch (...) {
            for (frameindex_t frame = 0; frame < numFrames; ++frame) {
                Fg_DelMem(fg, mem, frame);
            }
            Fg_FreeMemHead(fg, mem);
            delete[] buf;
            return nullptr;
        }
    }
    return mem;
}

影像擷取#

Framegrabber API 提供了兩種影像資料傳遞模式,可與三種不同的擷取模型結合使用。

影像資料可以同步或非同步模式傳遞。在同步模式中,應用程式必須提供擷取迴圈。在非同步模式中,可以註冊一個回呼函式,只要有新的影像資料可用,該函式就會被呼叫。Framegrabber API 將在非同步模式的獨立執行緒中提供影像擷取迴圈。對於某些 GUI 架構,將 Framegrabber API 執行緒內容同步回 GUI 執行緒內容可能會很複雜,而在該架構提供的執行緒內容中執行擷取迴圈可能會更合理。

註冊非同步模式的回呼函數#

typedef int (* Fg_ApcFunc_t)(
    frameindex_t frame,
    void * data);

int Fg_registerApcHandlerEx(
    Fg_Struct * fg,
    unsigned int dma,
    Fg_ApcFunc_t func,
    void * data,
    unsigned int timeout,
    unsigned int flags);

int Fg_unregisterApcHandler(
    Fg_Struct * fg,
    unsigned int dma);

資訊

在 Framegrabber API 5.9 版本中,加入了函式 Fg_registerApcHandlerEx() 進行明確的生命週期管理即呼叫 Fg_unregisterApcHandler() 並且變更了使用封鎖擷取模型時的行為 ACQ_BLOCK 。請參閱章節 在 Plain C 中為非同步模式註冊回呼函數 以了解舊的介面。

函數 Fg_registerApcHandlerEx() 可用於在開始擷取之前設定非同步模式。

要 Fg_registerApcHandlerEx(),類型為 Fg_ApcFunc_t 可以註冊以在接收到影像資料時被呼叫。該函式將針對特定的擷取卡和 DMA 通道進行註冊,並在被呼叫時傳遞兩個參數。回呼函式的第一個參數是所接收影像的影格編號(在 ACQ_STANDARD 進行明確的生命週期管理即呼叫 ACQ_SELECT中)或緩衝區編號(在 ACQ_BLOCK中)。第二個參數是指向呼叫 Fg_registerApcHandlerEx() 並且可以用作指向上下文結構或類別的指標,例如 this 實作影像處理的類別執行個體的指標。

時所提供的指標。

回呼函式將從 Framegrabber API 提供的擷取迴圈中被呼叫。這意味著回呼函式是在擷取迴圈的執行緒內容中被呼叫的。這也意味著回呼函式傳回所花費的時間會增加到擷取迴圈的一般管理負擔中,並且在此期間可能會接收到多個影像。如果在該期間接收到任何影像,回呼函式將在傳回後立即再次被呼叫。

影像擷取的處理程序可以透過 timeout 進行明確的生命週期管理即呼叫 flags 中的參數進行控制,該參數用於呼叫 Fg_registerApcHandlerEx()。逾時時間以秒為單位,指定擷取迴圈等待新影像到達的時間。對於參數 flags,可以使用下列值以及它們與運算子 | 的組合(二進位 OR):

旗標 描述
FG_APC_DEFAULTS 擷取迴圈中的預設處理方式
FG_APC_BATCH_FRAMES 如果接收到多個影像,在對回呼函數的單次呼叫中,可能僅傳遞最新的影像
FG_APC_DELIVER_ERRORS 將錯誤以負數 frameindex_t 值傳遞至回呼函數
FG_APC_IGNORE_TIMEOUTS 忽略影像逾時並繼續處理
FG_APC_IGNORE_APCFUNC_RETURN 忽略回呼傳回值並繼續處理
FG_APC_IGNORE_STOP 忽略擷取停止的狀態並繼續處理
FG_APC_HIGH_PRIORITY 提高實作擷取迴圈的執行緒優先權。若要使用此值,需要作業系統的提升權限。
FG_APC_OLD_ACQ_BLOCK_BEHAVIOR 使用 Fg_getLastPicNumberBlockingEx() 於 ACQ_BLOCK 中c

擷取迴圈的預設處理方式為:

  • 每一個影像都會傳遞至回呼函數
  • 錯誤值不會傳遞至回呼函數
  • 影像逾時會結束擷取迴圈
  • 回呼函數傳回非零值會結束擷取迴圈
  • 停止擷取會結束擷取迴圈
  • 擷取迴圈在具有預設優先權的執行緒中執行
  • 函數 Fg_getLastPicNumberBlockingEx() 被呼叫,且結果會傳遞至回呼函數

資訊

結束擷取迴圈將會自動取消註冊回呼函數。雖然在停止擷取時總是結束擷取迴圈似乎很合乎邏輯,但情況並非總是如此。例如,如果應用程式需要透過呼叫具有有限擷取影格數的 Fg_AcquireEx,以特定影格數的叢發(bursts)方式執行擷取。在擷取指定數量的影格之後,驅動程式會自動停止擷取。保持擷取迴圈持續運行,並從獨立執行緒啟動擷取叢發可能會更為方便。

資訊

設定 FG_APC_HIGH_PRIORITY 只有在處理程序具備變更執行緒優先權所需的權限時才可能執行。請參閱所使用作業系統的權限與存取權管理說明文件。對於使用 Microsoft Windows 的應用程式,會使用函數 SetThreadPriority 。在 Linux 上,則會使用函數 pthread_setschedparam 。

以下範例顯示如何使用簡單的結構來註冊回呼函數,該結構包含處理影像擷取所需的資訊:

struct ApcUserCallbackData
{
    Fg_Struct * fg;
    dma_mem * mem;
    unsigned int dma;
    unsigned int timeoutInSeconds;
    int mode;
};

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // process new image ...
    } else {
        // handle error ...
    }

    return 0;
}

void SetupApcUserCallback(ApcUserCallbackData * context)
{
    // register callback function
    int result =
        Fg_registerApcHandlerEx(
            context->fg,
            context->dma,
            &ApcUserCallback,
            context,
            context->timeoutInSeconds,
            FG_APC_DELIVER_ERRORS | FG_APC_IGNORE_TIMEOUTS);
    if (result != FG_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

範例中未顯示上下文結構的配置和管理。當回呼函式保持註冊狀態時,該指標必須有效。其中一種解決方案是將所有內容保存在 C++ 類別中。若要在 C++ 類別上下文中使用回呼函式,可以使用靜態函式來註冊回呼處理常式,並且 this 指標應用作上下文資料指標,該指標可以轉換回類別指標並相應地使用。

當不再需要回呼函式時,可以透過呼叫取消註冊 Fg_unregisterApcHandler() 使用相同的抓圖卡控制代碼和 DMA 通道:

Fg_unregisterApcHandler(context->fg, context->dma);

開始擷取#

若要啟用從抓圖卡到應用程式記憶體的影像資料傳輸,必須啟動 applet 中的擷取功能。這會將影像資料應傳輸至的畫面緩衝區通知抓圖卡,並在成功傳輸新影像的資料時,啟用傳送中斷至驅動程式的功能。在大多數情況下,在抓圖卡端啟動擷取後,您也必須在相機端啟動擷取並開始觸發相機。

有關控制相機的進一步資訊,請參閱 The Camera Control Library siso_genicam 或 The Camera Link Serial Interface Library clsersis。雖然觸發相機超出本文件的範圍,但 applet 的文件將提供有關應用程式透過軟體或硬體訊號觸發相機之選項的更多資訊。

int Fg_Acquire(
    Fg_Struct * fg,
    unsigned int dma,
    frameindex_t frames);

int Fg_stopAcquire(
    Fg_Struct * fg,
    unsigned int dma);

這些函式 Fg_Acquire() 進行明確的生命週期管理即呼叫 Fg_stopAcquire() 只能與記憶體管理函數結合使用 Fg_AllocMem() 進行明確的生命週期管理即呼叫 Fg_FreeMem() 且僅限於使用標準擷取模型 ACQ_STANDARD不建議使用這些函數,因此將不予記錄。相反地,這些函數 Fg_AcquireEx() 進行明確的生命週期管理即呼叫 Fg_stopAcquireEx() 。

使用進階或彈性 Memory 管理進行影像擷取#
int Fg_AcquireEx(
    Fg_Struct * fg,
    unsigned int dma,
    frameindex_t frames,
    int flags,
    dma_mem * mem);

int Fg_stopAcquireEx(
    Fg_Struct * fg,
    unsigned int dma,
    dma_mem * mem,
    int flags);

函數 Fg_AcquireEx() 可以被呼叫以在擷取卡的單一 DMA 通道上啟動影像擷取。此呼叫會將 DMA 通道 dma 繫結至 Memory 參照碼 mem ,從而使用配置用於接收影像資料的 Memory。

擷取將持續進行,直到達到透過參數 frames 指定的影格數為止。如果 GRAB_INFINITE 傳遞給 frames,擷取將無限期地持續進行。透過參數 flags,可以選擇擷取模型。請參閱 擷取模型 節以取得更多資訊。

型號 描述
ACQ_STANDARD 無影格緩衝區保護的連續擷取
ACQ_BLOCK 具有影格緩衝區封鎖功能的連續擷取
ACQ_SELECT 完全由應用程式控制且具有手動影格緩衝區處理的擷取

當在對 Fg_AcquireEx()的呼叫中指定要擷取的影格數時,當 Framegrabber SDK 遇到預期的影格數時,擷取將會自動停止。在某些情況下,這可能會在處理最後一個影格時導致非預期的行為。為避免這種情況,除了擷取模型外, ACQ_NO_AUTOSTOP 也可以在參數 flags中指定。當使用 ACQ_NO_AUTOSTOP 時,驅動程式將在達到要求的影格數時停止擷取影格,但 Framegrabber SDK 和擷取卡的其餘部分將保持在擷取模式,直到 Fg_stopAcquireEx() 時釋放。

若要停止擷取並重設所使用 applet 的執行狀態, Fg_stopAcquireEx() 函數應被呼叫。透過參數 flags,可以選擇停止模式:

Mode 描述
STOP_ASYNC 立即停止擷取
STOP_SYNC_TO_APC 在非同步模式下,將停止擷取同步至回呼函數
等待回呼完成的時間(以毫秒為單位)可透過參數 FG_APC_STOP_TIMEOUT 進行設定
STOP_SYNC 將停止擷取與驅動程式同步
等待下一個影像傳輸完成的時間(以秒為單位)可透過參數 FG_STOP_TIMEOUT 進行設定
STOP_ASYNC_FALLBACK 可與 STOP_SYNC 一起使用
如果無法同步停止,則退回使用 STOP_ASYNC

為同步模式編寫擷取迴圈#

在同步模式下,應用程式必須處理接收新影像的作業。這通常透過擷取迴圈的形式來完成,該迴圈會呼叫 Framegrabber API 所提供的其中一個函數,這些函數會等待新影像在特定的 DMA 通道上傳輸。若要了解應使用哪一個函數,請參閱擷取模型章節以取得更多資訊。針對每個模型,對應的小節中都提供了範例擷取迴圈。

frameindex_t Fg_getLastPicNumberBlocking(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout);

函數 Fg_getLastPicNumberBlocking() 只能與函數結合使用 Fg_Acquire() 且僅限於標準擷取模型 ACQ_STANDARD。不建議使用此函數,且本文將不予記錄。取而代之的是函數 Fg_getLastPicNumberBlockingEx() 或 Fg_getImageEx() 。

使用進階或彈性記憶體管理等待影像#
frameindex_t Fg_getLastPicNumberBlockingEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout,
    dma_mem * mem);

frameindex_t Fg_getImageEx(
    Fg_Struct * fg,
    int strategy,
    frameindex_t frame,
    unsigned int dma,
    unsigned int timeout,
    dma_mem * mem);

函數 Fg_getLastPicNumberBlockingEx() 等待參數中要求的畫格 frame 在參數中給定的秒數內 timeout 到達參數中所指定的給定 DMA 通道 dma。第一個畫格的編號為 1,而非 0。若成功,該函數會傳回大於 0 的畫格編號;若失敗,則傳回負數錯誤代碼。

資訊

傳回的畫格編號可能大於所要求的畫格編號,因為該函數永遠會傳回可用的最新畫格編號。這意味著在兩次呼叫該函數之間,可能已經有多個畫格到達,應用程式需要決定是僅處理最新畫格還是處理所有畫格。

以下範例顯示了簡單的擷取迴圈,用於 ACQ_STANDARD:

const int timeoutInSeconds = 10;

frameindex_t nextFrame = 1;
while (true) {
    // get new image
    const frameindex_t newestFrame =
        Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
                                      timeoutInSeconds, mem);

    if (newestFrame > 0) {
        // process new images ...

        nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
    } else {
        // handle error ...
    }
}

函數 Fg_getImageEx() 複雜度高得多。該函數的行為以及成功時傳回代碼的含義取決於參數 strategy 進行明確的生命週期管理即呼叫 timeout 以及所使用的擷取模型。如果發生錯誤,結果將始終是負數錯誤代碼。以下將對不同的策略進行簡短描述,但一般而言,該函數的使用應限於章節中所述的使用案例 阻斷式擷取模型。不鼓勵任何其他方式使用該函數,且本文中將不再詳細討論。

SEL_NEW_IMAGE:該函數要求指定逾時時間,並等待至少一個新影像到達。在 ACQ_STANDARD 進行明確的生命週期管理即呼叫 ACQ_SELECT 中,將傳回最新影像的畫格編號。在 ACQ_BLOCK,此函式會封鎖最新影像的畫面緩衝區,解除封鎖其他影像的任何畫面緩衝區,並傳回所封鎖影像的緩衝區編號。

SEL_NEXT_IMAGE:在 ACQ_STANDARD中,若未指定逾時時間,該函式將傳回所接收最新影像的緩衝區編號。若指定了逾時時間,該函式將等待前一次呼叫該函式時所接收之上一張畫面之後的下一張畫面,若先前未呼叫該函式,則等待畫面編號 1。該函式將傳回所接收最新影像的畫面編號。在 ACQ_BLOCK中,此函式會封鎖尚未被封鎖或解除封鎖所接收之第一張影像的畫面緩衝區,並傳回所封鎖影像的緩衝區編號。如果無法再封鎖任何影像且指定了逾時時間,該函式會等待至少有一張新影像到達,然後再執行封鎖作業。

SEL_ACT_IMAGE:在 ACQ_STANDARD中,該函式的運作方式與指定 SEL_NEXT_IMAGE 時相同。在 ACQ_BLOCK中,此函式會封鎖最新影像的畫面緩衝區,解除封鎖其他影像的任何畫面緩衝區,並傳回所封鎖影像的緩衝區編號。如果無法再封鎖任何影像且指定了逾時時間,該函式會等待至少有一張新影像到達,然後再執行封鎖作業。

SEL_LAST_IMAGE:此函式會傳回該函式最後傳回的值,或是由 Fg_getLastPicNumberBlockingEx()傳回的值。這會是最後的畫面編號或緩衝區編號,具體取決於上次呼叫該函式的方式。

SEL_NUMBER:此函式會模擬 Fg_getLastPicNumberBlockingEx().

以下範例顯示了簡單的擷取迴圈,用於 ACQ_BLOCK 使用 SEL_NEXT_IMAGE 的行為(處理所擷取的每一張影像):

const int timeoutInSeconds = 10;

while (true) {
    // get new image
    const frameindex_t buffer =
        Fg_getImageEx(fg, SEL_NEXT_IMAGE, 0, dma, timeoutInSeconds, mem);

    if (buffer > 0) {
        // process new image ...
    } else {
        // handle error ...
    }
}

驅動程式影像擷取逾時 FG_TIMEOUT#

除了在呼叫函式 Fg_getImageEx() 或 Fg_getLastPicNumberBlockingEx()時指定等待影像的時間,或是在為非同步模式設定回呼函式時指定之外,驅動程式也會在處理影像傳輸中斷的過程中追蹤影像擷取。如果在參數 FG_TIMEOUT。如果 FG_TIMEOUT 設為 FG_TIMEOUT_INFINITE 所指定的秒數內未接收到任何影像資料,驅動程式將停止影像擷取(若 FG_TIMEOUT_INFINITE 是 INT_MAX 的值為 - 1,無論接收兩張影像之間的時間間隔為何,驅動程式都不會停止擷取)。

參數 FG_TIMEOUT 的值會在開始擷取時轉發給驅動程式。在開始擷取之後對參數進行的任何變更,直到停止並重新開始擷取之前,驅動程式都不會予以考量。

在驅動程式停止擷取之後,任何仍在等待影像的函式呼叫都會傳回 FG_TIMEOUT_ERR ,而在擷取停止後呼叫以等待影像的任何函式都會傳回 FG_TRANSFER_NOT_ACTIVE.

資訊

。 FG_TIMEOUT 是 不 FG_TIMEOUT_INFINITE參數的預設值為 FG_TIMEOUT 設為 FG_TIMEOUT_INFINITE ,但在大多數情況下會設為 1000000 秒(相當於大約 277 小時 46 分鐘)。建議在開始擷取之前明確設定參數

。

Fg_setParameterWithType(fg, FG_TIMEOUT, FG_TIMEOUT_INFINITE, dma);

張數與擷取資訊#

int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t frame);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    dma_mem * mem);

frameindex_t Fg_getStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t frame,
    unsigned int dma,
    dma_mem * mem);

以下範例顯示如何停用驅動程式影像擷取逾時: Fg_getParameterEx():

參數 描述 Type
FG_TRANSFER_LEN 實際傳輸的位元組數 size_t
FG_TIMESTAMP_LONG 影像的高解析度 Timestamp uint64_t
FG_TIMESTAMP_LONG_FREQUENCY 高解析度 Timestamp 頻率 uint64_t
FG_TIMESTAMP 以毫秒為單位的影像 Timestamp uint32_t
FG_IMAGE_TAG 影像標籤 uint32_t
FG_IMAGE_NUMBER 影格編號 uint64_t

資訊

影格編號資訊是在 Framegrabber SDK 5.9 版本中新增的。

每個影格的資訊並非永久有效,僅在對應的緩衝區尚未排入後續傳輸佇列時有效。根據所使用的擷取模型,此函式需要影格編號或緩衝區編號。請參閱擷取模型一節以了解更多詳情。

以下範例顯示如何請求特定影格的 Timestamp:

// get time stamp frequency
uint64_t frequency = 0;
Fg_getParameterEx(fg, FG_TIMESTAMP_LONG_FREQUENCY, &frequency, 0, nullptr, 0);

// ...

// get frame time stamp
uint64_t timestamp = 0
int result =
    Fg_getParameterEx(fg, FG_TIMESTAMP_LONG, &timestamp, dma, mem, frame);
if (result == FG_OK) {
    // this will probably be 'seconds since booting the computer' ...
    // it makes more sense when you calculate the difference
    // between timestamps of two frames
    double seconds =
        static_cast<double>(timestamp)/static_cast<double>(frequency);

    // ...
}

若要取得指向影格緩衝區的指標,可以呼叫 Fg_getImagePtrEx() 函式。每個影格的指標並非永久有效,僅在對應的緩衝區尚未排入後續傳輸佇列時有效。根據所使用的擷取模型,此函式需要影格編號或緩衝區編號。請參閱 擷取模型 一節以了解更多詳情。

以下範例顯示如何請求特定影格的影格緩衝區指標:

// get a pointer to the frame buffer for the newest image
void * buffer = Fg_getImagePtrEx(fg, frame, dma, mem);

// get the actual number of bytes transferred
size_t length = 0;
int result =
    Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, frame);

if ((buffer != nullptr) && (result == FG_OK) && (length > 0)) {
        // process image data ...
}

函數 Fg_getStatusEx() 可以呼叫以請求下列一般狀態資訊,此時參數 frame 將會被忽略:

Status 描述
NUMBER_OF_GRABBED_IMAGES 傳輸的總影格數
NUMBER_OF_LAST_IMAGE 回報的最後一個影格編號 Fg_getLastPicNumberBlockingEx()
NUMBER_OF_NEXT_IMAGE 最後一個回報的畫格之後的下一個畫格之畫格編號
GRAB_ACTIVE 0:DMA 通道未作用
1:DMA 通道作用中

以下範例顯示如何要求 DMA 通道的 Acquisition Status:

frameindex_t active =
    Fg_getStatusEx(fg, GRAB_ACTIVE, 0, dma, mem);
if (active == 1) {
    // the DMA channel is active ...
} else if (active == 0) {
    // the DMA channel is inactive ...
} else {
    // handle error ...
}

擷取模型#

Framegrabber API 提供三種不同的擷取模型,可在呼叫下列項目時進行選擇: Fg_AcquireEx():standard、blocking 與 selective。這三種擷取模型皆以本章所說明之記憶體模型為基礎 記憶體管理 其使用至少兩個畫格緩衝區來擷取影像。

本節中的範例將使用由虛擬記憶體中四個連續畫格緩衝區組成的記憶體緩衝區。

Memory Buffer Model

Frame Numbers and Buffer Numbers#

在整個 Framegrabber API 中,會使用畫格編號與緩衝區編號,兩者皆屬於以下類型 frameindex_t.

畫格編號是嚴格單調遞增的自然數,範圍介於 [1; FRAMEINDEX_MAX],指的是自擷取開始以來所擷取的影像數量;而用於畫格編號的類型 frameindex_t 是一個帶符號類型,在 API 中偶爾用來傳回負的 Error Codes 以及畫格編號。特別是在 32 位元應用程式中,必須小心處理畫格編號溢位:一旦達到 FRAMEINDEX_MAX ,影像計數將會循環回 1!(在 64 位元應用程式中,即使是在極高的畫面速率下,影像 Counter 溢位也只會在數十萬年後才會發生。)

相比之下,緩衝區編號指的是影像資料所在的畫格緩衝區,對於 N 個緩衝區而言,其範圍介於 [1; N]。在此處使用的範例中,緩衝區編號介於 1 … 4 之間,且分別對應至 FB0 … FB3 。

The Standard Acquisition Model (ACQ_STANDARD)#
frameindex_t Fg_getLastPicNumberBlockingEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout,
    dma_mem * mem);
int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t frame);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    dma_mem * mem);

標準擷取模型是透過在呼叫 ACQ_STANDARD 設為 flags 中傳遞 Fg_AcquireEx()來選取。所有畫格緩衝區隨時都可供用於擷取影像資料的驅動程式以及用於處理影像資料的應用程式存取。

當擷取啟動時,驅動程式會在擷取卡中將至少一個畫格緩衝區排入佇列以進行影像資料傳輸,並從第一個畫格緩衝區 FB0開始。一旦影像完全傳輸至電腦記憶體,軟體便會收到傳輸完成的通知,且驅動程式將會多排入一個畫格緩衝區。擷取卡將繼續為佇列中的下一個緩衝區傳輸下一張影像的資料,在我們的範例中即為 FB1.

(排隊的圖框緩衝區數量取決於多種因素,例如所使用的擷取卡、驅動程式版本,以及 Windows 登錄中的設定或 Linux 驅動程式的參數。然而,在標準擷取模式中,排隊永遠會以線性方式進行,並且會從 FB0 在最後一個圖框緩衝區完成排隊後重新開始。雖然排隊的緩衝區數量可能會影響電腦中可達到的最高幀率,但這種影響只有在每秒超過約 10,000 幀時才能測量得到。)

由於驅動程式以一致的循環方式使用圖框緩衝區,因此可以使用以下關係將圖框編號簡單地轉換為緩衝區編號: bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers)中。函數 Fg_getImagePtrEx() 可用於取得指向圖框編號或緩衝區編號之圖框緩衝區的指標。

在標準擷取模型中,應用程式須負全責確保影像擷取不會「覆蓋」影像處理。唯有透過應用程式結合影像處理來控制影像來源觸發,或是仔細調校緩衝區數量以配合所使用電腦系統的幀率與效能,或是在能夠略過影像的情況下,才能確保這一點。

在某種情境中,當應用程式在前一個影像仍在處理時僅觸發單一影像,兩個緩衝區就足夠了:一個用於正在處理的影像資料,另一個用於傳輸影像資料。(如果應用程式能確保只在影像完全處理完畢後才產生觸發,實際上一個緩衝區就足以進行處理。然而,Framegrabber API 不支援此作法,在所有情況下應用程式都必須至少配置兩個緩衝區。)

在某種情境中,當影像持續產生而無須應用程式端控制時,影像來源與驅動程式中的緩衝區處理皆可視為自由運行。在以下範例中, FB0 進行明確的生命週期管理即呼叫 FB1 包含有效的影像資料。 FB0 正由應用程式進行處理, FB1 正等待處理。驅動程式已至少將 FB2 排隊,且目前正在傳輸影像資料。這種情況仍然安全,因為 FB3 尚未使用,即使它可能已經排隊。

記憶體緩衝區模型安全

呼叫函數 Fg_getLastPicNumberBlockingEx() 以取得處理 FB0 完成後的下一個圖框編號,在此情況下將傳回單一新圖框(傳輸至 FB1的圖框編號),前提是同時傳輸至 FB2 尚未完成。

如果影像處理速度比影像擷取慢,應用程式可能已經繼續處理 FB1。然而,在以下範例中,期間又傳輸了兩個影像 FB2 進行明確的生命週期管理即呼叫 FB3,且驅動程式已重新從 FB0開始。當影像傳輸完成時,驅動程式將會使用 FB1,因此擷取卡可能會覆蓋目前正在處理的資料。唯有當應用程式能確保在 FB1 仍在處理時,影像來源不會產生任何影像資料時,此情境才是安全的。

記憶體緩衝區模型不安全

呼叫函數 Fg_getLastPicNumberBlockingEx() 以取得處理 FB0 完成後將傳回兩個新畫面(傳輸至以下位置的畫面編號: FB3的圖框編號),前提是同時傳輸至 FB0 尚未完成。

以下範例擴充了寫入擷取迴圈 (Writing an Acquisition Loop)章節中提供的擷取迴圈結構,以處理所有畫面:

const int timeoutInSeconds = 10;

frameindex_t nextFrame = 1;
while (true) {
    // get new image
    const frameindex_t newestFrame =
        Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
                                      timeoutInSeconds, mem);

    if (newestFrame > 0) {
        for (frameindex_t frame = nextFrame; frame <= newestFrame; ++frame) {
                // get a pointer to the frame buffer for the newest image
                void * ptr =
                    Fg_getImagePtrEx(fg, frame, dma, mem);

            // get the actual number of bytes transferred
            size_t length = 0;
            int result =
                Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length,
                                  dma, mem, frame);

            if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
                    // process image data ...
            }
        }

        nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
    } else {
        // handle error ...
    }
}

如果應用程式可以跳過影像且只需要處理接收到的最新畫面,則可以從擷取迴圈中移除內層 for 迴圈,改為僅處理 newestFrame.

函數 Fg_getImageEx() 不應與標準擷取模型搭配使用。

在非同步模式下,Framegrabber API 將會呼叫 Fg_getLastPicNumberBlockingEx() 以等待新影像,並且如果旗標 FG_APC_BATCH_FRAMES 已設定,將為最新影像呼叫一次回呼函數;若未設定,則會針對接收到的每個影像各呼叫一次(可能會呼叫多次)。回呼將接收該影像的畫面編號。

若要在非同步模式下處理畫面,可以在回呼函數中使用內層迴圈的內容(ApcCallbackData 是註冊非同步模式的回呼函數 (Registering a Callback Function for Asynchronous Mode)章節中所述的結構):

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // get a pointer to the frame buffer for the newest image
        void * ptr =
            Fg_getImagePtrEx(context->fg, frame, context->dma, context->mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(context->fg, FG_TRANSFER_LEN, &length,
                              context->dma, context->mem, frame);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }
    } else {
        // handle error ...
    }

    return 0;
}

透過這種方式,當傳入 FG_APC_BATCH_FRAMES 時 flags 設為 Fg_registerApcHandlerEx(),回呼函數既可用於僅處理最新影像,也可用於處理所有影像。當未傳入 FG_APC_BATCH_FRAMES 時,將會針對每個新影像呼叫回呼函數一次(可能會呼叫多次)。

阻斷式擷取模型 (ACQ_BLOCK)#
frameindex_t Fg_getImageEx(
    Fg_Struct * fg,
    int strategy,
    frameindex_t frame,
    unsigned int dma,
    unsigned int timeout,
    dma_mem * mem);
int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t buffer);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);

frameindex_t Fg_getStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);
int Fg_setStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);

資訊

在阻斷式擷取模型中使用非同步模式時的行為 ACQ_BLOCK 的預設行為已在 Framegrabber API 5.9 版中變更。

透過傳入以下內容來選取阻斷式擷取模型 ACQ_BLOCK 設為 flags 中傳遞 Fg_AcquireEx()。每個畫面緩衝區只能由驅動程式專門用於擷取影像資料,或者排入佇列或被阻斷以供應用程式處理影像資料。只要畫面緩衝區處於被阻斷狀態,使用它們就始終是安全的,但當驅動程式遇到沒有更多未阻斷的緩衝區可用於影像資料傳輸的情況時,可能會遺失影像。

當擷取啟動時,驅動程式會在擷取卡中將至少一個畫格緩衝區排入佇列以進行影像資料傳輸,並從第一個畫格緩衝區 FB0。一旦影像完全傳輸至電腦記憶體,就會通知軟體傳輸已完成,畫面緩衝區將會為應用程式排入佇列,並且在應用程式明確或隱式解除阻斷之前,驅動程式將不會使用它。只要有一個或多個未阻斷的畫面緩衝區可用,驅動程式就會在擷取卡中多排入一個畫面緩衝區。如果沒有更多未阻斷的畫面緩衝區可用,則稱為 虛擬緩衝區 (dummy buffer) 的特殊畫面緩衝區將會排入擷取卡中,以保持影像擷取持續運作。擷取卡將繼續將下一個影像的資料傳輸到佇列中的下一個緩衝區,在我們的範例中,這將是 FB1.

(如標準擷取模型 (The Standard Acquisition Model)小節中所述,排入擷取卡佇列中的畫面緩衝區數量取決於各種因素。)

虛擬畫面緩衝區可以被認為是無限大的,並且可以吸收任意大小的資料,但對於處理影像而言沒有實質意義。因此,應用程式無法存取虛擬緩衝區,任何傳輸到虛擬緩衝區的影像都將遺失。由於每次傳輸畫面時,將下一個緩衝區排入佇列的操作都是自動發生的,這意味著如果沒有未阻斷的畫面緩衝區可供驅動程式用於排入佇列,則會自動至少遺失一個影像。即使資料來源停止,且在畫面緩衝區再次可用之後才重新啟動,情況也是如此!

在同步模式下,函數 Fg_getImageEx() 應該被呼叫,以請求並阻斷一個畫面緩衝區來存放新擷取的影像。該函數將傳回被阻斷畫面緩衝區的緩衝區編號。以下策略與阻斷式擷取模型相關:

策略 描述
SEL_NEXT_IMAGE 傳回下一畫面的緩衝區編號
此策略用於按照傳輸順序逐一處理每個單一畫面
SEL_ACT_IMAGE 傳回最新畫面的緩衝區編號
當影像處理速度比 Acquisition Frame Rate 慢時,此策略可用於略過畫面

上述清單並不完整,僅包含與阻斷式擷取模型相關的策略。

如果 SEL_NEXT_IMAGE 使用時,只有下一個畫面緩衝區會被阻斷,所有其他緩衝區仍會排隊等待應用程式在後續提出要求。若 SEL_ACT_IMAGE 使用時,只有最新的畫面緩衝區會被阻斷,所有其他排隊等待應用程式處理的畫面緩衝區將從佇列中移除並隱式解除阻斷。

當呼叫函式 Fg_getImageEx() 使用此模型時,應一律將 0 傳遞至 frame。此函式無法用於等待特定的畫面編號,或將畫面編號轉換為緩衝區編號。若要取得畫面緩衝區的畫面編號,函式 Fg_getParameterEx() 並傳入 FG_IMAGE_NUMBER 中的參數來重新啟動尋找 param一個指向型別變數的指標 frameindex_t 以取得參數 buffer.

函數 Fg_getStatusEx() 中的畫面編號與緩衝區編號,可呼叫下列與阻斷式擷取模型相關的狀態資訊:

Status 描述
NUMBER_OF_LOST_IMAGES 遺失的畫面數量
NUMBER_OF_BLOCKED_IMAGES 目前被阻斷的畫面數量
NUMBER_OF_IMAGES_IN_PROGRESS 透過以下方式取得的畫面數量 Fg_getImageEx()
BUFFER_STATUS 0:畫面緩衝區未被阻斷
1:畫面緩衝區已被阻斷

函數 Fg_setStatusEx() 可在先前呼叫並要求阻斷畫面緩衝區之後,呼叫此函式來解除阻斷畫面緩衝區 Fg_getImageEx():

Status 描述
FG_UNBLOCK 解除阻斷單一畫面緩衝區
FG_UNBLOCK_ALL 解除阻斷目前所有被阻斷的緩衝區

FG_UNBLOCK_ALL 也將移除任何仍在佇列中等待應用程式處理、但尚未被要求與阻斷的緩衝區。

當使用函式 Fg_AcquireEx() 若要指定擷取的影像數量,遺失的影像以及成功傳送的影像都會被計算在內。這可能會導致在呼叫 FG_TIMEOUT_ERR 或 FG_TRANSFER_NOT_ACTIVE 時發生非預期的 Fg_getImageEx() 結果,且在擷取迴圈中僅計算已傳送的影像。

在應用程式僅觸發單一影像,而前一個影像仍在處理中的情境下,兩個緩衝區就已足夠:一個用於正在處理的影像資料,另一個用於傳送影像資料。(由於會進行緩衝區鎖定,即使應用程式確保只有在影像完全處理完畢後才產生觸發,在所有情況下至少仍需要兩個緩衝區。)

在應用程式端未加控制、持續產生影像的情境下,影像來源可被視為自由運行 (free running)。然而,驅動程式僅能在仍有未封鎖的可用緩衝區時,才能將影像傳送給應用程式。在以下範例中, FB0 進行明確的生命週期管理即呼叫 FB1 包含有效的影像資料。 FB0 正由應用程式進行處理, FB1 正等待處理。驅動程式已至少將 FB2 排隊,且目前正在傳輸影像資料。這種情況仍然安全,因為 FB3 尚未使用,即使它可能已經排隊。

記憶體緩衝區模型安全

如果影像處理速度比影像擷取慢,應用程式可能已經完成處理 FB0,將其解除封鎖並繼續處理 FB1。然而,在以下範例中,期間又傳輸了兩個影像 FB2 進行明確的生命週期管理即呼叫 FB3,且驅動程式已重新從 FB0。當影像傳送完成時,若應用程式尚未完成處理 FB1 並將其解除封鎖,驅動程式將沒有更多可用於排入佇列的免費緩衝區。虛擬緩衝區 (dummy buffer) 將被排入佇列,擷取過程中至少會遺失一個影像,但尚未解除封鎖的影格緩衝區之資料完整性將得以維持。

記憶體緩衝區模型不安全

以下範例擴充了寫入擷取迴圈 (Writing an Acquisition Loop)章節中提供的擷取迴圈結構,以處理所有畫面:

const int timeoutInSeconds = 10;
while (true) {
    // get new image
    frameindex_t buffer =
        Fg_getImageEx(fg, SEL_NEXT_IMAGE, 0, dma, timeoutInSeconds, mem);

    if (buffer > 0)
        // get the frame number (if needed)
        uint64_t frame = 0;
        Fg_getParameterEx(fg, FG_IMAGE_NUMBER, &frame, dma, mem, buffer);

        // get a pointer to the frame buffer for the newest image
        void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, buffer);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }

        // unblock frame buffer
        Fg_setStatusEx(fg, FG_UNBLOCK, buffer, dma, mem);
    } else {
        // handle error ...
    }
}

函數 Fg_getLastPicNumberBlockingEx() 不應與封鎖式擷取模型搭配使用。

在非同步模式下,Framegrabber API 將會呼叫 Fg_getImageEx() 使用 SEL_ACT_IMAGE 如果旗標 FG_APC_BATCH_FRAMES 已設定,或者使用 SEL_NEXT_IMAGE (若未設定)。回呼函數將接收影像的緩衝區編號。c

若要在非同步模式下處理影格,可以在回呼函數中使用 if 條件句(ApcUserCallbackData 是如註冊非同步模式的回呼函數一節中所述的結構):

int ApcUserCallback(frameindex_t buffer, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (buffer > 0) {
        // get the frame number (if needed)
        uint64_t frame = 0;
        Fg_getParameterEx(fg, FG_IMAGE_NUMBER, &frame, dma, mem, buffer);

        // get a pointer to the frame buffer for the newest image
        void * ptr =
            Fg_getImagePtrEx(context->fg, buffer, context->dma, context->mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(context->fg, FG_TRANSFER_LEN, &length,
                              context->dma, context->mem, buffer);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }

        // unblock frame buffer
        Fg_setStatusEx(fg, FG_UNBLOCK, buffer, dma, mem);
    } else {
        // handle error ...
    }

    return 0;
}

透過這種方式,當傳入 FG_APC_BATCH_FRAMES 時 flags 設為 Fg_registerApcHandlerEx(),回呼函數既可用於僅處理最新影像,也可用於處理所有影像。當未傳入 FG_APC_BATCH_FRAMES 時,將會針對每個新影像呼叫回呼函數一次(可能會呼叫多次)。

選擇性擷取模型 ACQ_SELECT#
frameindex_t Fg_getLastPicNumberBlockingEx(
    Fg_Struct * fg,
    frameindex_t frame,
    unsigned int dma,
    int timeout,
    dma_mem * mem);
int Fg_getParameterEx(
    Fg_Struct * fg,
    int param,
    void * value,
    unsigned int dma,
    dma_mem * mem,
    frameindex_t frame);

void * Fg_getImagePtrEx(
    Fg_Struct * fg,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);
int Fg_setStatusEx(
    Fg_Struct * fg,
    int status,
    frameindex_t buffer,
    unsigned int dma,
    dma_mem * mem);

資訊

選擇性採集模型 ACQ_SELECT 是在 Framegrabber SDK 5.9 版中新增的。

透過傳遞 ACQ_SELECT 設為 flags 中傳遞 Fg_AcquireEx()來選擇選擇性擷取模型。應用程式可完全掌控驅動程式對影格緩衝區的使用。

影格緩衝區必須明確選定以便在擷取卡 (frame grabber) 中排入佇列,且驅動程式將嚴格按照選定的順序將影格緩衝區排入佇列。當擷取開始時,只有在選定了任何影格緩衝區的情況下,驅動程式才會將影格緩衝區排入擷取卡中進行影像資料傳輸,並從第一個被選定的影格緩衝區開始。一旦影像完全傳送至電腦記憶體,系統就會通知軟體傳送已完成,該影格緩衝區不再被視為已選定,且驅動程式將不會使用它,直到再次明確選定為止。如果有可用緩衝區,驅動程式將會多排入一個或多個影格緩衝區。如果沒有更多可用的影格緩衝區,則不會有任何影格緩衝區排入佇列。擷取卡將繼續將下一個影像的資料傳輸到佇列中的下一個緩衝區。如果擷取卡中的佇列變空,可能會導致內部緩衝區溢位並遺失影像。

(如標準擷取模型 (The Standard Acquisition Model)小節中所述,排入擷取卡佇列中的畫面緩衝區數量取決於各種因素。)

由於應用程式完全掌控影格緩衝區的使用,因此追蹤緩衝區編號也是應用程式的責任。除非始終以相同的順序選定影格緩衝區,否則沒有簡單直覺的方法可以將影格編號轉換為緩衝區編號。此外,也沒有執行此轉換的 API 函數。

函數 Fg_setStatusEx() 可以透過使用 FG_SELECT_BUFFER 並傳遞緩衝區編號來呼叫以選定影格緩衝區。

為求簡單起見,在以下範例中,我們假設在開始擷取後,所有四個緩衝區 FB0 … FB3 均按自然順序被選定。此外,影像傳送後會進行處理,並依序再次選定緩衝區。透過這種方式,緩衝區的順序將始終保持不變,且影格編號可以透過以下關係直覺地轉換為緩衝區編號: bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers).

在應用程式端未加控制、持續產生影像的情境下,影像來源可被視為自由運行 (free running)。然而,驅動程式僅能在仍有可用且已選定緩衝區的情況下,將影像傳送給應用程式。在以下範例中, FB0 進行明確的生命週期管理即呼叫 FB1 包含有效的影像資料且未被選定。 FB0 正由應用程式進行處理, FB1 正等待處理。驅動程式已至少將 FB2 排隊,且目前正在傳輸影像資料。這種情況仍然安全,因為 FB3 尚未使用,即使它可能已經排隊。

記憶體緩衝區模型安全

呼叫函數 Fg_getLastPicNumberBlockingEx() 以取得處理 FB0 完成後的下一個圖框編號,在此情況下將傳回單一新圖框(傳輸至 FB1的圖框編號),前提是同時傳輸至 FB2 尚未完成。

如果影像處理速度比影像擷取慢,應用程式可能已經完成處理 FB0,並將其選取以進行後續處理 FB1。然而,在以下範例中,期間又傳輸了兩個影像 FB2 進行明確的生命週期管理即呼叫 FB3,且驅動程式已重新從 FB0。當影像傳送完成時,若應用程式尚未完成處理 FB1 且被選取時,驅動程式將不再有可用於排隊的可用緩衝區。只要沒有新的影像到達,這種情況就不會像在以下情況中那樣導致相同的隱含影像遺失 ACQ_BLOCK中。但是,一旦接收到另一個影像,擷取卡中的內部記憶體緩衝區就可能會發生溢位狀況,並可能導致資料遺失。

記憶體緩衝區模型不安全

以下範例擴充了寫入擷取迴圈 (Writing an Acquisition Loop)章節中提供的擷取迴圈結構,以處理所有畫面:

const frameindex_t numberOfBuffers = 4;
const int timeoutInSeconds = 10;

frameindex_t nextFrame = 1;
while (true) {
    // get new image
    const frameindex_t newestFrame =
        Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
                                      timeoutInSeconds, mem);

    if (newestFrame > 0) {
        for (frameindex_t frame = nextFrame; frame <= newestFrame; ++frame) {
            // get the buffer number
            frameindex_t buffer = 1 + ((frame - 1) % numberOfBuffers);

            // get a pointer to the frame buffer for the newest image
            void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);

            // get the actual number of bytes transferred
            size_t length = 0;
            int result =
                Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length,
                                  dma, mem, buffer);

            if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
                // process image data ...
            }

            // select frame buffer
            Fg_setStatusEx(fg, FG_SELECT_BUFFER, buffer, dma, mem);
        }

        nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
    } else {
        // handle error ...
    }
}

如果應用程式可以跳過影像且只需處理接收到的最新畫格, Fg_setStatusEx() 必須搭配 FG_SELECT_BUFFER 來使用,以選取被跳過的緩衝區。否則,應用程式將會留下未使用的緩衝區。

函數 Fg_getImageEx() 不應與 selective acquisition model 搭配使用。

在非同步模式下,Framegrabber API 將會呼叫 Fg_getLastPicNumberBlockingEx() 以等待新影像,並且如果旗標 FG_APC_BATCH_FRAMES 已設定,將為最新影像呼叫一次回呼函數;若未設定,則會針對接收到的每個影像各呼叫一次(可能會呼叫多次)。回呼將接收該影像的畫面編號。

若要以非同步模式處理畫格,內部迴圈的內容可用於回呼函數(ApcUserCallbackData 是如註冊非同步模式的回呼函數一節中所述的結構):

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // get the buffer number
        frameindex_t buffer = 1 + ((frame - 1) % numberOfBuffers);

        // get a pointer to the frame buffer for the newest image
        void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);

        // get the actual number of bytes transferred
        size_t length = 0;
        int result =
            Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, buffer);

        if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
            // process image data ...
        }

        // select frame buffer
        Fg_setStatusEx(fg, FG_SELECT_BUFFER, buffer, dma, mem);
    } else {
        // handle error ...
    }

    return 0;
}

透過這種方式,當傳入 FG_APC_BATCH_FRAMES 時 flags 設為 Fg_registerApcHandlerEx(),回呼函數既可用於僅處理最新影像,也可用於處理所有影像。當未傳入 FG_APC_BATCH_FRAMES 時,將會針對每個新影像呼叫回呼函數一次(可能會呼叫多次)。

將資料傳輸至擷取卡#

限制

本節描述使用 VisualApplets operator 的應用程式的新 API DmaFromPC。該 API 具有以下限制:

  • 這是一個初步的功能預覽。這意味著函數名稱和功能可能會在未來版本的 Framegrabber SDK 中變更。
  • 它僅適用於包含 VisualApplets operator 的 applet DmaFromPC。請勿將此 API 用於使用以下項目的常規 DMA 通道: DmaToPC 或隨 Framegrabber SDK 一起提供的 Advanced Acquisition Applets。此 operator DmaFromPC 適用於 VisualApplets 3.4.0 或更高版本。
  • 目前僅在 Windows 上實作。

透過 VisualApplets operator DmaFromPC,可以將資料從 PC 傳輸到擷取卡。應用程式包括但不限於共同處理影像資料,或為擷取卡上的複雜影像處理提供額外的參數。

配置記憶體#

用於傳輸資料的緩衝區必須使用進階記憶體管理中所記錄的函數來配置,例如:

const size_t bufferSize = 1024 * 1024;
const size_t numBuffers = 16;
const size_t totalSize = bufferSize * numBuffers;

dma_mem * mem = Fg_AllocMemEx(fg, totalSize, numBuffers);
if (mem != nullptr) {
    // use memory, send buffers ...

    Fg_FreeMem(fg, mem);
}

或者,您可以使用彈性記憶體管理中所記錄的函數,例如:

dma_mem * mem = Fg_AllocMemHead(fg, totalSize, numBuffers);
for (int i = 0; i < numBuffers; i++) {
    auto buffer = new uint8_t[bufferSize];
    Fg_AddMem(fg, buffer, bufferSize, i, mem);
}

雖然所有緩衝區的大小都相同,但每次單一傳輸的位元組數是個別指定的。資料傳輸的大小必須是 operator 之 Parallelism 的倍數 DmaFromPc(即 32),而上限則是緩衝區的大小。

開始資料傳輸#

 int Fg_startBufferQueue(
    Fg_Struct * fg,
    uint32_t dma,
    dma_mem * mem);

int Fg_stopBufferQueue(
    Fg_Struct * fg,
    uint32_t dma,
    int32_t flags);

若要開始將資料傳輸至擷取卡,請呼叫函數 Fg_startBufferQueue()。此呼叫將會繫結 DMA 通道 dma 繫結至 Memory 參照碼 mem ,因此將會使用為資料傳輸所配置的記憶體。

參數 dma 至此函數 Fg_startBufferQueue() 取決於使用 VisualApplets operator 用於將影像傳輸至 PC 的常規 DMA 通道數量 DmaToPC 存在於 VisualApplets 設計中。用於 VisualApplets operator 的 DMA 通道 DmaFromPC 始終是最後一個 DMA 通道。

在所有資料傳輸完畢後,呼叫 Fg_stopBufferQueue() 會停止緩衝區佇列,並切斷 DMA 通道與所使用記憶體代handle之間的聯繫。

為資料傳輸將緩衝區排隊#

int Fg_queueBuffer(
    Fg_Struct * fg,
    frameindex_t buffer,
    uint64_t numBytesToTransfer,
    uint32_t dma,
    dma_mem * mem);

一旦要傳輸到畫面擷取卡的資料已寫入緩衝區且緩衝區已準備好傳輸,您可以透過呼叫以下指令將緩衝區放入緩衝區佇列中 Fg_queueBuffer()。對於您放入佇列中的每個緩衝區,您必須指定要傳輸的位元組數。

您最多可以在緩衝區佇列中放入 16 個緩衝區。佇列中的下一個緩衝區將由驅動程式自動傳輸到畫面擷取卡。透過這種方式,可以在需要時確保向畫面擷取卡進行連續的資料串流傳輸。

參數 mem 僅在啟動緩衝區佇列之前將緩衝區排入佇列時才是必要的。在 Fg_startBufferQueue() 被呼叫後,參數 mem 是選用的,並且可以 NULL.

等待緩衝區傳輸完成#

int Fg_waitForBuffers(
    Fg_Struct * fg,
    uint32_t dma,
    uint64_t timeout,
    void * /* reserved */,
    size_t /* reserved */);

函數 Fg_waitForBuffers() 等待至少一個緩衝區在參數中給定的秒數內完全傳輸 timeout 到達參數中所指定的給定 DMA 通道 dma。如果成功,該函數會傳回完全傳輸的緩衝區數量;如果失敗,則傳回負的錯誤代碼。

您只能重複使用透過以下方式排入佇列的緩衝區 Fg_queueBuffer() 並且一旦緩衝區完全傳輸完畢,就用新資料Fill它們。

適用於 VisualApplets operator 的資料傳輸模型 DmaFromPC 非常類似於 The Selective Acquisition Model。這意味著由於應用程式完全控制緩衝區的使用,追蹤緩衝區編號也是應用程式的責任。除非緩衝區始終以相同的順序選擇,否則沒有簡單的方法將傳輸 Counter 轉換為緩衝區編號。而且也沒有執行此轉換的 API 函數。

Applet 事件#

Framegrabber SDK 安裝中包含的 applets 可以向應用程式發送有關畫面擷取卡中影像擷取或處理相關各種事件的通知。此類事件的範例包括畫面開始事件(start of frame events)或畫面結束事件(end of frame events),這些事件是在從相機接收到影像的第一個或最後一個像素時產生的,或者是觸發輸入上升事件(trigger input rising events)或觸發輸入下降事件(trigger input falling events),這些事件是在偵測到觸發輸入訊號邊緣時產生的。VisualApplets 的使用者可以在其設計中使用事件 operator,根據其應用程式的需求產生事件。

每個事件來源都有一個唯一的名稱,並由無號 64 位元整數的單一位元識別,稱為事件遮罩(event mask)。透過這種方式,可以透過在事件遮罩中設定多個位元將多個事件來源群組在一起,例如透過使用二進位 or operator | 針對多個事件來源的多個事件遮罩。然而,這也意味著任何 applet 最多只能支援 64 個事件來源。

事件可以以同步或非同步模式傳遞。在同步模式下,應用程式必須提供事件迴圈。在非同步模式下,可以註冊一個回呼函數,只要發生一個或多個事件,該函數就會被呼叫。這兩種方法不應在應用程式中混合使用,因為這可能會導致資源衝突。

由於事件來源可能會對電腦系統產生高插斷負載,因此必須明確啟用每個事件來源,並且僅應在需要時才進行啟動。

針對每次發生的事件,系統會記錄接收到事件來源中斷的時間。除了時間戳記之外,某些事件來源還可以在每個事件中產生額外資料,稱為事件酬載 (event payload)。具有酬載的事件無法透過為多個事件來源設定多個位元來進行群組。請參閱您所使用的任何 applet 的說明文件,以了解 applet 中的事件來源是否會產生額外資料,以及如何解讀該資料。

事件資訊#

uint64_t Fg_getEventMask(
    Fg_Struct * fg,
    const char * name);

int Fg_getEventPayload(
    Fg_Struct * fg,
    uint64_t mask);

int Fg_getEventCount(
    Fg_Struct * fg);

const char * Fg_getEventName(
    Fg_Struct * fg,
    uint64_t mask);

如果已知事件來源的名稱,則可以透過呼叫函數來請求對應的位元 Fg_getEventMask()。如果無法識別事件名稱,該函數將傳回 0。

任何給定事件的酬載大小都可以透過呼叫函數來請求 Fg_getEventPayload()。如果事件遮罩對應至單一有效的事件來源,該函數將傳回大於或等於零的值,否則傳回負的錯誤代碼。

若要走訪所有事件來源,可以呼叫函數 Fg_getEventCount() 以請求 applet 支援的事件來源數量,並使用函數 Fg_getEventName() 來取得事件來源的名稱。以下範例顯示了此作法:

int numEvents = Fg_getEventCount(fg);
for (int event = 0; event < numEvents; ++event) {
    // get the event mask for each event
    const uint64_t eventMask = (1 << event);

    const char * eventName = Fg_getEventName(fg, eventMask);
    if (eventName != nullptr) {
        std::cout << "Event " << event << " is " << eventName << std::endl;
    }
}

為非同步事件處理註冊回呼函數#

typedef int (* Fg_EventFunc_t)(
    uint64_t events,
    void * data,
    const struct fg_event_info * info);

int Fg_registerEventCallback(
    Fg_Struct * fg,
    uint64_t mask,
    Fg_EventFunc_t handler,
    void * data,
    unsigned int flags,
    struct fg_event_info * info);

int Fg_unregisterEventCallback(
    Fg_Struct * fg,
    uint64_t mask);

資訊

在 Framegrabber SDK 5.9 版中,類型 struct fg_event_data 已被移除,並新增了函數 Fg_unregisterEventCallback() 。請參閱章節 在 Plain C 中取消註冊非同步事件處理的回呼函數 以了解舊的介面。

要 Fg_registerEventCallback(),類型為 Fg_EventFunc_t 可以進行註冊,以便在接收到來自一組事件來源的一個或該多個事件時被回呼叫。該函數將針對指定的 frame grabber 和事件遮罩進行註冊,並在呼叫時傳入三個參數。回呼函數的第一個參數是接收到事件的所有事件來源的事件遮罩。第二個參數是隨同呼叫一起提供的指標 Fg_registerEventCallback() 並且可以用作指向上下文結構或類別的指標,例如 this 實作影像處理的類別實例指標。第三個參數是指向 struct fg_event_info 該變數必須由應用程式進行分配,並在呼叫時傳入 Fg_registerEventCallback() 時傳入,且用於儲存有關事件的資訊。

在任何指定的 frame grabber 上,只能為同一組事件註冊一個回呼函數。

回呼函數將從 Framegrabber API 提供的事件迴圈中呼叫。這意味著回呼函數是在事件迴圈的執行緒內容中被呼叫的。這也意味著回呼函數返回所需的時間會增加事件迴圈的一般管理開銷,並且在此期間可能會接收到多個事件。如果在該期間接收到任何事件,回呼函數將在其返回後立即再次被呼叫。

透過參數 flags 中傳遞 Fg_registerEventCallback(),應用程式可以選擇每次呼叫回呼函數時是僅傳遞單一事件,還是將所有擱置事件分組在一起。傳遞時的預設行為 FG_EVENT_DEFAULT_FLAGS 傳遞給參數 flags 是在每次呼叫回呼函數時傳遞一個事件。透過傳遞 FG_EVENT_BATCHED ,所有擱置事件將被分組在一起。

當不再需要回呼函式時,可以透過呼叫取消註冊 Fg_unregisterEventCallback().

以下範例顯示如何使用簡單的結構來註冊回呼函數,該結構包含處理影像擷取所需的資訊:

struct EventUserCallbackData
{
    Fg_Struct * fg;
    fg_event_info info;
    uint64_t mask;
};

int EventUserCallback(uint64_t events, void * data,
                      const struct fg_event_info * info)
{
    auto context = reinterpret_cast<EventUserCallbackData *>(data);

    // process events

    return 0;
}

void SetupEventUserCallback(EventUserCallbackData * context)
{
    // register callback function
    int result = Fg_registerEventCallback(
        context->fg, context->mask, &EventUserCallback, context,
        FG_EVENT_DEFAULT_FLAGS, &context->info);
    if (result != FG_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

範例中未顯示上下文結構的配置和管理。當回呼函式保持註冊狀態時,該指標必須有效。其中一種解決方案是將所有內容保存在 C++ 類別中。若要在 C++ 類別上下文中使用回呼函式,可以使用靜態函式來註冊回呼處理常式,並且 this 指標應用作上下文資料指標,該指標可以轉換回類別指標並相應地使用。

同步等待事件#

uint64_t Fg_eventWait(
    Fg_Struct * fg,
    uint64_t mask,
    unsigned int timeout,
    unsigned int flags,
    struct fg_event_info * info);

應用程式可以透過呼叫同步等待來自一組事件來源的事件 Fg_eventWait()。參數 flags 允許選擇每次呼叫該函數時是僅傳回單一事件,還是將所有擱置事件分組在一起。傳遞時的預設行為 FG_EVENT_DEFAULT_FLAGS 傳遞給參數 flags 是在每次呼叫該函數時最多傳回一個事件。透過傳遞 FG_EVENT_BATCHED ,所有擱置事件將被分組在一起。該函數將會等待,直到參數中指定的任何事件來源接收到事件為止 mask,或是參數中指定的秒數 timeout 已過期。如果在逾時發生之前未收到任何事件,則函數將傳回 0,否則將傳回包含所接收事件或事件群組的遮罩。

啟用事件#

int Fg_activateEvents(
    Fg_Struct * fg,
    uint64_t mask,
    int enable);

事件來源在傳送事件之前必須先啟用。透過呼叫 Fg_activateEvents() 可以啟用或停用一組事件來源。如果將 1 傳遞給參數 enable,則參數中傳遞的事件來源群組 mask 將會啟用。如果將 0 傳遞給參數 enable,則會將其停用。一旦應用程式不再使用所有事件來源,就應該將其停用。

由於事件來源可以在啟用後的任何時間觸發事件,因此建議僅在滿足以下條件之一後才啟動事件來源:已使用以下項目為其註冊回呼 Fg_registerEventCallback(),或者獨立執行緒已準備就緒並使用以下項目等待事件 Fg_eventWait().

支援從多個程序使用擷取卡#

某些應用程式可能需要從不同的處理程序存取單一擷取卡。一個簡單的範例是用於監控另一個處理程序(例如擷取處理程序)活動的處理程序。另一個範例可能是多個獨立的擷取處理程序,每個處理程序使用連接有多達四個相機的擷取卡之其中一個相機。

Framegrabber API 透過主從模式對此類情境提供有限的支援。使用主從處理程序時,了解其限制非常重要。同樣重要的是,要了解當兩個或多個處理程序必須同步時,應用程式開發人員所面臨的通用挑戰。雖然 Framegrabber API 支援從多個處理程序使用擷取卡,但除了在初始化擷取卡時同步處理程序啟動的非常基本功能外,它並不支援處理程序間同步。對於通用的處理程序間通訊與同步,必須使用其他作業系統功能或支援庫。

主從模式的一項特定限制是,每當兩個處理程序嘗試控制擷取卡上的相同功能時,最後一個處理程序會「獲勝」並覆寫第一個處理程序的動作。第一個處理程序甚至可能不會注意到此覆寫,並對擷取卡的狀態做出錯誤的假設。(存在一項實驗性功能,可使用處理程序間通訊來同步處理程序之間的狀態,然而,此方法有其自身的影響,將在實驗性參數與擷取同步支援一節中討論。)

一般的建議是設計處理程序,使其承擔獨特的任務(相對於 Applet 參數和功能而言,這些任務是互斥的),並且不需要知道其他處理程序的存在。唯一的例外是監控處理程序,它僅存取 Applet 的唯讀功能,或透過處理程序間通訊方式從其他處理程序收集資訊。

另一項限制是,雖然只有主處理程序會完整初始化擷取卡,但每個處理程序都必須進行基本初始化。如果先前的處理程序已經開始影像擷取或相機探索,則此基本初始化可能會干擾先前的處理程序。

在初始化期間同步處理程序#

int Fg_InitLibrariesEx(
    const char * path,
    unsigned int flags,
    const char * id,
    unsigned int timeout);

void Fg_AbortInitLibraries();

void Fg_InitLibrariesStartNextSlave();
Fg_Struct * Fg_InitEx(
    const char * applet,
    unsigned int board,
    int flags);

Fg_Struct * Fg_InitConfigEx(
    const char * config,
    unsigned int board,
    int flags);

當同步多個處理程序時,由 Framegrabber API 提供的同步點是對以下項目的呼叫 Fg_InitLibrariesEx()。如同 Fg_InitLibraries(),對該呼叫的第一個參數不會被使用,且應用程式應一律傳遞 nullptr.

要同步的處理程序會形成一個群組,且同步將僅考慮群組內的處理程序。應同步的處理程序群組是透過參數來識別 id。透過這種方式,複雜的應用程式可以有多個獨立的同步群組。識別群組的字串不應為空,且應僅由所用作業系統中檔案名稱允許的字元組成。應用程式所使用的識別碼不建議以 siso- 開頭,前綴詞 siso- 應被視為保留供 Framegrabber API 使用。

同步行為可以透過參數進行設定 flags。下列值與巨集可用於參數 flags:

旗標 描述
FG_INIT_LIBRARIES_SINGLE 預設初始化,無同步
FG_INIT_LIBRARIES_MASTER 主端初始化,準備從端同步
FG_INIT_LIBRARIES_SLAVE 從端初始化,等待主端
FG_INIT_LIBRARIES_SEQUENTIAL 嚴格依序啟動
FG_INIT_LIBRARIES_AUTOSTART_ON_INIT 在擷取卡初始化時啟動下一個從端
FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY(n) 設定從端優先順序
FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES(n) 設定從端數量

要 FG_INIT_LIBRARIES_SINGLE 與呼叫相同 Fg_InitLibraries().

要 FG_INIT_LIBRARIES_SEQUENTIAL 將僅從主端處理程序啟動具有最高優先順序的從端處理程序。具有第二高優先順序的從端處理程序必須從具有最高優先順序的從端處理程序啟動,依此類推。這確保了處理程序初始化的嚴格順序。如果 FG_INIT_LIBRARIES_SEQUENTIAL 用於同步群組中,則必須在所有處理程序(包括主端處理程序)中指定。巨集 FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY() 當使用 FG_INIT_LIBRARIES_SEQUENTIAL 時,必須在所有從端處理程序中使用,並且定義從端的優先順序。最高優先順序為 1,最低優先順序為 63。群組中的每個處理程序必須具有唯一的優先順序,且指派優先順序時不得留有空隙。(如果處理程序 2 想要啟動處理程序 3,但沒有處理程序 3,則沒有人會啟動處理程序 4。)巨集 FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES() 當使用 FG_INIT_LIBRARIES_SEQUENTIAL 時,必須在主端處理程序中使用,並且定義從端處理程序的數量。允許的最小值為 1,最大值為 63。

呼叫中的最後一個參數 Fg_InitLibrariesEx() 指定等待處理程序排程以向應用程式回報錯誤的時間(以毫秒為單位)。

當呼叫 Fg_InitLibrariesEx() 時,通常稱為主端處理程序的群組第一個處理程序會建立一個群組。所有後續處理程序(通常稱為從端處理程序)可以選擇在對應的呼叫中等待 Fg_InitLibrariesEx() 以進行排程。然後,透過在主端處理程序中呼叫 Fg_InitLibrariesStartNextSlave() 來觸發啟動一個或多個從端。此函數 Fg_InitLibrariesStartNextSlave() 可以在程序通過調用來初始化擷取卡時自動調用 Fg_Init(), Fg_InitEx(), Fg_InitConfig() 或 Fg_InitConfigEx().

雖然該函數 Fg_InitLibraries() 在正常情況下絕不應該失敗,但 Fg_InitLibrariesEx() 在使用同步時則不然。請合理指定等待時間並相應地處理錯誤。

在多執行緒應用程序中,對 Fg_InitLibrariesEx() 的調用等待可以通過從不同執行緒調用 Fg_AbortInitLibraries()來中止。這將導致被中止的調用回報錯誤。

在主程序中,對 Fg_Init() 或 Fg_InitConfig() 的調可用於初始化擷取卡,而所有從程序必須調用 Fg_InitEx() 或 Fg_InitConfigEx() 並傳遞 FG_INIT_FLAG_SLAVE 中的參數來重新啟動尋找 flags之ㄧ。調用 Fg_InitEx() 或 Fg_InitConfigEx() 與 FG_INIT_FLAG_DEFAULT 與呼叫相同 Fg_Init() 或 Fg_InitConfig()分別對應。

以下範例展示了主程序如何使用兩個從程序來為嚴格循序排程設置同步群組。主程序使用對 Fg_InitLibrariesStartNextSlave() 的調用,在進行一些進一步的初始化後啟動第一個從程序:

const char * syncGroupId = "example";

int result =
    Fg_InitLibrariesEx(
        nullptr,
        FG_INIT_LIBRARIES_MASTER
            | FG_INIT_LIBRARIES_SEQUENTIAL
            | FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES(2),
        syncGroupId,
        0);

if (result == FG_OK) {
    const char * applet = "Acq_SingleCXP12Area";

    Fg_Struct * fg = Fg_Init(applet, 0);
    if (fg != nullptr) {
        // further initialization ...

        Fg_InitLibrariesStartNextSlave();

        // use frame grabber ...
    }

    Fg_FreeLibraries();
}

範例中的從程序除了在調用中指定的優先級之外完全相同 Fg_InitLibrariesEx(),此處僅顯示第一個從程序。從程序使用 FG_INIT_LIBRARIES_AUTOSTART_ON_INIT 在調用 Fg_InitEx() 時隱式啟動下一個從程序:

const char * syncGroupId = "example";

int result =
    Fg_InitLibrariesEx(
        nullptr,
        FG_INIT_LIBRARIES_SLAVE
            | FG_INIT_LIBRARIES_SEQUENTIAL
            | FG_INIT_LIBRARIES_AUTOSTART_ON_INIT
            | FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY(1),
        syncGroupId,
        10000);

if (result == FG_OK) {
    const char * applet = "Acq_SingleCXP12Area";

    Fg_Struct * fg = Fg_InitEx(applet, 0, FG_INIT_FLAG_SLAVE);
    if (fg != nullptr) {
        // use frame grabber ...
    }

    Fg_FreeLibraries();
}

如果從程序在主程序之前啟動,它將在對 Fg_InitLibrariesEx() 的調用中等待主程序啟動。一旦主程序調用 Fg_InitLibrariesStartNextSlave() ,從程序即被排程執行,且對 Fg_InitLibrariesEx() 傳回 FG_OK的調用。如果主程序未啟動,或者在從程序等待時間耗盡之前未完成初始化,則從程序無法被排程執行,並且對 Fg_InitLibrariesEx() 的調用將返回超時錯誤。

隨 Framegrabber SDK 安裝的 microDisplay X 工具支援主/從同步,從程序可以使用群組識別碼與 microDisplay X 同步 siso-microdisplay-master。旗標 FG_INIT_LIBRARIES_SEQUENTIAL 不受 microDisplay X 支援。

對參數和擷取同步的實驗性支援#

資訊

對參數與擷取同步的實驗性支援是在 Framegrabber SDK 5.9 版本中加入的。

Framegrabber API 提供實驗性支援,可在處理程序同步群組中同步所有參數變更與擷取狀態。若要啟用參數與擷取同步, FG_INIT_FLAG_PARAM_SYNC 必須新增至參數 flags 中傳遞 Fg_InitEx() 或 Fg_InitConfigEx() 以用於群組中的所有處理程序。

不過,在使用參數與擷取同步時,有幾點需要考量。

首先,Framegrabber API 所提供的同步功能會使用作業系統所提供的處理程序間通訊機制。由於取得與設定參數不再於處理程序中本機執行,而是必須在多個處理程序之間同步,因此這些作業的延遲與抖動都會增加。對於大多數應用程式而言,這些缺點應該沒有影響。不過,如果應用程式使用即時作業系統,且取得或設定一個或多個參數被視為具即時關鍵性,則不應啟用參數與擷取同步。如果 FG_INIT_FLAG_PARAM_SYNC 未明確使用,則不會啟用參數與擷取同步,且相較於舊版的 Framegrabber API,此實驗性功能對效能沒有影響。

其次,在使用同步時,主控或從屬處理程序皆可控制擷取,但絕對不能兩者同時控制。在同步群組中,主控處理程序中的擷取不得與一個或多個從屬處理程序中的擷取混合使用。在大多數情況下,擷取應在一個或多個從屬處理程序中執行,且 FG_INIT_FLAG_ACQUISITION_SLAVE 應新增至參數 flags 中傳遞 Fg_InitEx() 或 Fg_InitConfigEx() 以用於主控處理程序。

支援非統一記憶體存取#

int Fg_NumaPinThread(
    Fg_Struct * fg);

void * Fg_NumaAllocDmaBuffer(
    Fg_Struct * fg,
    size_t size);

int Fg_NumaFreeDmaBuffer(
    Fg_Struct * fg,
    void * ptr);

在使用非一致性記憶體存取 (NUMA) 的多處理器電腦中,每個實體處理器都有自己的記憶體與周邊匯流排。只要處理程序只存取其執行所在之同一實體處理器上的資源,存取速度幾乎與單處理器電腦一樣快,且比使用對稱多處理 (SMP) 的多處理器電腦快得多。不過,如果執行於一個實體處理器的處理程序想要存取屬於另一個實體處理器的資源,則必須在兩個實體處理器之間移動資料,從而降低存取速度。如需詳細資訊,請參閱維基百科上關於 非一致性記憶體存取 的文章。

為了支援在 NUMA 電腦上執行的應用程式,所有存取擷取卡的執行緒都應該釘選到裝置本機連接的實體處理器。這可以透過在存取參數或控制擷取卡的每個執行緒的內容中呼叫 Fg_NumaPinThread() 來完成。

此外,記憶體也應該在同一個實體處理器上本機配置。如果應用程式使用 Framegrabber API 提供的配置函式,則會自動完成此作業。不過,如果應用程式使用 Fg_AllocMemHead() 進行明確的生命週期管理即呼叫 Fg_AddMem(),則應使用支援 NUMA 的記憶體配置來配置記憶體。Framegrabber API 提供相關函式 Fg_NumaAllocDmaBuffer() 進行明確的生命週期管理即呼叫 Fg_NumaFreeDmaBuffer() 以在擷取卡本機連接的同一實體處理器上配置與釋放記憶體。

如果應用程式使用其他支援 NUMA 的函式來配置記憶體,則函式 Fg_getIntSystemInformationForBoardIndex() 使用 INFO_DRIVERGROUPAFFINITY 可用於提供驅動程式 IRQ 群組親和性,而函式 Fg_getInt64SystemInformationForBoardIndex() 使用 INFO_DRIVERAFFINITYMASK 可用於提供裝置的驅動程式 IRQ 處理器親和性遮罩。

某些 NUMA 電腦可能還有其他限制。例如,某些 NUMA 電腦的設計旨在平衡對周邊匯流排的存取,不允許單一裝置使用全部甚至大部分頻寬。雖然這對大多數伺服器應用程式有利,但對於影像擷取與處理應用程式而言,此策略可能會限制擷取卡裝置可用的頻寬。

使用 Plain C時的注意事項#

當應用程式限於使用純 C 編譯器時,將無法使用 Framegrabber API 為求便利或型別安全而提供的一些 C++ 包裝函式。本章將針對每個主題說明如何使用 C++ 包裝函式所使用的底層功能。

Plain C 中的系統資訊#

int Fg_getSystemInformation(
    Fg_Struct * fg,
    enum Fg_Info_Selector info,
    enum FgProperty property,
    int arg,
    void * buffer,
    unsigned int * size);

在小節 一般系統資訊 進行明確的生命週期管理即呼叫 特定應用的系統資訊 章節 系統資訊 中,記錄了一組函式,用於要求關於系統一般情況或特定擷取卡的各種資訊。這兩個章節中的函式全都是函式 Fg_getSystemInformation().

函數 Fg_getSystemInformation() 的 C++ 包裝函式,將一律以字串形式傳回所要求的資訊,該字串儲存在傳遞給函式的緩衝區中。若要配置大小充足的緩衝區,可以透過將用於參數 size 的變數初始化為 0 並在呼叫中傳遞 NULL 傳遞給參數 buffer 來要求緩衝區大小。如果所要求的資訊屬於數值型別,則必須在成功要求資訊後轉換字串。

若要要求關於系統的一般資訊(如第 一般系統資訊取得 bytearray,可以呼叫 Fg_getSystemInformation() 節中所記錄),應呼叫 NULL 並將其傳遞至參數 fg ,且將 0 傳遞至參數 arg.

例如,可以查詢電腦中安裝的畫面擷取卡數量,如下所示:

int numBoards = 0;
char buffer[256];
unsigned int size = sizeof(buffer);
int result =
    Fg_getSystemInformation(NULL, INFO_NR_OF_BOARDS, PROP_ID_VALUE,
                            0, buffer, &size);
if (result == FG_OK) {
    numBoards = atoi(buffer);
    std::cout << "Number of boards: " << numBoards << std::endl;
}

若要要求關於特定畫素擷取卡的資訊(如第 特定應用的系統資訊取得 bytearray,可以呼叫 Fg_getSystemInformation() 節中所記錄),預期會將畫素擷取卡的控制代碼傳遞至參數 fg,或將機板索引傳遞至參數 arg.

例如,以下程式碼會請求主機板類型與名稱。

const int boardIndex = 0;

char buffer[256];
unsigned int size = sizeof(buffer);

int boardType = 0;
std::string boardName;
int result =
    Fg_getSystemInformation(NULL, INFO_BOARDTYPE, PROP_ID_VALUE,
                            boardIndex, buffer, &size);
if (result == FG_OK) {
    boardType = atoi(buffer);

    size = sizeof(buffer);
    result =
        Fg_getSystemInformation(NULL, INFO_BOARDNAME, PROP_ID_VALUE,
                                boardIndex, buffer, &size);
}
if (result == FG_OK) {
    boardName = buffer;

    std::cout << "Board #" << boardIndex
              << " is a " << boardName
              << " (type " << std::hex << boardType
              << std::dec << ")" << std::endl;
}

若要要求需要額外輸入引數的資訊,緩衝區會以雙向方式運作。額外引數必須在呼叫前以字串形式儲存於緩衝區中。在呼叫期間,會使用並覆寫傳遞至緩衝區中的引數,因為要求的資訊會寫入緩衝區中。

在 Plain C 中存取參數值#

int Fg_getParameterWithType(
    Fg_Struct * fg,
    int id,
    void * value,
    unsigned int dma,
    enum FgParamTypes type);

int Fg_freeParameterStringWithType(
    Fg_Struct * fg,
    int id,
    void * value,
    unsigned int dma,
    enum FgParamTypes type);
int Fg_setParameterWithType(
    Fg_Struct * fg,
    int id,
    const void * value,
    unsigned int dma,
    enum FgParamTypes type);

在第 存取參數值 章節 處理 Applet 參數節中記錄了用於要求最常見型別參數的 C++ 包裝函式。若要從純 C 存取參數,或存取沒有對應 C++ 包裝函式的型別參數,可以使用函式 Fg_getParameterWithType() 進行明確的生命週期管理即呼叫 Fg_setParameterWithType() 。呼叫時需要一個相符型別的變數,且對應的 FgParamTypes 值必須在呼叫中傳遞至參數 type 。型別值記錄在第 參數類型 節中,且應參閱小程式(applet)說明文件以了解特定參數的型別。

例如,下列程式碼將小程式的第一個 DMA 通道寬度設為 1024。

const int dma = 0;
const int width = 1024;
int result = FG_INVALID_PARAMETER;
int paramId = Fg_getParameterIdByName(fg, "FG_WIDTH");
if (paramId > 0) {
    result =
        Fg_setParameterWithType(fg, paramId, &width, dma,
                                FG_PARAM_TYPE_INT32_T);
}

呼叫 Fg_getParameterWithType() 時的一個特殊情況是型別 FG_PARAM_TYPE_CHAR_PTR ,它代表字串參數。雖然可以透過呼叫 Fg_getParameterPropertyEx()來要求配置足夠大小之緩衝區所需的字串長度,但這並不是存取動態參數最安全的方法。較佳的方法是改為要求型別為 FG_PARAM_TYPE_CHAR_PTR 使用 FG_PARAM_TYPE_CHAR_PTR_PTR 的參數。此要求將會配置正確大小的緩衝區,並在不再需要該值之後,透過呼叫 Fg_freeParameterStringWithType() 予以釋放。

以下範例顯示如何使用 FG_PARAM_TYPE_CHAR_PTR_PTR,假設 paramId 是型別為 FG_PARAM_TYPE_CHAR_PTR:

const char * val = NULL;
int result =
    Fg_getParameterWithType(fg, paramId, &val, dma,
                            FG_PARAM_TYPE_CHAR_PTR_PTR);
if (result == FG_OK) {
    // use the string value stored in val ...

    Fg_freeParameterStringWithType(fg, paramId, val, dma,
                                   FG_PARAM_TYPE_CHAR_PTR_PTR);
}

的參數。對 Fg_freeParameterStringWithType() 接收一個 char *,實際上不是 char **.

存取欄位參數#
struct FieldParameterAccess {
    enum FgParamTypes vtype;
    unsigned int index;
    unsigned int count;
    union {
        int32_t * p_int32_t;
        uint32_t * p_uint32_t;
        int64_t * p_int64_t;
        uint64_t * p_uint64_t;
        double * p_double;
    };
};

欄位參數 (Field parameters) 是表示兩個或多個相同類型數值的陣列的參數。典型的範例是用於重新對應像素值的對照表 (Lookup Table)。欄位參數的類型可以是 FG_PARAM_TYPE_STRUCT_FIELDPARAMINT, FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64 或 FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE.

欄位參數的大小可以透過請求參數 Property PROP_ID_FIELD_SIZE 來決定,如章節 存取參數屬性 章節 處理 Applet 參數中所記錄。如果需要, 在 Plain C 中存取參數 Property 也請參閱章節。

若要請求或變更欄位參數陣列中的數值,應使用 Fg_getParameterWithType() 進行明確的生命週期管理即呼叫 Fg_setParameterWithType() 的實例來呼叫函數 struct FieldParameterAccess 進行明確的生命週期管理即呼叫 FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS 應在參數 type中傳遞。成員 vtype 的項 struct FieldParameterAccess 必須設為與欄位中單一數值類型相對應的 enum FgParamTypes 值,而不是欄位參數類型本身。成員 index 進行明確的生命週期管理即呼叫 count 指定欄位參數陣列中的偏移量,以及函數呼叫中所請求或提供的項目數量。最後,如果呼叫了 Fg_setParameterWithType() ,則必須使用預先配置的緩衝區來初始化聯集 (Union) 中對應於欄位中單一數值類型的指標,該緩衝區包含輸入時要變更的數值;如果在呼叫 Fg_getParameterWithType() 之後,則包含目前儲存在陣列中的數值。

以下範例請求對照表中的前 256 個數值,假設 paramId 是型別為 FG_PARAM_TYPE_STRUCT_FIELDPARAMINT 且欄位本身由至少 256 個類型為 FG_PARAM_TYPE_INT32_T:

int32_t field[256];
struct FieldParameterAccess fpa;
fpa.vtype = FG_PARAM_TYPE_INT32_T;
fpa.index = 0;
fpa.count = 256;
fpa.p_int32_t = &field;
int result =
    Fg_getParameterWithType(fg, paramId, &fpa, dma,
                            FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS);
if (result == FG_OK) {
    // work with the values in field ...
}

在 Plain C 中存取參數 Property#

int Fg_getParameterProperty(
    Fg_Struct * fg,
    int id,
    enum FgProperty property,
    void * buffer,
    int * size);

int Fg_getParameterPropertyEx(
    Fg_Struct * fg,
    int id,
    enum FgProperty property,
    int dma,
    void * buffer,
    int * size);

在第 存取參數屬性 章節 處理 Applet 參數的數值組成,文件中記錄了用於請求具有最常見類型的參數 Property 的 C++ 包裝函式 (Wrappers)。若要從純 C 存取參數 Property,或存取沒有對應 C++ 包裝函式的參數 Property 類型,可以使用函數 Fg_getParameterPropertyEx() 。不建議使用函數 Fg_getParameterProperty() ,因為該函數呼叫隱式使用了 DMA 通道 0,但不同通道的參數 Property 可能會有所不同。

函數 Fg_getParameterPropertyEx() 除了其中一種情況外,其他所有情況都會將請求的資訊以字串形式傳回,並儲存在傳遞給函數的緩衝區中。若要配置足夠大小的緩衝區,可以透過初始化用於參數 size 的變數初始化為 0 並在呼叫中傳遞 NULL 傳遞給參數 buffer 來要求緩衝區大小。如果所要求的資訊屬於數值型別,則必須在成功要求資訊後轉換字串。

以下範例展示如何取得參數的最小值,假設 paramId 是以下類型的參數 FG_PARAM_TYPE_INT32_T:

char buffer[256];
int size = sizeof(buffer);

int32_t minVal = 0;
int result =
    Fg_getParameterPropertyEx(fg, paramId, PROP_ID_MIN, dma, buffer, &size);
if (result == FG_OK) {
    minVal = atoi(buffer);

    // work with the property ...
}
的變數來請求緩衝區的大小。#
struct FgPropertyEnumValues {
    int32_t value;
    char name[1];
};

#define FG_PROP_GET_NEXT_ENUM_VALUE(pev) ...

存取列舉數值參數 Property PROP_ID_ENUM_VALUES 當請求列舉參數的 Property Fg_getParameterPropertyEx() 不會將該屬性以字串形式傳回。相反地,緩衝區將透過以下方式填入: struct FgPropertyEnumValues. 該巨集 FG_PROP_GET_NEXT_ENUM_VALUE() 可用於遍歷緩衝區中的元素。為方便起見,該屬性 PROP_ID_IS_ENUM 將回傳枚舉參數所需的緩衝區大小。

以下範例示範如何取得並輸出參數的 enum values 屬性,假設 paramId 是一個枚舉參數:

const int defBufferSize = 256;

int size = defBufferSize;
char * buffer = malloc(size);

int result =
    Fg_getParameterPropertyEx(fg, paramId, PROP_ID_IS_ENUM,
                              dma, buffer, &size);
if (result == FG_OK) {
    int newSize = atoi(buffer);
    if (newSize > 0) {
        free(buffer);
        size = newSize;
        buffer = malloc(size);

        result =
            Fg_getParameterPropertyEx(fg, paramId, PROP_ID_ENUM_VALUES,
                                      dma, buffer, &size);
    } else {
        result = FG_INVALID_TYPE;
    }
}
if (result == FG_OK) {
    struct FgPropertyEnumValues * pev;
    for (pev = (struct FgPropertyEnumValues *)buffer;
         pev != NULL;
         pev = FG_PROP_GET_NEXT_ENUM_VALUE(pev)) {
            printf("%s is %d\n", pev->name, pev->value);
    }
}
free(buffer);

在 Plain C 中為非同步模式註冊回呼函數#

struct FgApcControl {
    unsigned int version;
    Fg_ApcFunc_t func;
    void *data;
    unsigned int timeout;
    unsigned int flags;
};

int Fg_registerApcHandler(
    Fg_Struct * fg,
    unsigned int dma,
    struct FgApcControl * control,
    enum FgApcControlFlags flags);

函數 Fg_registerApcHandler() 可用於在開始擷取之前設定非同步模式。

回調函數的運作方式與該節所述相同 註冊非同步模式的回呼函數 章節 影像擷取. 透過呼叫該函式來註冊回調函式時 Fg_registerCallbackHandler() 控制擷取迴路的參數是透過一個 struct FgApcControl, 然而在呼叫 C++ 封裝函式時 Fg_registerApcHandlerEx() 這些會作為參數傳遞給函式。

在註冊回呼函式時,有兩個地方會傳遞旗標。該參數 flags 該函數的 Fg_registerApcHandler() 不控制採樣迴路,且未被使用。應用程式應始終將 0 傳遞給此參數。相反地,控制採樣迴路的旗標應傳遞至該成員變數中 flags 的項 struct FgApcControl.

以下範例顯示如何使用簡單的結構來註冊回呼函數,該結構包含處理影像擷取所需的資訊:

struct ApcUserCallbackData
{
    Fg_Struct * fg;
    dma_mem * mem;
    unsigned int dma;
    unsigned int timeoutInSeconds;
    int mode;
};

int ApcUserCallback(frameindex_t frame, void * data)
{
    auto context = reinterpret_cast<ApcUserCallbackData *>(data);

    if (frame > 0) {
        // process new image ...
    } else {
        // handle error ...
    }

    return 0;
}

void SetupApcUserCallback(ApcUserCallbackData * context)
{
    // register callback function
    FgApcControl control;
    control.version = 0;
    control.func = &ApcUserCallback;
    control.data = context;
    control.timeout = context->timeoutInSeconds;
    control.flags = FG_APC_DELIVER_ERRORS | FG_APC_IGNORE_TIMEOUTS;

    int result = Fg_registerApcHandler(
        context->fg, context->dma, &control, 0);
    if (result != FG_OK) {
        throw std::runtime_error("Failed to register callback function");
    }
}

範例中未顯示上下文結構的配置和管理。當回呼函式保持註冊狀態時,該指標必須有效。其中一種解決方案是將所有內容保存在 C++ 類別中。若要在 C++ 類別上下文中使用回呼函式,可以使用靜態函式來註冊回呼處理常式,並且 this 指標應用作上下文資料指標,該指標可以轉換回類別指標並相應地使用。

當不再需要回呼函式時,可以透過呼叫取消註冊 Fg_registerApcHandler() 透過使用相同的幀擷取器處理程序和 DMA 通道,但傳遞 NULL 傳遞給參數 func:

int result = Fg_registerApcHandler(context->fg, context->dma, NULL, 0);

在 Plain C 中取消註冊非同步事件處理的回呼函數#

int Fg_registerEventCallback(
    Fg_Struct * fg,
    uint64_t mask,
    Fg_EventFunc_t handler,
    void * data,
    unsigned int flags,
    struct fg_event_info * info);

要 Fg_registerEventCallback(),類型為 Fg_EventFunc_t 可進行註冊,以便在收到來自某組事件來源的一項或多項事件時觸發回調,詳情請參閱第 為非同步事件處理註冊回呼函數 章節 影像擷取.

當不再需要回呼函式時,可以透過呼叫取消註冊 Fg_registerEventCallback(), 將該面具傳遞給同一組事件, FG_EVENT_DEFAULT_FLAGS 傳遞給參數 flags 進行明確的生命週期管理即呼叫 NULL 傳遞給參數 handler, data 進行明確的生命週期管理即呼叫 info:

// unregister event handler
Fg_registerEventCallback(fg, mask, NULL, NULL, FG_EVENT_DEFAULT_FLAGS, NULL);

  1. 群組代碼是一個位元陣列,每個位元對應於授權的一項功能。在此語境下,「超集」意指小程式群組代碼中的每個位元,都必須在影像擷取卡群組代碼中被設定為高。 在此情境下,「子集」意指小程式群組代碼中的每個位元,都必須在幀擷取器群組代碼中被設定為高。若小程式使用了幀擷取器上尚未透過授權啟用的功能,則該小程式將無法由Framegrabber SDK 載入。↩↩

  2. 參數的值可以設定為 [PROP_ID_MIN; PROP_ID_MAX] 步長為 PROP_ID_STEP 根據線性關係 PROP_ID_VALUE = PROP_ID_MIN + n*PROP_ID_STEP, 其中 n 屬於 [0; (PROP_ID_MAX-PROP_ID_MIN)/PROP_ID_STEP[. ↩↩↩

  3. 曾使用過的舊版Framegrabber API Fg_getLastPicNumberBlockingEx() 在擷取迴路中,無論採用何種擷取模式。當 ACQ_BLOCK 若使用此功能,新的預設行為是呼叫 Fg_getImageEx() 與 SEL_ACT_IMAGE 當 FG_APC_BATCH_IMAGES 已設定,且 SEL_NEXT_IMAGE 否則。這表示在 ACQ_BLOCK使用預設行為時,回呼處理常式接收到的是緩衝區編號,而不是影格編號。由於在使用 ACQ_BLOCK時,無法安全地判定給定影格編號對應的緩衝區編號,因此 FG_APC_OLD_ACQ_BLOCK_BEHAVIOR 並且從回呼處理常式中呼叫 Fg_getImageEx() 是不建議的做法,僅應在絕對需要維持應用程式碼的相容性時使用。 ↩↩