Baumer工业相机堡盟工业相机如何通过BGAPISDK的软触发实现两相机同步采集(C++)

简介: Baumer工业相机堡盟工业相机如何通过BGAPISDK的软触发实现两相机同步采集(C++)

Baumer工业相机

Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。


Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩展性等特点。

Baumer工业相机由于其性能和质量的优越和稳定,常用于高速同步采集领域。


Baumer工业相机的高速同步采集的技术背景

多个工业相机的高速同步采集是一个技术过程,涉及到从多个相机高速采集图像,并将其整合到一个同步系统中。这个过程需要先进的硬件和软件技术,以及对摄像机同步、数据传输和图像处理的理解。


为了实现多个工业相机的高速同步采集,有必要使用支持硬件触发并可通过先进软件进行同步的相机。例如,可以使用主时钟信号或脉冲发生器来触发摄像机,以确保所有摄像机在完全相同的时刻捕获图像。


此外,还需要一个高速数据传输网络,以确保每个摄像机捕获的大量图像数据能够快速传输到中央处理单元(CPU)进行进一步分析。这通常涉及使用高速通信协议,如千兆以太网或CoaXPress。


在图像处理方面,需要专门的软件来处理由多个摄像机产生的大量数据。这可能包括实时视频处理算法、运动检测、模式识别或其他先进技术,旨在从捕获的图像中提取有用信息。


总的来说,实现多个工业相机的高速同步采集是一项复杂的技术任务,需要多个学科的专业知识,包括硬件设计、软件开发和图像处理。


本文这里只简单介绍Baumer的两个工业相机在设置相同参数时,使用软触发进行的同步采集


Baumer工业相机通过BGAPI SDK在回调函数里同步保存图像

下面介绍在C++里两个Baumer工业相机在软触发的情况下进行同步采集


工业相机在回调函数BufferEvent保存

在回调函数里保存图像,使用毫秒级的时间戳去存储图像的名称,C++调用代码如下所示:

//图像回调函数
//==================
void BGAPI2CALL BufferHandler( void * callBackOwner, Buffer * pBufferFilled )
{
  CGigeDemoDlg* pDlg = (CGigeDemoDlg*)callBackOwner;
  unsigned char* imagebuffer = NULL;
  USES_CONVERSION;
  try
  {
  if(pBufferFilled == NULL)
  {
  }
  else if(pBufferFilled->GetIsIncomplete() == true)
  {
    // queue buffer again
    pBufferFilled->QueueBuffer();
  }
  else
  {
    pDlg->FrameID= pBufferFilled->GetFrameID();                                                 //获取当前图像FrameID显示帧率
    int width = 0, height = 0;
    width = (int)pBufferFilled->GetWidth();height = (int)pBufferFilled->GetHeight();    //获取当前图像像素长宽
    CString PixelFormat1 = (CString)pBufferFilled->GetPixelFormat();        //获取当前图像像素格式
    imagebuffer = (BYTE*)((bo_int64)pBufferFilled->GetMemPtr()+pBufferFilled->GetImageOffset());//获取当前图像数据
    #pragma region //保存图像功能
    if(pDlg->m_bSaveImage &&!pDlg->m_strDirectory.IsEmpty())
    {   
    CTime time = CTime::GetCurrentTime(); 
    CString strtime1;
    strtime1.Format(_T("\\%4d%2d%2d%2d%2d%2d"),time.GetYear(),time.GetMonth(),time.GetDay(),time.GetHour(),time.GetMinute(),time.GetSecond());
    SYSTEMTIME st;
    GetSystemTime(&st);  // 获取系统时间,精确到毫秒
    int msecond = st.wMilliseconds;  // 获取毫秒数
    CString strtime;
    strtime.Format(_T("\\%s-%d"),strtime1,msecond);
    CString  strpath = pDlg->m_strDirectory+strtime+"-";
    pDlg->SaveImageMono(strpath, imagebuffer,width,height);
    pDlg->m_bSaveImage = false;
    }
    #pragma endregion 
    Gdiplus::Rect rc = Gdiplus::Rect(0,0,width,height);
    #pragma region 黑白相机代码:像素格式为mono时转Bitmap的代码,彩色相机此处代码不同
    if(pDlg->m_pBitmap == NULL)
    {
    pDlg->m_pBitmap = new Gdiplus::Bitmap(width,height,PixelFormat8bppIndexed);
    }
    Gdiplus::BitmapData lockedbits;
    Gdiplus::ColorPalette * pal = (Gdiplus::ColorPalette*)new BYTE[sizeof(Gdiplus::ColorPalette)+255*sizeof(Gdiplus::ARGB)];
    pal->Count=256;
    for(UINT i=0;i<256;i++)
    {
    UINT color=i*65536+i*256+i;
    color= color|0xFF000000;
    pal->Entries[i]=color;
    }
    pDlg->m_pBitmap->SetPalette(pal);
    Gdiplus::Status ret = pDlg->m_pBitmap->LockBits(&rc,Gdiplus::ImageLockModeWrite,PixelFormat8bppIndexed,&lockedbits);
    BYTE* pixels = (BYTE*)lockedbits.Scan0;
    BYTE* src = (BYTE*)imagebuffer;
    for (int row = 0; row < height; ++row) 
    {
    CopyMemory(pixels, src, lockedbits.Stride);
    pixels += width;
    src += width;
    }
    pDlg->m_pBitmap->UnlockBits(&lockedbits);
    #pragma endregion 
    #pragma region //将图像显示在PictureControl控件上
    HDC hDC = ::GetDC(pDlg->m_stcPicture.m_hWnd);
    Gdiplus::Graphics GdiplusDC(hDC);
    CRect rcControl;
    pDlg->m_stcPicture.GetWindowRect(&rcControl);
    Gdiplus::Rect rtImage(0,0,rcControl.Width(),rcControl.Height());
    GdiplusDC.DrawImage(pDlg->m_pBitmap,rtImage,0,0,width,height, Gdiplus::UnitPixel);
    delete []pal;
    ::ReleaseDC(pDlg->m_stcPicture.m_hWnd,hDC);
    delete pDlg->m_pBitmap ;
    pDlg->m_pBitmap =NULL;
    #pragma endregion 
    // queue buffer again
    pBufferFilled->QueueBuffer();
  }
  }
  catch (BGAPI2::Exceptions::IException& ex)
  {
  CString str;
  str.Format(_T("ExceptionType:%s! ErrorDescription:%s in function:%s"),ex.GetType(),ex.GetErrorDescription(),ex.GetFunctionName());  
  } 
}

SaveImageMono保存图像内存作为图像的函数

bool CGigeDemoDlg::SaveImageMono(CString str,BYTE* bdata,int width,int height)
{  
  if (str.IsEmpty()) return false;
  HANDLE hFile = CreateFile(str,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  //DWORD dwError = GetLastError();
  if (!hFile || hFile == INVALID_HANDLE_VALUE) 
  return false;
  // 
  BITMAPFILEHEADER  FileHeader;
  BITMAPINFOHEADER  InfoHeader;
  RGBQUAD rgbQuad[256];
  DWORD dwImageSize    = width*height;
  FileHeader.bfType    = 0x4D42; // "BM"
  FileHeader.bfSize    = sizeof(FileHeader) + sizeof(InfoHeader) + dwImageSize;
  FileHeader.bfReserved1    = FileHeader.bfReserved2 = 0;
  FileHeader.bfOffBits    = sizeof(FileHeader) + sizeof(InfoHeader);
  ZeroMemory(&InfoHeader, sizeof(InfoHeader));
  InfoHeader.biWidth    = width;
  InfoHeader.biHeight    = -height;
  InfoHeader.biSizeImage    = dwImageSize;
  InfoHeader.biBitCount   = 8;
  InfoHeader.biCompression  = BI_RGB;
  InfoHeader.biPlanes    = 1;
  InfoHeader.biSize    = sizeof(BITMAPINFOHEADER);
  InfoHeader.biXPelsPerMeter  = 0xEC4;
  InfoHeader.biYPelsPerMeter  = 0xEC4;
  for ( int i=0;i<256; i++ )
  {
  rgbQuad[i].rgbBlue = (BYTE)i;
  rgbQuad[i].rgbGreen = (BYTE)i;
  rgbQuad[i].rgbRed = (BYTE)i;
  rgbQuad[i].rgbReserved = 0;
  }
  DWORD Written;
  WriteFile(hFile, (LPVOID)&FileHeader,sizeof(BITMAPFILEHEADER),&Written, NULL);
  WriteFile(hFile, (LPVOID)&InfoHeader,sizeof(BITMAPINFOHEADER),&Written, NULL);
  WriteFile(hFile, (LPVOID)rgbQuad,256*sizeof(RGBQUAD),&Written,NULL);
  BYTE* src = bdata;
  //int stride = m_Width*4;
  //DWORD bytes = m_Width * 4;
  //for (unsigned long y = 0; y < NHEIGHT; y++) {
  WriteFile(hFile, (LPVOID)src,dwImageSize,&Written,NULL);
  //}
  CloseHandle(hFile);
  return true;
}

工业相机使用软触发进行同步采集

使用软件触发进行同步采集,C++调用代码如下所示:

m_pDevice->GetRemoteNode("TriggerMode")->SetString("Off"); //关闭触发模式,进入自由采集图片流模式
  m_pDevice->GetRemoteNode("AcquisitionStart")->Execute();
void CGigeDemoDlg::TriggerCameras()
{
  if (lisDev.Count ==2)
    {
        fullsavebmp1 = true; fullsavebmp2 = true;
        if ((lisDev[0]->GetRemoteNode("TriggerMode")->GetString() == "Software"
            || lisDev[0]->GetRemoteNode["TriggerSource"]->GetString() == "SoftwareTrigger")
            && (lisDev[1]->GetRemoteNode["TriggerSource"]->GetString() == "Software"
            || lisDev[1]->GetRemoteNode["TriggerSource"]->GetString() == "SoftwareTrigger"))
        {
            int count1 = 0;
            BGAPI2::Device * dev1 = lisDev[0];
            BGAPI2::Device * dev2 = lisDev[1];
            #pragma region 线程自动触发相机
    AfxBeginThread(AutoTriggerSoftwareThread1(dev1) , (void*)this); 
    AfxBeginThread(AutoTriggerSoftwareThread2(dev2) , (void*)this);   
    #pragma endregion  
        }
    }
    if (lisDev.Count == 1)
    {
        if ((lisDev[0]->GetRemoteNode("TriggerMode")->GetString() == "Software"
            || lisDev[0]->GetRemoteNode["TriggerSource"]->GetString() == "SoftwareTrigger"))
        {
              dev->GetRemoteNode("TriggerSoftware")->Execute();
        }
    }
}

工业相机的软件触发命令线程函数

工业相机软件触发命令,C++调用代码如下所示:

UINT CGigeDemoDlg::AutoTriggerSoftwareRun1(LPVOID pParam)
{
  CGigeDemoDlg *dlg = (CGigeDemoDlg *)pParam;
  dlg->TriggerSoftwareRun1();
  return 0;
}
CString strtimeStart; 
void CGigeDemoDlg::TriggerSoftwareRun1()
{
  try
  {  
  bool m_bRun0 = true;
  while (m_bRun0)
  {
    dev->GetRemoteNode("TriggerSoftware")->Execute();    
    m_bRun0  = false;      
  }  
  }
  catch (int e)
  {
  MessageBox(_T("Camera FullFrameSaveimage Error"));
  }
}
UINT CGigeDemoDlg::AutoTriggerSoftwareRun2(LPVOID pParam)
{
  CGigeDemoDlg *dlg = (CGigeDemoDlg *)pParam;
  dlg->TriggerSoftwareRun2();
  return 0;
}
CString strtimeStart; 
void CGigeDemoDlg::TriggerSoftwareRun2()
{
  try
  {  
  bool m_bRun0 = true;
  while (m_bRun0)
  {
    dev->GetRemoteNode("TriggerSoftware")->Execute();    
    m_bRun0  = false;      
  }  
  }
  catch (int e)
  {
  MessageBox(_T("Camera FullFrameSaveimage Error"));
  }
}

Baumer工业相机的软件触发同步采集测试图

8.png


使用软件触发同步采集后保存的图像名称如下所示:


7.png

Baumer工业相机的高速同步采集功能的优势

高速同步采集: 堡盟工业相机具有高速同步采集功能,可以同时采集多个图像。这使它们成为时间至关重要的应用的理想选择,例如在机器视觉、机器人和自动化领域。


提高精度和准确度: 堡盟工业相机的高速同步采集使图像数据的准确性和精确性得到提高。这在需要进行高精确度测量的应用中尤为重要。


提高生产力: 堡盟工业相机具有一次采集多幅图像的能力,有助于提高制造业和其他工业流程的生产率。它们可以帮助实时识别错误和低效率,以便立即采取纠正措施。


定制化: 堡盟工业相机可进行定制,以满足不同应用的具体需求。它们提供各种选项,包括分辨率、帧率和动态范围。


耐用性: 堡盟工业相机的设计能够承受恶劣的环境,并且经久耐用。它们能抵抗冲击和振动,并能在极端温度下工作,使它们适合在工业和制造业环境中使用。


Baumer工业相机的高速同步采集功能的行业应用

汽车行业: 在汽车行业,高速相机被用来捕捉移动物体的图像,如车辆、行人和骑自行车的人。这些数据被用于各种应用,包括避免碰撞系统、高级驾驶辅助系统和自动停车系统。


制造行业: 在制造业中,高速相机被用来监测装配线和流程,检查缺陷,并确保质量控制。它们还被用来捕捉快速移动的机器和设备的图像,用于维护和排除故障。


制药业: 在制药业,高速相机被用来捕捉药物开发和测试过程的图像,包括分析液体、气溶胶和悬浮液中颗粒的行为。


体育行业: 在体育行业,高速相机被用来捕捉运动员运动中的图像,使教练和训练员能够分析技术并作出调整,以达到最佳表现。


航空航天工业: 在航天工业中,高速相机被用来捕捉飞机在测试和开发过程中的图像,包括分析风洞测试和高速飞行。


这些只是多工业相机的高速同步采集功能的几个行业应用。该技术在各行业还有很多应用。

目录
相关文章
|
6月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
496 0
|
9月前
|
监控 API 开发工具
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取每张图像的微秒时间和FrameID功能(C++)
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取每张图像的微秒时间和FrameID功能(C++)
97 0
|
9月前
|
数据采集 API 开发工具
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用Force IP强制修改网口IP功能(C++)
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用Force IP强制修改网口IP功能(C++)
74 0
|
3天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
69 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
51 13
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
53 5
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
41 5
|
1月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
48 4
|
1月前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
34 3