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 result code。如果函式呼叫順利執行,傳回值將會是 FG_OK。在大多數情況下,負值表示發生錯誤狀況。Error Codes 定義在標頭檔中 basler_fg.h.
函式 Fg_getErrorDescription() 可以用來取得特定 result code 的字串表示法。此函式的第一個引數是擷取卡的代代碼(Handle),並未被使用,僅為了 API 回溯相容性而包含在內。您應該一律傳遞 nullptr.
函式 Fg_getLastErrorNumber() 將會一律傳回在相同執行緒內容中呼叫的最後一個函式的 result code。當函式本身未傳回 result code 時,這非常有用。該函式接受擷取卡代碼作為引數,當 nullptr 在最後呼叫的函式未收到擷取卡代碼的情況下。若要請求需要擷取卡代碼的函式之 result code,必須將相同的代碼傳遞給 Fg_getLastErrorNumber().
資訊
Error Codes 儲存在執行緒區域儲存區(Thread-Local Storage)中。這表示透過呼叫 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;
}
本文件其餘部分的程式碼範例將不包含錯誤處理,因為這是針對應用程式需求而定的。不過,在可行範圍內,仍會檢查傳回碼以確保函式成功執行。
採集卡庫初始化#
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() 使用 property PROP_ID_VALUE:
| Information | 描述 | 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 版中新增的。有關舊介面,請參閱純 C 語言中的系統資訊一節。
對於任何指定的擷取卡,大多數其他資訊都可以透過函數來查詢 Fg_getIntSystemInformationForBoardIndex(), Fg_getInt64SystemInformationForBoardIndex() 和 Fg_getStringSystemInformationForBoardIndex() 透過使用擷取卡索引或擷取卡代號以及 property PROP_ID_VALUE:
| Information | 描述 | Type |
|---|---|---|
| INFO_TIMESTAMP_FREQUENCY | 用於影像 Timestamp 的 Timestamp 頻率 | int64_t |
| INFO_BOARDNAME | 面板名稱(例如顯示於 microDiagnostics 中) | 字串 |
| INFO_BOARDTYPE | sisoboards.h 中定義的面板類型 | int32_t |
| INFO_BOARDSERIALNO | 面板序號 | int32_t |
| INFO_FIRMWAREVERSION | 面板的韌體版本 | 字串 |
| INFO_HARDWAREVERSION | 面板的硬體版本 | 字串 |
| INFO_CAMERA_INTERFACE | 面板提供的相機介面('CameraLink' 或 'CXP') | 字串 |
| INFO_DRIVERVERSION | 用於面板的驅動程式版本 | 字串 |
| INFO_DRIVERARCH | 用於面板的驅動程式架構 | 字串 |
| INFO_DRIVERFULLVERSION | 用於面板的完整驅動程式版本(包含架構) | 字串 |
| INFO_DRIVERGROUPAFFINITY | 驅動程式 IRQ 群組親和性(請參閱支援非統一記憶體存取) | int32_t |
| INFO_DRIVERAFFINITYMASK | 驅動程式 IRQ 處理器親和性遮罩(請參閱支援非統一記憶體存取) | int64_t |
| INFO_LICENSE_GROUP_CODE | 擷取卡授權群組代碼(必須是 Applet 授權群組代碼的超集合a) | int32_t |
| INFO_LICENSE_USER_CODE | 擷取卡授權使用者代碼(必須與 Applet 授權使用者代碼相符) | 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 路徑) | 字串 |
| INFO_APPLET_BITSTREAM_ID | Applet 的位元串流 id(必須在參數 arg 中傳入 Applet 路徑) | 字串 |
| 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 payload size | int32_t |
上方列表並不完整,僅包含對應用程式有用的資訊 Property。有關該 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;
}
初始化後一旦取得擷取卡代號,便可使用函數 Fg_getIntSystemInformationForFgHandle(), Fg_getInt64SystemInformationForFgHandle() 和 Fg_getStringSystemInformationForFgHandle() 可用於根據上方列表取得特定於主機板的資訊。(請參閱章節 採集卡初始化。)可以使用採集卡 handle 與 Property 來請求下列資訊 PROP_ID_VALUE:
| Information | 描述 | Type |
|---|---|---|
| INFO_APPLET_CAPABILITY_TAGS | 描述 applet 功能的 key-value-pairs 列表 | 字串 |
| INFO_OWN_BOARDINDEX | 採集卡的主機板索引 | int32_t |
| INFO_FPGA_BITSTREAM_ID | FPGA 中作用中的 applet 的 bit stream id | 字串 |
| INFO_APPLET_FULL_PATH | 完整 applet 路徑 | 字串 |
| INFO_APPLET_FILE_NAME | applet 檔案名稱 | 字串 |
| INFO_APPLET_TYPE | 0:HAP 檔案;1:DLL/SO 檔案(請參閱 Applets) | int32_t |
上面的清單並不完整,僅包含對應用程式有用的資訊的 property。有關 API 的更多使用案例,請參閱 Framegrabber API 參考手冊。
使用板卡索引#
透過使用擷取卡索引來選取擷取卡。如果您知道系統中存在的擷取卡數量,在預設情況下,第一張擷取卡的索引為 0,第二張擷取卡的索引為 1,以此類推。
Framegrabber API 允許使用者使用設定檔為每張擷取卡指定唯一索引,該設定檔可從 microDiagnostics 內部產生。在這種情況下,使用者應了解其指派內容及對應的擷取卡索引。
Applets#
若要配合擷取卡運作,需要一個 Applet。Applet 是多項內容的集合,概括而言包括:
- 擷取卡上 FPGA 的設計,用於實作從接收來自相機裝置的影像到將處理後的影像傳送至電腦 Memory 中的影像處理
- 軟體設計說明,其中包含設計中使用的 VisualApplets 運算子,以及將參數對映至 FPGA 暫存器的資訊。
- 一組軟體介面函式庫,可用作為類別執行個體的產生器,用於處理運算子參數值與 FPGA 暫存器內容之間的轉換。
- 頂層函式庫 VAS,負責處理所有運算子類別的執行個體化、初始化以及與 applet 的介面連接。
如果 Applet 是使用 VisualApplets 設計的,則會以 HAP 檔案的形式提供;如果是透過 Framegrabber SDK 預先安裝的,則會以封裝的 applet 函式庫檔案形式提供。HAP 檔案通常儲存在 Hardware Applets 目錄中(位於 Framegrabber SDK 安裝資料夾內),而封裝的 applet 函式庫檔案則安裝在 dll 目錄的板卡專屬子資料夾中。在本文檔中,這些子資料夾稱為對應類型 applet 的標準位置。
每個 applet 都提供了一組參數來設定所提供的功能。請參閱所用特定 applet 的說明文件,以取得該 applet 所提供功能和參數的詳細說明。
選擇正確的 Applet#
可以與 Framegrabber SDK 一起安裝的 applet 通常以封裝的函式庫檔案形式提供。這些檔案的命名慣例遵循以下規則:
- 檔名通常開頭為
Acq_。(這些 applet 稱為 Advanced Acquisition Applet,上一代則稱為 Standard Applet 且具有不同的命名慣例。) - 接下來,applet 支援的攝影機數量以
Single,Dual或Quad. - 的形式給出。
CXP12表示 CoaXPress 攝影機介面,支援 CoaXPress 標準 2.0 版所指定的最高 12 Gbit/s 速率。 - 在較舊的擷取卡平台上,Camera Link 連線的最大數量以
x1,x2或x4的形式給出。(這已從命名慣例中刪除,因為x4包含對x2和x1,x2支援x1,且支援的最大連線數量通常可以透過支援的攝影機數量以及擷取卡提供的實體連接埠數量來明確判定。) - 然後會給出支援的感測器類型。這會是
Area或Line。對於同時支援面陣和線陣感測器的 applet,這部分會被省略。 - 名稱的最後一部分是 applet 支援的資料格式,例如
Gray8,Bayer16,RGB24。對於支援多種資料格式的 applet,這部分會被省略。
例如,applet Acq_SingleCXP12Area 是一個進階擷取 Applet,支援一個最高 12 Gbit/s 的 CXP 攝影機和一個面陣感測器。該 applet 支援多種資料格式。若要了解支援的資料格式和感測器尺寸,必須參閱 applet 說明文件。
例如,如果應用程式需要兩個 12 Gbit/s 的 CXP 攝影機、一個具備 10 位元灰階輸出的線陣感測器,請尋找名稱為以下格式的 applet: Acq_DualCXP12Line, Acq_DualCXP12LineGray10 或 Acq_DualCXP12LineGray16 並請閱讀隨附的文件。如果相機僅使用單一實體鏈結進行連接,則 Quad 也可以考慮使用 Applet 的其他變體。
啟動行為#
當 Framegrabber SDK 開始初始化 fglib5 函式庫時,如 Frame Grabber Initialization 中所述,甚至在載入 applet 之前就需要存取採集卡。例如,這是根據序號對主機板進行排序,或甚至是檢視所有可供載入的 applet 所必需的。
用於此初始化的 applet 取決於您所使用的採集卡。您可以使用以下章節中的資訊來設定採集卡,使其從預先定義的 applet 開始執行。當 呼叫 Fg_Init 會導致較長的啟動時間。
在啟動時,Framegrabber SDK 預設會載入以下項目所使用的最後一個 Applet: Fg_Init.
您可以透過設定系統環境變數來停用此行為 FGSDK_LOAD_LAST_APPLET_ON_INIT=Off.
microEnable 5 marathon 擷取卡的啟動行為#
在 microEnable 5 marathon 擷取卡上,您必須將 Applet 燒錄至板卡上可用的其中一個分割區中。Framegrabber SDK 的預設行為是將下列項目中使用的 Applet 設定為 Fg_Init 作為開機分割區。在啟動時,系統會在系統上電時使用開機分割區中的 Applet 來初始化板卡。此 Applet 隨後也將用於 Framegrabber SDK 的第一次初始化階段。
microEnable 6 擷取卡的啟動行為#
在 CXP-12 Interface Card、imaWorx CXP-12 Quad、imaFlex CXP-12 Quad 或 imaFlex CXP-12 Penta 擷取卡上,Framegrabber SDK 會嘗試開啟最後載入的 Applet。最後載入的 Applet 定義於 LastApplet 區段中的 FG 在名為以下的組態檔中 me6_<n>_init.config其中 <n> 是主機板的驅動程式索引。此驅動程式索引非常類似於該節中所述的主機板索引 使用板卡索引但它是特定於驅動程式的,且不會受到主機板重新排序的影響。在 Windows 上, driver index 0 指的是驅動程式找到的第一張 CXP-12 Interface Card、imaWorx CXP-12 或 imaFlex CXP-12 擷取卡, driver index 1 指的是第二張板卡,依此類推。在所有其他作業系統上,驅動程式索引與板卡索引相同,不會套用任何重新排序。
在 Windows 系統上,設定檔通常位於下列目錄中 %APPDATA%\basler。在所有其他作業系統上,設定檔位於 $HOME/.config/basler 目錄中。如果找不到設定檔,則 Framegrabber SDK 會在 Framegrabber SDK 安裝的根資料夾中尋找。
如果找不到擷取卡的設定檔,或者初始化失敗,將會使用預設 Applet。預設 Applet 通常是支援每個實體連接埠一個面陣感測器類型相機的 Applet。
如果預設 Applet 的初始化也失敗,則 Framegrabber SDK 會開啟任何可用的 Applet。
列舉 Applets#
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() 函式。如果您想要列舉 Framegrabber 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 | 字串 |
| FG_AP_STRING_BITSTREAM_UID | 用於識別 FPGA 設計的 UID | 字串 |
| FG_AP_STRING_DESIGN_NAME | FPGA 設計的名稱 | 字串 |
| FG_AP_STRING_APPLET_NAME | Applet 的名稱 | 字串 |
| FG_AP_STRING_DESCRIPTION | Applet 的描述 | 字串 |
| FG_AP_STRING_CATEGORY | Applet 的類別 | 字串 |
| FG_AP_STRING_APPLET_PATH | Applet 的完整路徑 | 字串 |
| FG_AP_STRING_SUPPORTED_PLATFORMS | 支援平台的逗號分隔清單 | 字串 |
| FG_AP_STRING_TAGS | 標籤的逗號分隔清單 | 字串 |
| FG_AP_STRING_VERSION | Applet 的版本 | 字串 |
| FG_AP_STRING_APPLET_FILE | Applet 的檔案名稱 | 字串 |
| FG_AP_STRING_RUNTIME_VERSION | 所需的 Framegrabber SDK 版本 | 字串 |
上面的清單並不完整,僅包含對應用程式有用的資訊的 property。有關 API 的更多使用案例,請參閱 Framegrabber API 參考手冊。
使用 applet 迭代器後,應透過呼叫以下內容來釋放: 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 呼叫來複製設定。
在此脈絡下,Configuration 是指初始化擷取卡所使用的 applet 以及該 applet 的 Property 設定。如需詳細資訊,請參閱Working with Applet Parameters一章。
資訊
只有具有以下副檔名的主設定檔案 .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 Parameters#
使用 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 使用數值識別碼來存取參數值或 Property。許多參數都具有標頭檔中所指定的固定數值識別碼 basler_fg.h,不過除了最常見的參數之外,並不建議使用此方法。若要取得參數的數值識別碼,此函數 Fg_getParameterIdByName() 將會被使用。若將名稱轉譯為識別碼時發生錯誤,該函數會傳回 0 或負的錯誤代碼。
若要取得已知數值識別碼的參數名稱,此函數 Fg_getParameterNameById() 可用於此目的。
若要取得已知數值識別碼的參數類型,此函數 Fg_getParameterTypeById() 可用於此目的。
參數類型#
參數具有類型,若要取得或設定參數的值,必須知道其類型,這可以從 Applet 的說明文件、透過自行使用 VisualApplets 設計的 Applet 隱含得知,或是透過使用以下函數來要求其類型: Fg_getParameterTypeById().
| Parameter Type | C/C++ Type |
|---|---|
| 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 | 字串 |
| 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 |
具有其中一種欄位參數類型的參數,應使用 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 通道以及影格編號或緩衝區編號。這適用於任何影像詮釋資料,例如影像傳輸至電腦 Memory 時的時間戳記、實際影像資料的長度以及類似資訊。這些引數無法透過上述函式處理,本主題將在影像擷取章節中進行更詳細的討論。
存取參數 Property#
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 中存取 Property 小節。
除了目前的值之外,參數還具有各種 Property。使用多載函數 Fg_getParameterPropertyWithType() 並不建議,因為函數呼叫會隱含使用 DMA 通道 0,不過不同通道的參數 Property 可能會有所不同。請改用多載函數 Fg_getParameterPropertyWithTypeEx() 可以請求以下 Property:
| Property | 描述 | Type |
|---|---|---|
PROP_ID_VALUE | 目前引數值 | 與參數相同 |
PROP_ID_DATATYPE | 引數類型 (根據 FgParamTypes 的列舉值) | int32_t |
PROP_ID_NAME | 描述性名稱 | 字串 |
PROP_ID_PARAMETERNAME | 引數名稱 | 字串 |
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 | 列舉值(請參閱存取列舉值 Property) | FgPropertyEnumValues[] |
PROP_ID_FIELD_SIZE | 欄位引數中的元素數量 | int32_t |
上述清單並不完整,僅包含未被視為過時的資訊相關 Property。有關該 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 ...
}
Memory 管理#
要從相機擷取影像,電腦中需要有 Memory 來傳輸這些影像,並在程式中存取影像資料。Framegrabber API 使用環形緩衝區 Memory 模型。Memory 緩衝區由至少兩個項目組成,通常稱為 frame buffer 或子緩衝區,這些項目將重複用於從相機擷取的後續畫格。應該使用多少個 frame buffer 取決於各種因素,其中最重要的是在處理某個畫格時,還能接收到多少個後續畫格。這將在影像擷取章節的擷取模型小節中更詳細地討論。
每個幀緩衝區的大小必須相同,且必須足夠大,以容納符合應用程式需求的最大可能影像;其大小通常由寬度決定 FG_WIDTH, 高度 FG_HEIGHT 以及像素格式 FG_FORMAT。(對於在VisualApplets 中設計的小程式,其寬度、高度和像素格式的設定通常會較為複雜。)
迴圈緩衝區可以是一大塊記憶體,在虛擬位址空間中是連續的,並被劃分為大小相等的幀緩衝區;或者,它也可以由多個幀緩衝區組成,這些幀緩衝區是分別在記憶體中分配,並加入管理結構中的。 dma_mem 由 API 使用。
基本 Memory 管理(不建議)#
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),且適用於簡單的使用情境以及應用程式的更特定需求。
進階 Memory 管理#
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 的大型連續 Memory buffer,該緩衝區會被細分為 numFrames 個大小相等的張數緩衝區。此函式會傳回 Memory 管理結構的控制代碼,若發生錯誤則傳回 nullptr。
傳回的指標並非指向 Memory buffer 本身的指標,且不應直接使用!當不再需要 Memory 後,應使用 Fg_FreeMemEx().
釋放。在以下範例中,程式碼將為 16 個張數緩衝區配置一個 Memory buffer,這些緩衝區可容納尺寸為 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時。這對於確保能夠正確計算非常大的影像大小是必要的。
彈性 Memory 管理#
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);
如果應用程式對 Memory allocation 有特定需求,而不希望由 Framegrabber API 進行處理,則可使用函式 Fg_AllocMemHead() 來準備 Memory 管理結構。此函式預期接收與 Fg_AllocMemEx()相同的參數,但它不會配置任何 Memory。在應用程式中配置 Memory 後,需要使用函式 Fg_AddMem()將每個張數緩衝區個別新增。如果應用程式需要在擷取執行期間動態變更用於 Memory buffer 的張數緩衝區,則可使用函式 Fg_DelMem() 從 Memory buffer 中移除張數緩衝區。在不再需要 Memory buffer 後,應先透過呼叫 Fg_FreeMemHead() 來釋放管理結構,然後再釋放張數緩衝區的 Memory。
以下程式碼展示了函式 'Fg_AllocMemEx()' 如何作為更具彈性的 Memory management 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 Framework,將 Framegrabber API 執行緒內容同步回 GUI 執行緒內容可能會很複雜,而在由 Framework 提供的執行緒內容中執行擷取迴圈可能會更合理。
為非同步模式註冊回呼函式#
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 實作影像處理的類別實例的指標。
對於任何指定的圖像擷取卡上的每個 DMA 通道,只能註冊一個回呼函數。
回呼函數將由 Framegrabber API 提供的擷取迴圈呼叫。這意味著回呼函數是在擷取迴圈的執行緒內容中被呼叫的。這也意味著回呼函數傳回所需的時間會增加到擷取迴圈的一般管理負荷中,並且在此期間可能會接收到多張影像。如果在該期間接收到任何影像,回呼函數將在傳回後立即再次被呼叫。
影像擷取的處理可以透過以下方式控制: timeout 和 flags 呼叫中的參數位於: Fg_registerApcHandlerEx()。逾時以秒為單位,擷取迴圈應等待新影像到達。對於參數 flags,可以使用以下值及其組合與運算子 | (二進位 OR)結合使用:
| Flag | 描述 |
|---|---|
FG_APC_DEFAULTS | 擷取迴圈中的預設處理 |
FG_APC_BATCH_FRAMES | 如果接收到多張影像,在對 callback 函式的單次呼叫中可能僅交付最新影像 |
FG_APC_DELIVER_ERRORS | 將錯誤作為負的 frameindex_t 值交付至 callback 函式 |
FG_APC_IGNORE_TIMEOUTS | 忽略影像逾時並繼續處理 |
FG_APC_IGNORE_APCFUNC_RETURN | 忽略 callback 回傳值並繼續處理 |
FG_APC_IGNORE_STOP | 忽略擷取停止的狀態並繼續處理 |
FG_APC_HIGH_PRIORITY | 提高執行擷取迴圈之執行緒的優先順序。若要使用此值,需要作業系統的提升權限。 |
FG_APC_OLD_ACQ_BLOCK_BEHAVIOR | 使用 Fg_getLastPicNumberBlockingEx() 於 ACQ_BLOCK 中c |
擷取迴圈的預設處理方式為:
- 每一張獨立的影像都會傳遞給回呼函數
- 錯誤值不會傳遞給回呼函數
- 影像逾時會結束擷取迴圈
- 回呼函數傳回非零值會結束擷取迴圈
- 停止擷取會結束擷取迴圈
- 擷取迴圈在具有預設優先權的執行緒中執行
- 函式
Fg_getLastPicNumberBlockingEx()被呼叫,且結果會傳遞給回呼函數
資訊
結束擷取迴圈將自動取消註冊回呼函數。雖然在停止擷取時總是結束擷取迴圈似乎合乎邏輯,但情況卻不一定如此。例如,考慮這樣的情況:應用程式需要透過呼叫 Fg_AcquireEx 並指定有限數量的擷取影格來以一定數量的影像突發方式執行擷取。在擷取指定數量的影格後,驅動程式會自動停止擷取。保持擷取迴圈運行並從獨立執行緒啟動擷取突發可能會很方便。
資訊
設定 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() 使用相同的 frame grabber handle 和 DMA 頻道:
Fg_unregisterApcHandler(context->fg, context->dma);
啟動擷取#
為了能夠將影像資料從 frame grabber 傳輸至應用程式 Memory,必須啟動 applet 中的擷取。這會將應傳輸影像資料的 frame buffer 告知 frame grabber,並在成功傳輸新影像的資料時啟用對驅動程式發送中斷的功能。在大多數情況下,在 frame grabber 端啟動擷取後,您也必須在相機端啟動擷取並開始觸發相機。
有關控制相機的更多資訊,請參閱The Camera Control Library siso_genicam 或 The Camera Link Serial Interface Library clsersis。雖然觸發相機不在本文件的討論範圍內,但 Applet 的文件將提供更多關於應用程式透過軟體或硬體訊號觸發相機選項的資訊。
使用基本 Memory 管理的影像擷取(不建議)#
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() 僅能與 memory 管理函式結合使用 Fg_AllocMem() 和 Fg_FreeMem() 且僅限於使用標準擷取模型 ACQ_STANDARD。不建議使用這些函式,因此不予文件化。相反地,可以呼叫函式 Fg_AcquireEx() 和 Fg_stopAcquireEx() 。
使用進階或彈性 Memory Management 進行圖像擷取#
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() 來啟動 frame grabber 上單一 DMA 頻道的影像擷取。此呼叫會將 DMA 頻道 dma 繫結至 memory handle mem ,因而使用配置用於接收影像資料的 Memory。
擷取將持續執行,直到達到透過參數 frames 指定的張數為止。如果將 GRAB_INFINITE 傳遞給 frames,擷取將無限期持續執行。透過參數 flags,即可選擇擷取模型。請參閱 Acquisition Models 章節以取得更多資訊。
| 模型 | 描述 |
|---|---|
| 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,可以選擇停止模式:
| 模式 | 描述 |
|---|---|
| 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 通道。若要了解應使用哪一個函數,請參閱 Acquisition Models 章節以取得更多資訊。針對每個模型,對應的小節中皆提供了一個範例擷取迴圈。
使用基本 Memory Management 等待影像(不建議)#
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() 。
使用進階或彈性 Memory Management 等待影像#
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 並且取決於所使用的擷取模型。若發生錯誤,結果將一律傳回負數的錯誤代碼。以下將簡要說明不同的策略,但原則上此函數的使用應侷限於 The Blocking Acquisition Model章節中所描述的使用情境。不建議將此函數用於任何其他用途,本文件也將不再深入探討。
SEL_NEW_IMAGE:該函數需要指定逾時,並等待至少一個新影像到達。在 ACQ_STANDARD 和 ACQ_SELECT 中,將傳回最新影像的影格編號,並且 ACQ_BLOCK,該函式會封鎖最新影像的 frame buffer,解除封鎖其他影像的任何 frame buffer,並傳回所封鎖影像的 buffer 編號。
SEL_NEXT_IMAGE: 在 ACQ_STANDARD,如果未指定逾時,函式將傳回所接收最新影像的 buffer 編號。如果指定了逾時,函式將等待下一格畫面,該畫面為前一次呼叫該函式時所接收畫面的下一格,若先前未呼叫過則為第 1 格畫面。函式將傳回所接收最新影像的畫面編號。在 ACQ_BLOCK,該函式會封鎖尚未被封鎖或解除封鎖之所接收第一張影像的 frame buffer,並傳回所封鎖影像的 buffer 編號。如果無法再封鎖更多影像且指定了逾時,函式會在執行封鎖作業之前等待至少一個新影像到達。
SEL_ACT_IMAGE: 在 ACQ_STANDARD,該函式的行為與指定 SEL_NEXT_IMAGE 時相同。在 ACQ_BLOCK,該函式會封鎖最新影像的 frame buffer,解除封鎖其他影像的任何 frame buffer,並傳回所封鎖影像的 buffer 編號。如果無法再封鎖更多影像且指定了逾時,函式會在執行封鎖作業之前等待至少一個新影像到達。
SEL_LAST_IMAGE: 該函式會傳回該函式或 Fg_getLastPicNumberBlockingEx()所傳回的最後一個值。這會是最後的畫面編號或 buffer 編號,具體取決於上一次呼叫該函式的方式。
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,但在大多數情況下會設為 1000000 秒(約相當於 277 小時 46 分鐘)。建議在開始擷取之前明確設定參數 FG_TIMEOUT 設定為 FG_TIMEOUT_INFINITE 。
以下範例顯示如何停用驅動程式影像擷取逾時:
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():
| Property | 描述 | 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, ×tamp, 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() 可以呼叫。每個影格的指標並非永久可用,其有效期限僅限於對應的緩衝區尚未排入後續傳輸佇列之前。根據所使用的擷取模型,此函式需要影格編號或緩衝區編號。請參閱一節 Acquisition Models 以了解更多詳細資訊。
以下範例顯示如何為指定的畫面要求畫面緩衝區指標:
// 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 ...
}
Acquisition Models#
Framegrabber API 提供了三種不同的擷取模型,可以在呼叫時進行選擇 Fg_AcquireEx():標準、封鎖和選擇性。這三種擷取模型皆基於章節中所述的 Memory 模型 Memory 管理 其中使用至少兩個影像緩衝區來擷取影像。
對於本節中的範例,將使用由虛擬記憶體中四個連續畫面緩衝區組成的記憶體緩衝區。

Frame Numbers 和 Buffer Numbers#
在整個 Framegrabber API 中,會使用畫面編號和緩衝區編號,兩者的類型均為 frameindex_t.
雖然畫面編號是嚴格遞增的自然數,範圍從 [1; FRAMEINDEX_MAX](指自擷取開始以來所擷取的影像數量),但用於畫面編號的類型 frameindex_t 是一個帶符號類型,在 API 中經常被用來傳回負的 Error Codes 以及畫面編號。尤其是在 32 位元應用程式中,必須小心處理畫面編號溢位:一旦達到 FRAMEINDEX_MAX ,影像計數將會循環回 1!(在 64 位元應用程式中,即使採用極高的畫面播放速率,影像計數器的溢位也只會在大約數十萬年後才會發生。)
相對地,緩衝區編號則指影像資料所在的畫面緩衝區,對於 N 個緩衝區,其範圍為 [1; N]。此處使用的範例中,緩衝區編號介於 1 … 4 之間,並且相應地指代 FB0 … FB3 。
標準擷取模型 (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。一旦影像完全傳輸至電腦 Memory 後,就會通知軟體傳輸已完成,且驅動程式將會多排入一個影格緩衝區。擷取卡將繼續為下一張影像將資料傳輸至佇列中的下一個緩衝區,在我們的範例中為 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。一旦影像完全傳輸至電腦 Memory,軟體就會收到傳輸完成的通知,畫面緩衝區會排入應用程式的佇列中,且在應用程式明確或隱式解除封鎖之前,驅動程式將不會使用它。只要有一個或多個未解除封鎖的畫面緩衝區可用,驅動程式就會在擷取卡中再排入一個畫面緩衝區的佇列。如果沒有更多未解除封鎖的畫面緩衝區可用,則稱為 dummy buffer 的特殊畫面緩衝區會排入擷取卡的佇列中,以保持影像擷取持續運作。擷取卡將繼續把下一張影像的資料傳輸至佇列中的下一個緩衝區,在我們的範例中,這將會是 FB1.
(如The Standard Acquisition Model小節所述,擷取卡中排隊的畫面緩衝區數量取決於多個因素。)
虛擬畫面緩衝區可以被認為是無限大的,並且可以吸收任意大小的資料,但對於處理影像而言沒有意義。因此,應用程式無法存取虛擬緩衝區,傳輸到虛擬緩衝區的任何影像都將遺失。由於每次傳輸畫面時自動排隊下一個緩衝區,這意味著如果沒有未阻塞的畫面緩衝區可供驅動程式排隊,則至少會自動遺失一個影像。即使資料來源停止,並且在畫面緩衝區再次可用之後才重新啟動,情況也是如此!
在同步模式下,應呼叫函數 Fg_getImageEx() 來請求並阻塞一個畫面緩衝區以取得新擷取的影像。該函數將傳回被阻塞畫面緩衝區的緩衝區編號。以下策略與阻塞擷取模型相關:
| 策略 | 描述 |
|---|---|
| SEL_NEXT_IMAGE | 傳回下一個畫面的緩衝區編號 此策略用於依傳輸順序逐一處理每個單一畫面 |
| SEL_ACT_IMAGE | 傳回最新畫面的緩衝區編號 當影像處理速度慢於 Acquisition Frame Rate 時,可使用此策略來略過畫面 |
上述清單並不完整,僅包含與阻塞擷取模型相關的策略。
如果 SEL_NEXT_IMAGE 被使用時,只有下一個 frame buffer 會被封鎖,所有其他 frame buffer 仍會保持在佇列中,供應用程式稍後要求。如果 SEL_ACT_IMAGE 使用時,只有最新的 frame buffer 會被封鎖,所有其他排隊等待應用程式的 frame buffer 都將從佇列中移除並隱式解除封鎖。
當呼叫函數 Fg_getImageEx() 使用此模型時,應始終將 0 傳遞至 frame。此函數無法用於等待特定的 frame 號碼,或將 frame 號碼轉換為 buffer 號碼。若要取得 frame buffer 的 frame 號碼,函數 Fg_getParameterEx() 可以透過傳遞 FG_IMAGE_NUMBER 中的參數 param來呼叫,指向型別為 frameindex_t 用於 frame 號碼,而參數則用於 buffer 號碼 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() 的變數,可在先前的呼叫要求並封鎖 frame buffer 後,將其解除封鎖。 Fg_getImageEx():
| Status | 描述 |
|---|---|
| FG_UNBLOCK | 解除單一影格緩衝區的封鎖 |
| FG_UNBLOCK_ALL | 解除所有目前被封鎖之緩衝區的封鎖 |
FG_UNBLOCK_ALL 也將移除任何仍排隊等待應用程式但尚未被請求和封鎖的 buffer。
當使用函數 Fg_AcquireEx() 來指定要擷取的影像數量時,遺失的影像以及成功傳送的影像都會被計算在內。這可能會導致非預期的 FG_TIMEOUT_ERR 或 FG_TRANSFER_NOT_ACTIVE 呼叫時的結果 Fg_getImageEx() 並且在擷取迴圈中僅計算成功傳送的影像。
在應用程式僅觸發單一影像而前一個影像仍在處理中的情境下,兩個 buffer 就足夠了:一個用於正在處理的影像資料,另一個用於傳輸影像資料。(由於會進行 buffer 鎖定,即使應用程式確保只有在完全處理完影像後才會產生觸發,但在所有情況下至少仍需要兩個 buffer。)
在應用程式端未進行控制、影像持續產生的情境下,影像來源可被視為自由運行(free running)。然而,只要仍有未封鎖的 buffer 可用,驅動程式就只能將影像傳送給應用程式。在以下範例中, FB0 和 FB1 包含有效的影像資料。 FB0 正由應用程式進行處理, FB1 正等待處理。驅動程式已將至少 FB2 排入佇列,且目前正在傳輸影像資料。這種情況仍然是安全的,因為 FB3 尚未使用,即使它可能已經排入佇列。

如果影像處理速度比影像擷取慢,應用程式可能已經完成處理 FB0,將其解除封鎖並繼續處理 FB1。然而,在以下範例中,在此期間已傳輸了另外兩個影像 FB2 和 FB3,且驅動程式已再次從 FB0。當影像傳輸完成時,如果應用程式尚未完成處理 FB1 並將其解鎖,驅動程式將不再有可用於排隊的空閒 buffer。虛擬 buffer 將會被排入佇列,擷取過程中將至少遺失一個影像,但尚未解除封鎖的 frame 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 如果旗標(flag) FG_APC_BATCH_FRAMES 已設定,或使用 SEL_NEXT_IMAGE 如果未設定。回呼函數(callback function)將會收到該影像的 buffer 編號。c
若要以非同步模式處理影格,可以在回呼函式中使用 if 條件句(ApcUserCallbackData 是如 Registering a Callback Function for Asynchronous Mode 一節中所述的結構):
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 版中新增。
透過傳遞來選取selective acquisition model ACQ_SELECT 設定為 flags 在呼叫 Fg_AcquireEx()。應用程式對驅動程式使用 frame buffer 擁有完全的控制權。
Frame buffer 必須明確選取以便在 frame grabber 中排入佇列,且驅動程式將會嚴格按照選取的順序將 frame buffer 排入佇列。當開始擷取時,如果已選取任何 frame buffer,驅動程式才會將 frame buffer 排入 frame grabber 的佇列以進行影像資料傳輸,並從第一個被選取的 frame buffer 開始。一旦影像完全傳輸至電腦 Memory,軟體就會接到傳輸完成的通知,該 frame buffer 不再被視為已選取,且在再次明確選取之前,驅動程式將不會使用它。如果有可用的 frame buffer,驅動程式將會再將一個或多個 frame buffer 排入佇列。如果沒有更多可用的 frame buffer,則不會有任何 frame buffer 被排入佇列。Frame grabber 將繼續把下一張影像的資料傳輸到佇列中的下一個 buffer。如果 frame grabber 中的佇列耗盡,可能會導致內部 buffer 溢位並遺失影像。
(如The Standard Acquisition Model小節所述,擷取卡中排隊的畫面緩衝區數量取決於多個因素。)
由於應用程式對 frame buffer 的使用擁有完全控制權,追蹤 buffer 號碼也是應用程式的責任。除非始終以相同的順序選擇 frame buffer,否則沒有簡單的方法可以將 frame 號碼轉換為 buffer 號碼。此外,也沒有執行此轉換的 API 函數。
函式 Fg_setStatusEx() 可以被呼叫以透過使用來選擇 frame buffer FG_SELECT_BUFFER 並傳遞 buffer 編號。
為簡單起見,在以下範例中,我們假設在開始擷取後所有四個 buffer FB0 … FB3 會按自然順序被選取。此外,影像傳輸完成後會進行處理,並依序再次選取 buffer。透過這種方式,buffer 的順序將始終保持不變,並且可以使用關係式將 frame 編號直接對應轉換為 buffer 編號 bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers).
在不受應用程式端控制而連續產生影像的情境下,影像來源可被視為自由運行(free running)。然而,驅動程式只有在仍有可用的已選擇 buffer 時,才能將影像傳遞給應用程式。在以下範例中, FB0 和 FB1 包含有效的影像資料且未被選擇。 FB0 正由應用程式進行處理, FB1 正等待處理。驅動程式已將至少 FB2 排入佇列,且目前正在傳輸影像資料。這種情況仍然是安全的,因為 FB3 尚未使用,即使它可能已經排入佇列。

呼叫函數 Fg_getLastPicNumberBlockingEx() 以取得處理 FB0 完成後的下一個影格編號,在此情況下將會傳回單一的新影格(傳輸至 FB1的影像之影格編號),前提是此時傳輸至 FB2 尚未完成。
如果影像處理速度比影像擷取慢,應用程式可能已經完成處理 FB0,選取它並繼續處理 FB1。然而,在以下範例中,在此期間已傳輸了另外兩個影像 FB2 和 FB3,且驅動程式已再次從 FB0。當影像傳輸完成時,如果應用程式尚未完成處理 FB1 並已選取它,則驅動程式將沒有更多可用的免費緩衝區可供排隊。只要沒有進一步的影像到達,這種情況就不會像在以下情況中那樣引起相同的隱式影像遺失 ACQ_BLOCK中。但是,一旦接收到另一張影像,擷取卡中的內部 Memory 緩衝區就可能會發生溢位狀況並導致資料遺失。

以下範例擴充了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 ...
}
}
如果應用程式可以略過影像且只需要處理接收到的最新 frame, Fg_setStatusEx() 必須搭配 FG_SELECT_BUFFER 來選取被略過的 buffer。否則,應用程式將會留下未使用的 buffer。
函式 Fg_getImageEx() 不應與選擇性擷取模型結合使用。
在非同步模式下,Framegrabber API 將呼叫 Fg_getLastPicNumberBlockingEx() 以等待新影像,並且如果設定了旗標 FG_APC_BATCH_FRAMES ,將針對最新影像呼叫一次回呼函數;如果未設定,則針對接收到的每個影像各呼叫一次。回呼將收到影像的畫面編號。
若要以非同步模式處理影格,可以在回呼函式中使用內部迴圈的內容(ApcUserCallbackData 是如 Registering a Callback Function for Asynchronous Mode 一節中所述的結構):
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 運算子的應用程式的新 API DmaFromPC。此 API 具有下列限制:
- 這是一個初步的功能預覽。這意味著函式名稱和功能可能會在未來版本的 Framegrabber SDK 中變更。
- 它僅適用於包含 VisualApplets 運算子的 Applet
DmaFromPC。請勿將此 API 用於使用以下的常規 DMA 通道DmaToPC或隨 Framegrabber SDK 一起提供的 Advanced Acquisition Applets。運算子DmaFromPC可在 VisualApplets 3.4.0 或更高版本中使用。 - 目前僅在 Windows 上實作。
使用 VisualApplets 運算子 DmaFromPC, 資料可以從 PC 傳輸到 frame grabber。應用程式包括但不限於協同處理影像資料,或為 frame grabber 上的複雜影像處理提供額外參數。
配置 Memory#
用於傳輸資料的緩衝區必須使用 Advanced Memory Management 中記錄的函式進行配置,例如:
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);
}
或者,您可以使用 Flexible Memory Management 中記錄的函式,例如:
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);
}
雖然緩衝區的大小全都相同,但每次單獨傳輸的位元組數是個別指定的。資料傳輸的大小必須是運算子 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 handle mem 因此將使用為資料傳輸所配置的 Memory。
參數 dma 繫結至函式 Fg_startBufferQueue() 由使用 VisualApplets operator 進行影像傳輸至 PC 的一般 DMA 通道數量決定 DmaToPC ,該函式存在於 VisualApplets 設計中。VisualApplets 運算子的 DMA 通道 DmaFromPC 始終是最後一個 DMA 通道。
在傳輸完所有資料後,呼叫 Fg_stopBufferQueue() 停止緩衝區佇列,並切斷 DMA 通道與所使用的 Memory 控柄之間的連結。
將緩衝區排入佇列以進行資料傳輸#
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。如果成功,該函式會傳回完全傳輸的緩衝區數量;如果失敗,則傳回負的錯誤代碼。
您只能重複使用透過以下方式排入佇列的緩衝區: Fg_queueBuffer() 並在緩衝區完全傳輸後用新資料填入它們。
VisualApplets 運算子的資料傳輸模型 DmaFromPC 非常類似於 選擇性擷取模型。這意味著由於應用程式完全控制緩衝區的使用,追蹤緩衝區編號也是應用程式的責任。除非始終以相同的順序選擇緩衝區,否則沒有簡單的方法可以將傳輸 Counter 轉換為緩衝區編號。此外,也沒有執行此轉換的 API 函式。
Applet Events#
包含在 Framegrabber SDK 安裝中的 Applet 可以向應用程式通知與擷取卡中的影像擷取或處理相關的各種事件。這類事件的範例包括影格開始事件,或在從攝影機收到影像的第一個或最後一個像素時產生的影格結束事件,或者在偵測到觸發輸入訊號邊緣時產生的觸發輸入上升事件或觸發輸入下降事件。VisualApplets 的使用者可以在其設計中使用事件運算子來產生其應用程式所需的事件。
每個事件來源都有一個唯一的名稱,並透過無符號 64 位元整數的單一位元來識別,稱為 event mask。這樣一來,就可以透過在 event mask 中設定多個位元來將多個事件來源分組,例如使用二進位或運算子 | 適用於多個事件來源的多個事件遮罩。不過這也表示,任何 Applet 最多只能支援 64 個事件來源。
事件可以以同步或非同步模式傳遞。在同步模式下,應用程式必須提供一個 event loop。在非同步模式下,可以註冊一個回呼函式,當發生一個或多個事件時將會呼叫該函式。這兩種方法不應在同一個應用程式中混合使用,因為這可能會導致資源衝突。
由於事件來源可能會對電腦系統產生高負載的中斷,因此必須明確啟用每個事件來源,且應僅在需要時才進行啟動。
對於事件的每次發生,都會記錄接收到事件來源中斷的時間。除了時間戳記之外,某些事件來源還可以在每個事件中產生額外資料,稱為 event payload。具有 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 可以進行註冊,以便在接收到來自一組事件來源的一個或該多個事件時被回呼。該函式將針對指定的擷取卡和事件遮罩進行註冊,並在呼叫時傳遞三個參數。回呼函式的第一個參數是接收到事件的所有事件來源的事件遮罩。第二個參數是隨同呼叫一起提供的指標 Fg_registerEventCallback() 並且可用作指向內容結構或類別的指標,例如 this 實作影像處理的類別實例的指標。第三個參數是指向 struct fg_event_info 的指標,該指標必須由應用程式配置並在呼叫 Fg_registerEventCallback() 時傳遞,用於儲存有關事件的資訊。
在任何指定的擷取卡上,只能為同一組事件註冊一個回呼函式。
回呼函式將由 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 支援從多個進程使用擷取卡,但它不支援超出非常基本功能(用於在初始化擷取卡時同步進程的啟動)之外的進程間同步。對於通用目的的進程間通訊與同步,必須使用其他作業系統功能或支援函式庫。
主/從模式的一個特定限制是,每當兩個行程嘗試控制擷取卡上的相同 Property 時,後一個行程會「獲勝」並覆蓋前一個行程的動作。前一個行程甚至可能不會注意到該覆蓋,並對擷取卡的狀態做出錯誤的假設。(存在一項實驗性功能,可使用處理序間通訊來同步行程之間的狀態,然而,這種方法有其自身的影響,將在 Experimental Support for Parameter and Acquisition Synchronization 一節中討論。)
一般的建議是設計進程時讓它們執行獨特的任務(相對於 Applet 參數與 Property 而言,這些任務是互斥的),並且不需要知道其他進程的存在。唯一的例外是監控進程,它只存取 Applet 的唯讀 Property,或透過進程間通訊手段從其他進程收集資訊。
另一個限制是,雖然只有主進程會完整初始化擷取卡,但每個進程中都必須進行基本的初始化。如果早期的進程已經開始影像擷取或相機探索,這種基本初始化可能會與它們發生衝突。
在初始化期間同步處理程序#
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:
| Flag | 描述 |
|---|---|
| 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 工具支援主/從同步,且從屬處理序可以使用群組 ID siso-microdisplay-master與 microDisplay X 同步。旗標 FG_INIT_LIBRARIES_SEQUENTIAL 不支援 microDisplay X。
對參數與擷取同步的實驗性支援#
資訊
參數和擷取同步的實驗性支援是在 Framegrabber SDK 5.9 版中加入的。
Framegrabber SDK 提供實驗性支援,可同步處理序同步群組中的所有參數變更與擷取狀態。若要啟用參數與擷取同步,必須將 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 電腦上執行的應用程式,存取擷取卡的所有執行緒都應該釘選 (pinned) 到裝置本機連接的實體處理器。這可以透過呼叫 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);
在各節中 一般系統資訊 和 板卡專屬系統資訊 章節 系統資訊 記錄了一組用於請求關於系統整體或特定圖形擷取卡各種資訊的函式。這兩個章節中的函式全部都是該函式的 C++ 包裝函式 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 Parameters,記錄了用於請求具有最常見型別之參數的 C++ 包裝函式。若要從純 C 存取參數,或存取沒有對應 C++ 包裝函式的型別參數,相關函式 Fg_getParameterWithType() 和 Fg_setParameterWithType() 在「使用 Applet 參數」章節的「存取參數值」小節中,記錄了用於請求最常見型別參數的 C++ 包裝函式。若要從普通的 C 存取參數,或存取沒有對應 C++ 包裝函式的型別參數,可以使用函式 FgParamTypes 。呼叫時需要一個相符型別的變數,並且必須在參數中傳遞對應的 type 在呼叫中。型別值記錄於章節 參數類型 中,且應參閱 Applet 文件以了解特定參數的型別。
例如,下列程式碼將 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;
};
};
,實際上不是一個 FG_PARAM_TYPE_STRUCT_FIELDPARAMINT, FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64 或 FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE.
欄位參數是指代表兩個或多個相同型別之值的陣列的參數。典型的例子是用於重新對應像素值的尋找表。欄位參數的型別可以是 PROP_ID_FIELD_SIZE 如章節中所記錄 存取參數 Property 章節 使用 Applet Parameters。如果需要,亦請參閱章節 在 Plain C 中存取 Property 。
來決定,如「Working with Applet Parameters」一章中的「Accessing Parameter Properties」一節所述。如果需要,也可以參閱「Accessing Parameter Properties in Plain C」一節。 Fg_getParameterWithType() 和 Fg_setParameterWithType() 若要請求或變更欄位參數陣列中的值,應使用下列函數 struct FieldParameterAccess 和 FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS 應在參數中傳遞 type。 vtype 的成員 struct FieldParameterAccess 必須設為 enum FgParamTypes 對應於欄位中單一值型別的值,而不是欄位參數型別本身。成員 index 和 count 指定欄位參數陣列中的位移,以及在函數呼叫中請求或提供的項目數量。最後,聯集(union)中對應於欄位中單一值型別的指標必須初始化為預先配置的緩衝區,該緩衝區包含輸入時要變更的值(如果有的話)。 Fg_setParameterWithType() 或呼叫後目前儲存在陣列中的值 Fg_getParameterWithType() 被呼叫。
以下範例請求查詢表格(lookup table)中的前 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);
在章節中 存取參數 Property 章節 使用 Applet Parameters,記錄了用於請求具有最常見型別之參數 Property 的 C++ 包裝函式。若要從純 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 ...
}
存取列舉值參數 Property#
struct FgPropertyEnumValues {
int32_t value;
char name[1];
};
#define FG_PROP_GET_NEXT_ENUM_VALUE(pev) ...
當請求 Property PROP_ID_ENUM_VALUES 一個列舉(enumeration)參數的 Fg_getParameterPropertyEx() 將不會以字串形式傳回 Property。相反地,緩衝區將會使用以下內容填入 struct FgPropertyEnumValues。巨集 FG_PROP_GET_NEXT_ENUM_VALUE() 可用於迭代緩衝區中的元素。為方便起見,Property PROP_ID_IS_ENUM 將傳回列舉參數所需的緩衝區大小。
以下範例展示如何取得並列印參數的列舉值 Property,假設 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)。參數 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() 透過使用相同的擷取卡(frame grabber)代碼與 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(),傳遞相同事件群組的遮罩(mask), FG_EVENT_DEFAULT_FLAGS 給參數 flags 和 NULL 至參數 handler, data 和 info:
// unregister event handler
Fg_registerEventCallback(fg, mask, NULL, NULL, FG_EVENT_DEFAULT_FLAGS, NULL);
-
群組代碼是一個位元欄位,其中每個位元對應於授權的單一功能。在此脈絡下的超集合(Superset)意味著,Applet 群組代碼中的每一個位元,都必須在圖形擷取卡群組代碼中被設定。在此脈絡下的子集合(Subset)意味著,對於 Applet 群組代碼中的每一個位元,都必須在圖形擷取卡群組代碼中被設定。如果 Applet 使用了尚未透過圖形擷取卡授權啟動的功能,則 Framegrabber SDK 將無法載入該 Applet。 ↩↩
-
參數的值可以設定為
[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[. ↩↩↩ -
較舊版本的 Framegrabber API 在擷取迴圈中使用
Fg_getLastPicNumberBlockingEx(),無論使用何種擷取模型。當使用ACQ_BLOCK時,新的預設行為是在設定了Fg_getImageEx()並使用SEL_ACT_IMAGE時呼叫FG_APC_BATCH_IMAGESSEL_NEXT_IMAGE,否則呼叫其他。這意味著在ACQ_BLOCK中,當使用預設行為時,回呼處理程式接收的是緩衝區編號,而不是影格編號。由於在使用ACQ_BLOCK時,無法安全地確定給定影格編號的緩衝區編號,因此不建議使用FG_APC_OLD_ACQ_BLOCK_BEHAVIOR並從回呼處理程式中呼叫Fg_getImageEx()不建議從回呼處理常式中呼叫,且應僅在絕對需要應用程式程式碼的向下相容性時使用。 ↩↩