Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。
Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩展性等特点。
Baumer工业相机中原始图像可以通过BGAPI SDK和其它图像处理库进行联合保存成各种格式的图像,比如和Halcon联合、比如和OpenCV联合、比如和GDI+图像库联合等等,若在高速采图领域,任何的图像转换过程可能都会造成效率的降低,因此直接保存原始数据的图像是最为快速的。
工业相机通常通过一个传感器实时捕捉高分辨率的图像,该传感器将光转换为电信号。这些信号然后由相机的图像处理硬件和软件转换成数字二进制图像。二进制图像由黑色和白色像素组成,每个像素由一个比特表示,要么是0,要么是1,0代表黑色像素,1代表白色像素。
工业相机经常使用二进制图像,因为它们在某些应用中更有效,更容易处理。二进制图像比彩色图像或灰度图像占用的存储空间更少。这使它们成为需要存储或传输大量图像的应用的理想选择,例如在制造业质量控制或安全监控中。二进制图像也更容易使用算法和软件进行分析和处理,这对检测缺陷、跟踪物体和识别图像数据中的模式很有帮助。
总的来说,工业相机将图像保存为二进制图像的技术背景是基于工业和制造业应用中对高速、有效处理大量图像数据的需要。
有关于Baumer万兆网工业相机堡盟相机VLXT-28M.I如何做全帧率图像存储到本地的介绍,之前已经有相关的技术博客可以参考:
Baumer万兆网工业相机堡盟相机VLXT-28M.I如何做全帧率图像存储到本地
这里主要描述如何在C++的平台下实现通过BGAPI SDK实现二进制保存图像功能的核心代码
本文介绍使用BGAPI SDK对Baumer的工业相机进行开发时,使用通过BGAPI SDK获取相机的原始图像数据Byte*,然后直接将图像数据转为二进制图像保存到本地。
一般讲Byte图像数据转换为二进制进行保存可以分为以下几步:
1、将Byte图像加载到内存中,即创建一个字节数组来保存数据。
2、确定图像的尺寸(宽度和高度),并计算出像素总数。
3、分配第二个相同大小的字节数组来保存二进制数据。
4、循环浏览图像中的每个像素,将其颜色值从字节转换成二进制(0或1)
5、将二进制数据阵列存储在一个文件或数据库中,用于本地存储。
如下为核心代码实现步骤:
C++环境下将Byte*图像保存为二进制图像如下所示:
unsigned char* imageData = // Byte*图像数据
int imageSize = // 图像数据的大小
std::vector<unsigned char> binaryData(imageSize * 8);
for (int i = 0; i < imageSize; i++) {
for (int j = 0; j < 8; j++) {
binaryData[i * 8 + j] = (imageData[i] >> j) & 1;
}
}
下面为在在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 strtime;
strtime.Format(_T("\\%4d%2d%2d%2d%2d%2d"),time.GetYear(),time.GetMonth(),time.GetDay(),time.GetHour(),time.GetMinute(),time.GetSecond());
CString strpath = pDlg->m_strDirectory+strtime+".jpg";
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
// 将Bitmap对象转换为Image对象
Image* img = (Image*)m_pBitmap.GetThumbnailImage(width, height, NULL, NULL);
//将Image图像进行JPEG图像压缩,压缩质量80
CString OutputImagePath = _T("CompressedImage.jpg")
CompressImage(img , OutputImagePath , 80);
#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());
}
}
下面为使用将二进制数据保存为图像文件算法的核心代码,如下所示:
int width = // 图像的宽度
int height = // 图像的高度
std::ofstream outFile("binary_image.bmp", std::ios::binary);
if (outFile) {
// BMP文件头
char bmpFileHeader[14] = { 'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0 };
// BMP信息头
char bmpInfoHeader[40] = { 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// 计算图像数据的大小
int imageDataSize = ((width * height * 1 + 31) / 32) * 4;
// BMP文件头中的文件大小
*(int*)&bmpFileHeader[2] = 54 + imageDataSize;
// BMP信息头中的图像宽度
*(int*)&bmpInfoHeader[4] = width;
// BMP信息头中的图像高度
*(int*)&bmpInfoHeader[8] = height;
// BMP信息头中的图像数据大小
*(int*)&bmpInfoHeader[20] = imageDataSize;
// 写入BMP文件头和BMP信息头
outFile.write(bmpFileHeader, 14);
outFile.write(bmpInfoHeader, 40);
// 写入图像数据
for (int i = height - 1; i >= 0; i--) {
for (int j = 0; j < width; j++) {
unsigned char pixel = 255 * binaryData[i * width + j];
outFile.write((char*)&pixel, 1);
}
// 每行的字节数必须是4的倍数
int paddingSize = ((width * 1 + 3) / 4) * 4 - width;
if (paddingSize > 0) {
char padding[3] = { 0, 0, 0 };
outFile.write(padding, paddingSize);
}
}
outFile.close();
}
减少了存储空间: 与全色或灰度图像相比,二进制图像占用的存储空间要少得多。这意味着你可以在你的设备上存储更多的图像,这对内存有限的设备特别有用。
更快的处理: 二进制图像比全色或灰度图像更容易被计算机处理。这意味着你的图像分析软件可以更快地分析图像,允许更快的决策和更有效的数据处理。
提高准确性:由于二进制图像更简单,它们更容易准确分析。这对图像识别软件特别有用,因为二进制图像可以使它更容易区分图像中的物体或特征。
一致性: 因为二元图像仅由黑白像素组成,它们的视觉外观更加统一。这使得它们更容易与其他图像进行比较,并为图像分析建立一个标准。
兼容性: 许多图像分析软件是专门为处理二进制图像而设计的,使它们成为需要精确和一致的图像分析的工业相机的理想格式。这意味着通过SDK使用二进制图像可以提高你的相机与其他软件应用的兼容性。
质量控制和检查: 二进制图像可用于各种工业制造过程中的缺陷检测和分类,如半导体、电子、汽车和制药行业。
监视和安全: 二进制图像被广泛用于监控系统,以检测异常情况,识别人脸,或跟踪移动的物体。
医学成像: 二元图像通常用于医学成像模式,如X射线、CT和MRI,以分割和分析感兴趣的病理区域。
机器人学和自动化: 二进制图像可用于机器人技术,用于工业环境中的物体识别、跟踪和定位,如仓库、工厂和物流中心。
农业和环境监测: 二进制图像可用于分析作物健康,识别植物疾病,检测害虫,或监测森林火灾、洪水和自然灾害。
综上所述,通过SDK使用工业相机和二进制图像处理,可以提高各种应用的准确性、速度和可靠性,从而使不同行业受益匪浅。