# project joee 开发日志 09.25

#                  需求

将原本采集卡连接单个摄像头的方案修改为:采集卡连接两个摄像头,并且将两个摄像头采集到的图像信息合并成一张图像。

#           -part1 - 需求分析

# 1. 驱动调用

  首先要修改采集卡调用部分的代码,使得采集卡 Devices::Grabber 部分能够接收和区分不同摄像头的句柄,并且调用相关 API 时加以区分这部分应该不难实现。

# 2. 回调处理

  其次当采集卡产生回调时,应根据不同摄像头相同序号的帧进行帧同步,标识出两个相同时刻的帧,问题在于,两个摄像头产生的缓冲区图像能否保证是在同一时刻产生的,以及采集卡缓冲区应该设置为多大。

查阅开发手册:

# 2.1. 图像缓冲区的三种状态:

  Empty 空状态,意味着当前缓冲区没有相机采集到的图像数据

  Full 满状态,意味着当前缓冲区已经被相机采集到的图像数据填满

  Transfer 传输状态,意味着相机正在向该缓冲区内传输数据

  用户可以通过 IKapGetBufferStatus 来获取指定传输缓冲区的状态。

# 2.2. 图像缓冲区的状态切换

  图像缓冲区的状态会在如下时刻切换:

  图像采集 启动 时,所有图像缓冲区被设置为 Empty

  当一帧完整的数据被传输到该图像缓冲区后,设置该图像缓冲区状态为 Full

  当图像数据正在向该图像缓冲区传输时,设置该图像缓冲区状态为 Transfer , 对于 线扫描相机 ,可以在 Transfer 状态获取图像缓冲区已经传输的有效行数

  当 IKEvent_FrameReady 的回调函数执行完成后(无论是否设置),都会自动设置图像缓冲区状态为 Empty

手册img1

手册img2

手册img3

1
2
3
4
//获取采集模式和数据传输方式的代码
int transfer_mode = (int)FrameTransferMode.IKP_FRAME_TRAANSFER_SYNCHRONOUS_NEXT_EMPTY_WITH_PROTECT;
CheckIKapBoard(IKapBoard.IKapSetInfo(m_hBoard,(uint)INFO_ID.IKP_FRAME_TRANSFER_MODE,transfer_mode));

配置参数的相关代码

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
//进行参数设置
private void Configure(IntPtr m_hBoard)
{

// 从文件读取配置信息 (*.vlcf)

CheckIKapBoard(IKapBoard.IKapLoadConfigurationFromFile(m_hBoard,
Application.StartupPath + "\\parameter\\" + configFileName + ".vlcf"));

// 图像缓冲区设置
CheckIKapBoard(IKapBoard.IKapSetInfo(m_hBoard, (uint)INFO_ID.IKP_FRAME_COUNT, m_nTotalFrameCount));

// 超时设置
int timeout = -1;
CheckIKapBoard(IKapBoard.IKapSetInfo(m_hBoard, (uint)INFO_ID.IKP_TIME_OUT, timeout));

//采集模式 非阻塞
int grab_mode = (int)GrabMode.IKP_GRAB_NON_BLOCK;
CheckIKapBoard(IKapBoard.IKapSetInfo(m_hBoard, (uint)INFO_ID.IKP_GRAB_MODE, grab_mode));

// 数据传输模式
int transfer_mode = (int)FrameTransferMode.IKP_FRAME_TRANSFER_SYNCHRONOUS_NEXT_EMPTY_WITH_PROTECT;
CheckIKapBoard(IKapBoard.IKapSetInfo(m_hBoard, (uint)INFO_ID.IKP_FRAME_TRANSFER_MODE, transfer_mode));

int ret;
// 获取图像宽度和高度
ret = IKapBoard.IKapGetInfo(m_hBoard, (uint)INFO_ID.IKP_IMAGE_WIDTH, ref nWidth);
CheckIKapBoard(ret);
ret = IKapBoard.IKapGetInfo(m_hBoard, (uint)INFO_ID.IKP_IMAGE_HEIGHT, ref nHeight);
CheckIKapBoard(ret);

}

当前使用的传输模式为

IKP_FRAME_TRANSFER_SYNCHRONOUS_NEXT_EMPTY_WITH_PROTECT
也就是第三种模式: 同步保护模式
在这种模式下,需要建立一定的图像缓冲区,但是需要及时取出图像帧,如果要保证不会出现帧丢失,就要适当增加缓冲区大小。除此之外,还需要手动对图像进行编号,特别是当图像采集设备多于一个时,要对原有的图像编号逻辑进行更换,以保证图像帧同步。

# 3. 图像后处理

在确保帧同步之后,就需要对图像进行合并。
由于之前确定的方案为从内存中提取 bitmap 数据直接处理,但是官方源码给的方案是保存成位图文件,所以这部分需要进行额外一些操作。

1
2
3
4
5
6
public System.Drawing.Imaging.BitmapData LockBits 
(System.Drawing.Rectangle rect,
System.Drawing.Imaging.ImageLockMode flags,
System.Drawing.Imaging.PixelFormat format);


对获取到的图像帧 A B 分别进行 LockBits 操作,使得能够直接对位图内存进行操作,然后通过逐行合并两个位图行得到新行,最后生成新的位图。

#           -part2 - 处理过程

使用 API IKapBoard.IKapGetBoardCount 确定当前连接到的设备数量

1
2
3
uint nPCIeDevCount = IKapBoard.IKapGetBoardCount(
(int)BoardType.IKBoardPCIE, ref nPCIeDevCount);

之后部分就跟之前的差不多了,添加 for 循环语句,并在 pParam 参数字段中传入该设备的标识号,这样就能在回调中识别该帧是由哪个设备产生的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (uint i = 0; i < nPCIeDevCount; i++)
{
//获取当前设备句柄
IntPtr m_hBoard = IKapBoard.IKapOpen((int)BoardType.IKBoardPCIE, i);
if (m_hBoard.Equals(new IntPtr(-1)))
{
//打开设备
log._.Info("Opening Camera ...");
OpenDevice(m_hBoard);
//设置参数
log._.Info("Configuring Camera ...");
Configure(m_hBoard);
//注册回调函数
RegisterCallback(m_hBoard,(IntPtr) i);
}

}

现在应该能识别到多个采集设备了,后续要到能够进行远程设备连接才能进行测试了。

图像后处理部分,根据需求更改再决定是否添加。