Fairsion工业相机开发手册笔记1

没想到这一忙就忙到了月底,橙果的纳新前前后后的工作都在搞。上次的文章是9月2号的,今天都已经9月28号了。明天加课的话,事情还是挺多的。所以加急搞点进度吧。

矩形检测出现了一些难以解决的问题,今天来看看Fairsion相机开发指南里的东西好了。


事情是这样的,我将图片预处理之后对图片进行了轮廓发现与绘制矩形的操作,使用trackbar调整时发现无论使用什么阈值都会导致结果不理想,即不仅无法检测到正常的矩形,还会检测到一些“不存在”的矩形。这个先暂时放放吧。

开发环境设置

相机的SDK 基于 ATL 控件进行开发设计, 遵循 COM组件标准。 在安装目录的 include 文件夹下为二次开发所需头文件, 一般仅包含fairsionIncludes.h即可, 由于 COM 组件特性, 用户不用再次引入 lib 文件以及区分 x86和 x64 的库, 直接调用 SDK 接口即可自动调用到对应的 x86 和 x64 的 dll。 SDK 基于 COM 组件编写, 所以不同语言载入方式有所不同 。我这里记录一下C++和Python的载入方法。

C++方式

头文件部分: 需要引入 fairsionIncludes.hfairsionCamera_i.c两个文件,fairsionIncludes.h 包含了相机函数、 辅助函数等定义, fairsionCamera_i.c 包含了相机接口类定义, fairsionCamera_i.c 在项目中只能定义一次, 不能重复定义, 上述两个文件均已放入 VS 相关环境变量, 所以不需要导入项目, 仅需调用时包含上述文件即可。
由于使用了 COM 组件的连接点和事件, 所以有两个变量需要定义, 定义内容如下:

1
2
_ATL_FUNC_INFO AddResultInfo = {CC_STDCALL, VT_BOOL, 1, {VT_I1|VT_BYREF}};
CComModule _Module;

加载 SDK 方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int main() {
IDeviceManager *pManager = NULL;
// 以单线程方式创建 COM 对象(应用程序调用 COM 库函数之前必须初始化 COM 库)
::CoInitialize(NULL);
// 创建设备管理类对象, 此对象在程序中创建一次即可, 建议为全局变量
::CoCreateInstance(CLSID_DeviceManager, 0, CLSCTX_INPROC,
__uuidof(IDeviceManager), (void **)&pManager);
if (pManager) {
// 创建 COM 对象成功, 可进行相关相机控制代码编写
}
// 关闭当前线程的 COM 库, 卸载线程加载的所有 dll
::CoUninitialize();
}

Python方式

Python 可通过引入安装目录下 samples\Grab_python文件夹下 fairsionCamera.py 文件, 然后调用以下代码进行 SDK 的加载和实例化:

1
2
3
4
5
6
from fairsionCamera import *
import win32com.client
from ctypes import *
print("获取设备管理类实例化对象")
pManager = win32com.client.DispatchEx('{939934F4-20DF-4866-A30D-0635F879725E}')
# 上述代码中'{939934F4-20DF-4866-A30D-0635F879725E}'来源于 fairsionCamera.py中 CLSIDToClassMap 结构

相机的初始化与逆初始化

对于相机的管理, Fairsion是通过设备管理对象进行相机资源管理与释放, 通过相机管理对象来获取当前活动相机对象,以达到控制指定相机的目的。

相机的工作流程为在程序入口先初始化设备管理对象, 然后枚举相机数目, 再获取指定相机对象, 然后在退出程序之前释放设备管理对象即可达到释放所有相机的目的。

  • 设备管理类初始化函数, IDeviceManager::InitializeIDeviceManager::Initialize 在程序入口处调用一次即可, 此函数会完成 SDK 所有准备工作, 与 IDeviceManager::Terminate 逆初始化函数对应。
  • 相机枚举函数, IDeviceManager::GetCameraCountIDeviceManager::GetCamera。 通过 IDeviceManager::GetCameraCount 函数获取指定时间内所连接的相机数目, 并通过索引的方式传入 IDeviceManager::GetCamera中获取指定索引的相机对象。
  • 相机对象获取函数, IDeviceManager::GetCameraIDeviceManager::GetCameraByNameIDeviceManager::GetCamera 即为传入相机索引即可获取相机对象,IDeviceManager::GetCameraByName 需要传入用户自定义名称或相机序列号才可获取相机对象 。用户自定义名称可以在Fair IP Configuration Tool里面进行设置。

  • 逆初始化函数, IDeviceManager::Terminate。 在程序末尾调用, 可释放所有相机对象资源, 与 IDeviceManager::Initialize 一一对应。

一个简单的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <fairsionIncludes.h>
#ifndef FAIRSIONCAMERA_I_C
#define FAIRSIONCAMERA_I_C
// 防止此文件重复定义
#include "fairsionCamera_i.c"
#endif
// 建议设备管理对象全局唯一
IDeviceManager* g_pManager = NULL;
int main()
{
long nCameraCount = 0;
ICamera* pCamera[16] = { NULL };
ICamera* pCameraIR530 = NULL;
// 实例化 COM 组件中设备管理类对象
::CoInitialize(NULL);
::CoCreateInstance(CLSID_DeviceManager, 0, CLSCTX_INPROC,
__uuidof(IDeviceManager), (void**)& g_pManager);
if (g_pManager)
{
// 初始化, 整个程序中仅需调用一次
g_pManager->Initialize();
// 获取 2s 内所连接的相机数目, 并枚举对应相机对象
g_pManager->GetCameraCount(2000, &nCameraCount);
for (int i = 0; i < nCameraCount; i++)
{
// 获取指定索引的相机对象
g_pManager->GetCamera(i, &pCamera[i]);
if (pCamera[i])
{
// 获取指定索引的相机对象成功, 可以进行相关相机操作
}
}11
// 获取用户自定义名称的相机对象
g_pManager->GetCameraByName(L"IR530GM", &pCameraIR530);
if (pCameraIR530)
{
// 获取指定用户自定义名称相机对象成功, 可以进行相关相机
操作
} /
/ 逆初始化, 与初始化函数一一对应
g_pManager->Terminate();
g_pManager->Release();
g_pManager = NULL;
} :
: CoUninitialize();
return 0;
}

相机参数操作

相机的参数操作包含参数读取, 参数写入, 参数保存, 参数加载等部分, 参数名称是根据 GenICam 标准的 XML 描述文件中的参数名称进行设置, 常用的参数操作函数根据整型、 浮点型、 字符型、 枚举类型进行详细的分类。

  • 整型: 提供整数型一系列参数操作, 接口包含 ICamera::IsAvailable(参数是否有效)、 ICamera::GetMin(获取参数最小值)、 ICamera::GetMax(获取参数最大值)、ICamera::GetInc(获取参数单元值)、 ICamera::GetValue(获取参数当前值)、ICamera::SetValue(设置参数当前值)等。
  • 浮点型: 提供浮点型一系列参数操作, 这些参数往往都有实际意义的单位如 us,接口包含 ICamera::IsAvailable(参数是否有效)、 ICamera::GetFMin(获取参数最小值)、 ICamera::GetFMax(获取参数最大值)、 ICamera::GetFValue(获取参数当前值)、 ICamera::SetFValue(设置参数当前值)等。
  • 字符型: 提供字符型一系列参数操作, 接口包含 ICamera::IsAvailable(参数是否有效)、 ICamera::GetStrValue(获取参数当前值)、 ICamera::SetStrValue(设置参数当前值)。
  • 枚举类型: 提供枚举类型一系列参数操作, 接口包含 ICamera::IsAvailable(参数是否有效)、 ICamera::GetSelector(获取当前枚举值索引)、ICamera::SetSelector(设置指定索引枚举值)、 ICamera::GetSelectorStr(获取当前枚举值字符)、 ICamera::SetSelectorStr(设置当前字符的枚举值)等。
  • 参数加载: 参数加载是通过已保存的配置文件来设置相机一系列参数, 接口为ICamera::LoadConfigure。
  • 参数保存: 参数保存提供将当前相机参数保存到指定文件的功能, 接口为ICamera::SaveConfigure。

演示代码太长了,不贴了。。。

相机事件链接

相机事件部分采用的是 COM 组件的连接点和事件, 根据事件类型提供了相机上线事件、相机掉线事件、 图像采集成功事件、 相机资源销毁事件、 自动白平衡完成事件等。

不同语言调用响应 COM 组件的连接点和事件, 其中较复杂的是 C/C++方式, 设计上也尽可能的做了简化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <fairsionIncludes.h>
#ifndef FAIRSIONCAMERA_I_C
#define FAIRSIONCAMERA_I_C
// 防止此文件重复定义
#include "fairsionCamera_i.c"
#endif
// 设备管理对象, 建议全局唯一
IDeviceManager* g_pManager = NULL;
// 事件接收器对象, 与设备管理对象绑定
CComObject<CSinkAddResult>* g_pSinkAddResult = NULL;
// 智能指针, 连接设备管理对象与事件接收器对象
IUnknownPtr g_pUnk;
// 事件接收器类的事件函数定义
_ATL_FUNC_INFO AddResultInfo = { CC_STDCALL, VT_BOOL, 1,
{VT_I1 | VT_BYREF} };
// ATL 需要用到的 COM 服务器模块对象, 必须定义
CComModule _Module;
// 初始化设备管理对象并绑定事件
void InitManagerEvent()
{
HRESULT hResult = S_FALSE;
if (NULL == g_pManager)
{
// 以单线程方式创建 COM 对象( 应用程序调用 COM 库函数之前必须初始化 COM 库)
::CoInitialize(NULL);
// 创建 fairsion 的设备管理类对象, 此对象在程序中创建一次即可, 建议为全局变量
::CoCreateInstance(CLSID_DeviceManager, 0, CLSCTX_INPROC, __uuidof(IDeviceManager), (void**)& g_pManager);
if (g_pManager)
{
// 初始化
g_pManager->Initialize();
// 创建事件接收对象
CComObject<CSinkAddResult>::CreateInstance(&g_pSinkAddResult);
// 将事件接收器与设备管理类对象绑定
IUnknownPtr pUnk(g_pManager);
g_pUnk = pUnk;
hResult = g_pSinkAddResult->DispEventAdvise(g_pUnk,&__uuidof(_IMsgNoticeEvents));
if (SUCCEEDED(hResult))
g_pSinkAddResult->AddRef();
}
}
}
// 逆初始化设备管理对象, 并关闭事件连接点
void ReleaseManagerEvent()
{
if (g_pManager)
{
// 逆初始化
g_pManager->Terminate();
// 关闭事件连接点, 注意要与 DispEventAdvise 一一对应, 否则会出错
g_pSinkAddResult->DispEventUnadvise(g_pUnk,&__uuidof(_IMsgNoticeEvents));
while (true)
{
if (0 == g_pSinkAddResult->Release()) break;
}
g_pSinkAddResult = NULL;
g_pManager->Release();
g_pManager = NULL;
g_pUnk = NULL;
}
}
VARIANT_BOOL __stdcall CSinkAddResult::ImageSuccess(ICamera* pCamera)
{
// 图像采集成功事件响应函数, pCamera 为对应相机对象
return VARIANT_TRUE;
}
VARIANT_BOOL __stdcall CSinkAddResult::CameraOnline(ICamera* pCamera)
{
// 相机上线事件响应函数, pCamera 为对应相机对象
return VARIANT_TRUE;
}
VARIANT_BOOL __stdcall CSinkAddResult::CameraDrop(ICamera* pCamera)
{
// 相机掉线事件响应函数, pCamera 为对应相机对象
return VARIANT_TRUE;
}
VARIANT_BOOL __stdcall CSinkAddResult::CameraDestory(ICamera* pCamera)
{
// 相机资源销毁事件响应函数, pCamera 为对应相机对象
return VARIANT_TRUE;
}
VARIANT_BOOL __stdcall CSinkAddResult::AutoBalanceEnd(ICamera* pCamera)
{
// 自动白平衡完成事件响应函数, pCamera 为对应相机对象
return VARIANT_TRUE;
}

相机图像获取

在获取到相机对象, 完成相机打开和相机启动传输后, 就可以开始相机取图相关工作了。
根据取图方式, 可分为主动和被动( 事件触发) 两种方式。

  • 主动取图方式。当相机可以取图时, 首先调用 ICamera::IsGrabSucceeded 传入超时时间(如 1000ms)来判断在超时时间范围内是否有图像生成成功, 主动获取图像又可分为获取图像指针和拷贝图像内存两种方式。获取图像指针可通过 ICamera::GetBufferICamera::GetRGBBuffer 实现, Camera::GetBuffer 获取到的是原始的 RAW 数据缓冲区指针, 彩色相机需要调用 Raw2Rgb 函数将 RAW 数据转换为 RGB24 彩色数据;ICamera::GetRGBBuffer 可直接获取 RAW 或 RGB 格式的数据缓冲区指针, 具体数据缓冲区图像格式, 由输入参数决定, 可以是 8 位灰度数据, 也可以是 24 位、 32 位 BGR 彩色格式数据。 拷贝图像内存可通过 ICamera::CopyImageBufferICamera::GetRGB32Buffer 函数实现, ICamera::CopyImageBuffer 只能将 RAW 原始数据拷贝至传入缓冲区, 彩色相机需要调用 Raw2Rgb 函数将 RAW 数据转换为 BGR24 彩色数据;ICamera::GetRGB32Buffer 可以通过输入参数来指定拷贝 8 位灰度数据, 或 24 位、 32位 BGR 彩色数据。 获取图像指针的方式仅建议在 C/C++语言下使用, 其他语言可使用拷贝图像内存方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// 获取 RAW 格式图像指针
unsigned char* GetRawImageBuffer(ICamera* pCamera)
{
VARIANT_BOOL bIsOpen = VARIANT_FALSE;
VARIANT_BOOL bIsCapturing = VARIANT_FALSE;
VARIANT_BOOL bIsGrabSucceed = VARIANT_FALSE;
unsigned char* pImgBuffer = NULL;
unsigned char* pErrorBuffer = NULL; 30
try
{
// 判断相机是否打开, 如果没有则打开相机
pCamera->IsOpen(&bIsOpen);
if (VARIANT_FALSE == bIsOpen)
pCamera->Open();
// 判断相机是否启动图像传输, 如果没有则启动相机图像传输
pCamera->IsCapturing(&bIsCapturing);
if (VARIANT_FALSE == bIsCapturing)
pCamera->StartCapture();
// 判断 1000ms 内图像是否生成成功
pCamera->IsGrabSucceeded(1000, &bIsGrabSucceed);
if (VARIANT_TRUE == bIsGrabSucceed)
{
pCamera->GetBuffer(&pImgBuffer);
if (pImgBuffer)
{
// 获取当前图像 RAW 格式数据指针成功
return pImgBuffer;
} else
{
// 当前图像数据丢包, 判定为异常数据
pCamera->GetErrorImage(&pErrorBuffer);
if (pErrorBuffer)
{
// 获取异常图像 RAW 格式数据指针成功
}
}
}
}
catch(...)
{
// 当前相机对象为无效对象, 相机已掉线
}
return NULL;
}
// 获取 BGR24 格式图像指针
unsigned char* GetBGRImageBuffer(ICamera* pCamera)
{
VARIANT_BOOL bIsOpen = VARIANT_FALSE;
VARIANT_BOOL bIsCapturing = VARIANT_FALSE;
VARIANT_BOOL bIsGrabSucceed = VARIANT_FALSE;
unsigned char* pImgBuffer = NULL;
unsigned char* pErrorBuffer = NULL;
try
{
// 判断相机是否打开, 如果没有则打开相机
pCamera->IsOpen(&bIsOpen);
if (VARIANT_FALSE == bIsOpen)
pCamera->Open();
// 判断相机是否启动图像传输, 如果没有则启动相机图像传输
pCamera->IsCapturing(&bIsCapturing);
if (VARIANT_FALSE == bIsCapturing)
pCamera->StartCapture();
// 判断 1000ms 内图像是否生成成功
pCamera->IsGrabSucceeded(1000, &bIsGrabSucceed);
if (VARIANT_TRUE == bIsGrabSucceed)
{
// 第一个参数为数据格式(8:RAW8,24:BGR24,32:BGR32)
// 第二个参数为算法类型(0:低级插值算法,2:高级插值算法)
pCamera->GetRGBBuffer(24, 0, &pImgBuffer);
if (pImgBuffer)
{
// 获取当前图像 BGR24 格式数据指针成功
return pImgBuffer;
} else
{
// 当前图像数据丢包, 判定为异常数据
pCamera->GetErrorImage(&pErrorBuffer);
if (pErrorBuffer)
{
// 获取异常图像 RAW 格式数据指针成功
}
}
}
}
catch(...)
{
// 当前相机对象为无效对象, 相机已掉线
}
return NULL;
  • 被动取图方式(事件触发)。被动取图方式是通过事件触发, 如果有正常图像或异常图像, 会自动触发讲述的图像采集成功事件响应函数, 在事件响应函数内实现上述主动取图的代码即可,建议事件响应函数内仅做图像拷贝操作, 将较为耗时的图像处理及检测放到别的线程函数中。这个没有示例代码。。

-------------本文结束,感谢您的阅读转载请注明原作者及出处-------------


本文标题:Fairsion工业相机开发手册笔记1

文章作者:Shawn Zhou

发布时间:2019年09月28日 - 10:09

最后更新:2019年09月28日 - 12:09

原始链接:http://shawnzhou.xyz/2019/09/28/19-09-28-01/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

知识无价,码字不易。对您有用,敬请打赏。金额随意,感谢关心。
0%