basler 多相机拍照,通过opencv (3.4.1)svm 实时颜⾊分类
(基于C++)
第⼀次写博客还是挺激动的,哈哈哈,先来⼀波⼲货吧,没写过博客,也不知道什么格式合适,⼤家先凑合看吧(笔芯)⼀、背景及实现效果介绍:
1.1背景简介
该⽰例基于⼯业4.0的项⽬,具体产线技术,流程这⾥就不多说了,主要说⼀下我负责的视觉那⼀块。视觉主要识别乐⾼积⽊,识别是否有积⽊,是什么颜⾊?(说到这⾥,估计有的⼈应该知道了我们这个⼯业4.0的东西了。)视觉这⼀部分主要⼯作是接收上位机给我的拍照命令,然后控制相机拍照并把识别结果返回给上位机,通讯采⽤c/s模式,其中相机有三个,收到拍照命令拍照,拍照时间随机。
1.2实现效果
要识别的积⽊原图如下(只选了两张作为代表,实际有多种情况):
然后上⼀张识别结果图吧,先看⼀下效果(另⼀张没保存,懒得再运⾏程序了):
这部分算是整个识别软件的核⼼,主要采⽤svm进⾏多分类,然后根据分类结果重新设置了像素的RGB,所以显⽰才想上图那样,⽬的是更易于观察结果。其中积⽊凸点反光点经过处理之后也基本能识别成要的结果,反光的部分就⽆能为⼒了,黄⾊积⽊和蓝⾊积⽊的反光区就被识别成了背景⿊⾊,但总体来说这个分类结果还是⽐较满意的,已经能够很容易的得到要的结果了。
再来看看软件的界⾯吧
但是识别分类只是其中的⼀部分,要实现预期⽬标还要做很多东西,⽐如怎么同时打开三个相机(Basler GigE),⽽且保证准确拍照(我没记错的话opencv只能打开⼀个相机吧?所以这⾥也算是⼀个技术点了),然后是通讯,界⾯等等⼀系列问题。
第⼀部分主要是简介,所以先上⼀张程序运⾏截图吧
说⼀下这个界⾯,左侧是程序的界⾯(嗯,好像是有点卡通啊,萌萌哒,哈哈哈哈),界⾯上的⽂本是对应的三个相机。右侧打开的⽂本是⽣成的⼯作⽇志(作⽤我就不说了吧),⽂本⽂档随着视觉的软件启动⽽⾃动打开。如果相机拍照,会把照⽚显⽰在界⾯上,写这篇⽂章的时候不在现场,所以没有相机,没有拍照图⽚,但⼤致是介个样⼦滴:
(呃~~,没错,这个是我p上去的!)
然后说⼀下整个开发的过程吧,⾸先从哪⾥⼊⼿呢?是不是很懵?其实当时我也是很懵,因为有⽹路,有相机,有图像处理分类还有软件界⾯等等,所以我就思考了⼀下整个流程,然后⼤致写了⼀下要实现的功能,上图吧(初稿):
这个是当时的原稿,后来才发现这个东西⽤处很⼤,做事之前最好先有个这样的“草稿”,有⼀个规划,这样不仅让⾃⼰⼼⾥有谱,⽽且还能防⽌⾃⼰懵圈,⽐如做到⼀半了突然蒙圈了,不知道⾃⼰在⼲嘛了(不开玩笑,这是真的),然后看⼀下这个流程也能让⾃⼰做⼀个定位,知道⾃⼰在⼲嘛,或者下⼀步⼲嘛。我有时候也会犯迷糊,写着写着不知道⾃⼰在⼲嘛了,然后看看流程就知道我要⼲什么,或者下⼀步要⼲什么,然后为了实现这⼀步的功能,去想办法,如果这个⽅法不⾏,那能不能换个⽅法实现?
现在回过头写⽂档,再把这个流程图给画⼀下吧:
⾸先初始化的东西有,界⾯,相机,svm训练,⽹络通讯,⽇志等等⼀系列东西;
接着是拍照,处理图⽚,再进⾏分类,中间有很多需要优化的东西,⽐如说⼀张图⽚保存下来10M+的⼤⼩,这⽆码⾼清⼤图不经过处理,那计算机得跑到什么时候才能给分完类呀?
好差不多流程弄完了,那接下来开始着⼿吧,理论上⾸先是解决⽹络的问题,测试通讯是否可⾏,这⼀块之前做过项⽬,能保证技术上可⾏,所以接下来开始先考虑相机吧。
⼆、多相机连接并保证可以拍照
背景已经介绍过了,这⾥再重新说⼀下相机这部分要实现的⽬标:三个相机(basler ,GigE接⼝),需要⼀直处于连接状态并保证独⽴随时拍照。
最开始的打算是⽤opencv打开相机:VideoCapture,但是后来发现这个没办法同时打开三个相机,只能打开⼀个相机,默认打开第⼀个先连接的相机,这⾥不多介绍了,具体想了解的话可以查资料。
后来继续查资料,发现basler有⾃⼰的SDK,可以⾃⼰开发相机,于是就装了pylon,接着就是⼀顿安装,配置环境(vs2013),然后看官⽹的sample,安装配置教程这⾥就不表述了,⽹上资料⼀查⼀⼤堆。
经过研究最后确定了⼀种可⾏的⽅案:通过匹配相机的mac,打开对应的相机。
流程图如下:
由于整个⽂件⼯程包含的东西⽐较多,⽽且相机的功能实现已经封装分布在⼯程的不同的类中,没办法完全给贴上来,所以只把核⼼代码给筛检之后贴了出来,如果有什么问题可以交流,下⾯是实现这些上述功能的核⼼代码:
#include <pylon/PylonIncludes.h>
// Namespace for using pylon objects.
using namespace Pylon;
// Namespace for using cout.
//using namespace std;
//这⼀句必须要
PylonInitialize();
//获得设备
CTlFactory& tlFactory = CTlFactory::GetInstance();
//声明设备信息对象,并设置信息
//参数是绑定MAC地址信息
CDeviceInfo Device_info_siasun_A,Device_info_siasun_B,Device_info_ROKAE;
Device_info_siasun_A.SetFullName("Basler acA1300-60gc#0030532699C7#192.168.2.202:3956");
Device_info_siasun_B.SetFullName("Basler acA1300-60gc#0030532699C6#192.168.2.144:3956");
Device_info_ROKAE.SetFullName("Basler acA1300-60gc#0030532699C8#192.168.2.203:3956");
//把信息添加到filter
DeviceInfoList_t Device_filter_siasun_A, Device_filter_siasun_B, Device_filter_ROKAE;
Device_filter_siasun_A.push_back(Device_info_siasun_A);
Device_filter_siasun_B.push_back(Device_info_siasun_B);
Device_filter_ROKAE.push_back(Device_info_ROKAE);
//创建相机对象
CInstantCamera Camera_siasun_A,Camera_siasun_B,Camera_ROKAE;
//注意此处容易出现异常,打开相机异常
//信息匹配,如果匹配成功,打开相机
//连接并打开相机
DeviceInfoList_t device_temp;
if (tlFactory.EnumerateDevices(device_temp, Device_filter_siasun_A) > 0)
{
Camera_siasun_A.Attach(tlFactory.CreateDevice(device_temp[0]));
Camera_siasun_A.Open();
}
if (tlFactory.EnumerateDevices(device_temp, Device_filter_siasun_B) > 0)
{
Camera_siasun_B.Attach(tlFactory.CreateDevice(device_temp[0]));
Camera_siasun_B.Open();
}
if (tlFactory.EnumerateDevices(device_temp, Device_filter_ROKAE) > 0)
{
Camera_ROKAE.Attach(tlFactory.CreateDevice(device_temp[0]));
Camera_ROKAE.Open();
}
//结果指针
//相机拍完照⽚之后会先把数据存⼊内存中,这⾥是放⼊了CGrabResultPtr指针对象中
CGrabResultPtr PtrGrabResult_siasun_A,PtrGrabResult_siasun_B,PtrGrabResult_ROKAE;
//开始抓拍
/*Camera_siasun_A.StartGrabbing(1);
Camera_siasun_B.StartGrabbing(1);
Camera_ROKAE.StartGrabbing(1);
//等待并检测,100ms超时
Camera_siasun_A.RetrieveResult( 100, PtrGrabResult_siasun_A, TimeoutHandling_ThrowException);
Camera_siasun_B.RetrieveResult( 100, PtrGrabResult_siasun_B, TimeoutHandling_ThrowException);
Camera_ROKAE.RetrieveResult( 100, PtrGrabResult_ROKAE, TimeoutHandling_ThrowException);
*/
//1000ms超时
//抓取⼀张图⽚
Camera_siasun_A.GrabOne(1000,PtrGrabResult_siasun_A, TimeoutHandling_ThrowException);
Camera_siasun_B.GrabOne(1000,PtrGrabResult_siasun_B, TimeoutHandling_ThrowException);
Camera_siasun_ROKAE.GrabOne(1000,PtrGrabResult_siasun_ROKAE, TimeoutHandling_ThrowException);
//****************接下来转换图⽚格式
//创建格式转换对象
CImageFormatConverter Format_converter_siasun_A,Format_converter_siasun_B,Format_converter_ROKAE;
CPylonImage PylonImage_Temp_siasun_A,PylonImage_Temp_siasun_B,PylonImage_Temp_ROKAE;
//设定转换格式
Format_converter_siasun_A.OutputPixelFormat = PixelType_BGR8packed;
Format_converter_siasun_A.OutputBitAlignment = OutputBitAlignment_MsbAligned;
namespace是干嘛的Format_converter_siasun_B.OutputPixelFormat = PixelType_BGR8packed;
Format_converter_siasun_B.OutputBitAlignment = OutputBitAlignment_MsbAligned;
Format_converter_ROKAE.OutputPixelFormat = PixelType_BGR8packed;
Format_converter_ROKAE.OutputBitAlignment = OutputBitAlignment_MsbAligned;
Format_converter_siasun_A.Convert(PylonImage_Temp_siasun_A, PtrGrabResult_siasun_A);
Format_converter_siasun_B.Convert(PylonImage_Temp_siasun_B, PtrGrabResult_siasun_B);
Format_converter_ROKAE.Convert(PylonImage_Temp_ROKAE, PtrGrabResult_ROKAE);
好了,相机打开的问题解决了,接下来该处理图像了。(做个⼯程真的是要经历九九⼋⼗⼀难呀,好多莫名的bug)
对,还有⼀个问题就是格式转换,我拍完照⽚之后是pylon的图⽚,需要转换成opencv需要处理的格式也就是Mat,最开始以为挺⿇烦的,想着实在不⾏就先保存到磁盘上,然后再⽤opencv读取过来,后来发现这个⼀⾏代码就搞定了:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论