前言
本篇博客稍微记录一下我所写的插件。具体内容是有关于大恒相机的,关于这个相机,相信搜索到这个博客的应该都有所了解了,关于这个插件,我所完成的功能是:利用该相机提供的SDK完成主动采图,回调采图,以及显示出该相机的参数列表这三个主要的功能。下面就是正文,我只会贴属于这个插件的代码,其他部分就不贴出来了。所以,应该只能给你编写的代码一些启示,但就目前这些代码,却没办法运行起来这个代码的,你应该根据软件的功能去继承一些虚函数,就可以完成这个插件。
正文
准备工作
首先,开始写这个插件肯定是从下载这个相机的SDK开始的,下面就给出下载链接。
大恒相机SDK,我们需要从这个软件的安装目录中拿出我们所需要的东西,需要的东西我也打包成一个链接:
大恒相机必备材料。 如果没积分的话可私信。
设备的初始化
下面这个函数的主要功能就是完成设备的初始化工作,下面这个函数没有什么跟那个SDK挂上钩的地方,接下去的这个函数比较重要。
bool MDeviceDahengG3UC::InitDevice(QString index,QString identifier)
{
m_cCameraID = index;
m_cIdentifier = identifier;
bool ret = false;
#ifdef WIN32_DAHENG_GEV
if(DeviceLoading(m_cIdentifier))
{
m_pConfigureObj = new MConfigureDahengG3UC();
m_pConfigureObj->InitConfigure(m_cCameraID,this);
m_pConfigureObj->RegisterConfigureCallback(m_fXmlChange,m_pCallUser);//注册配置文件的回调函数
QVariant wVal;
QVariant hVal;
QVariant pixelForamt;
QVariant frameRate;
this->GetParameter("Width",wVal);
this->GetParameter("Height",hVal);
this->GetParameter("PixelFormat",pixelForamt);
if(IsExistParameter("ResultingFrameRate"))
{
qDebug()<<"resultingFrameRate";
this->GetParameter("ResultingFrameRate",frameRate);
}
else if(IsExistParameter("ResultingFrameRateAbs"))
{
this->GetParameter("ResultingFrameRateAbs",frameRate);
}
m_iWidth = wVal.toInt();
m_iHeight = hVal.toInt();
m_cPixelFormat = pixelForamt.toString();
m_fFrameRate = frameRate.toFloat();
ret = true;
}
#endif
return ret;
}
下面这个函数就有比较多值得学习的地方,以下讲的地方都是从SDK中得出的。
首先,先给出整个具体的函数,然后再进行解析:
bool MDeviceDahengG3UC::DeviceLoading(QString identifier)
{
m_bLoaded = false;
bool bIsDeviceOpen = false; ///< 设备是否已打开标识
bool bIsStreamOpen = false; ///< 设备流是否已打开标识
#ifdef WIN32_DAHENG_GEV
int nRet = -1;
IGXFactory::GetInstance().Init();
GxIAPICPP::gxdeviceinfo_vector vectorDeviceInfo;
IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);
//判断当前设备连接个数
if (vectorDeviceInfo.size() <= 0)
{
qDebug()<<"No Device!";
return m_bLoaded;
}
else
{
for (uint n = 0; n < vectorDeviceInfo.size(); n++)
{
//判断枚举到的设备是否为Gige
bool accessStatus = vectorDeviceInfo[n].GetAccessStatus();
if(accessStatus)
{
GxIAPICPP::gxstring SerialNumber;
QString SerialNumber2;
GX_DEVICE_CLASS_LIST gClass2 = vectorDeviceInfo[n].GetDeviceClass();
SerialNumber = vectorDeviceInfo[n].GetSN();
SerialNumber2 = static_cast<QString>(SerialNumber);
if(SerialNumber2.length()>=16)
SerialNumber2 = SerialNumber2.left(16);
if(identifier.contains(SerialNumber2))
{
m_cSerialNumber = SerialNumber2;
m_cModelName = static_cast<QString>(vectorDeviceInfo[n].GetModelName());
m_cCameraVendor = vectorDeviceInfo[n].GetVendorName();
m_cUserDefine = vectorDeviceInfo[n].GetUserID();
m_cDeviceVersion = vectorDeviceInfo[n].GetDisplayName();
GxIAPICPP::gxstring strSN = vectorDeviceInfo[n].GetSN();
m_objDevicePtr = IGXFactory::GetInstance().OpenDeviceBySN(strSN,GX_ACCESS_EXCLUSIVE);
m_objStreamPtr = m_objDevicePtr->OpenStream(0);
m_pCaptureEventHandler = new CSampleCaptureEventHandler();
m_bLoaded = true;
bIsDeviceOpen = true;
break;
}
}
}
}
return m_bLoaded;
#endif
}
- 首先,是相机设备的初始化
IGXFactory::GetInstance().Init();//这个是进行实例的初始化
GxIAPICPP::gxdeviceinfo_vector vectorDeviceInfo;
IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);
//通过这个就可以获得相关的设备信息vectorDeviceInfo
- 接下来,就是获得当前设备的所有信息,这里就列举一些信息的获得就好了。这里可以稍微注意一下,强制类型转换在这里的应用。
GxIAPICPP::gxstring SerialNumber;
QString SerialNumber2;
GX_DEVICE_CLASS_LIST gClass2 = vectorDeviceInfo[n].GetDeviceClass();
SerialNumber = vectorDeviceInfo[n].GetSN();
SerialNumber2 = static_cast<QString>(SerialNumber);
- 接下来,根据设备序列号打开设备并获得实例
m_objDevicePtr = IGXFactory::GetInstance().OpenDeviceBySN(strSN,GX_ACCESS_EXCLUSIVE);
- 打开流对象
m_objStreamPtr = m_objDevicePtr->OpenStream(0);
上面这个就是对这个函数的一些解析了。如果有不懂的可以查下文档,肯定没有文档讲的细致,这里只是提出了一个解决问题的思路。
设备信息的获取
MDeviceDahengG3UCInfo.cpp
下面这个函数主要是获取根据SDK获取对象的相机信息,并且是相对上面那个函数会较全。
首先,这里先给出完整的这个函数,你可以大概先瞄一眼大概。
bool MDeviceDahengG3UCInfo::SearchDeviceInfos(QStringList &lInfo)
{
bool ret = false;
#ifdef WIN32_DAHENG_GEV
int nRet = -1;
IGXFactory::GetInstance().Init();
GxIAPICPP::gxdeviceinfo_vector vectorDeviceInfo;
IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);
if(vectorDeviceInfo.size()>0)
{
for (uint32_t i = 0; i < vectorDeviceInfo.size(); i++)
{
if(vectorDeviceInfo[i].GetDeviceClass() == GX_DEVICE_CLASS_GEV||vectorDeviceInfo[i].GetDeviceClass()==GX_DEVICE_CLASS_U3V)
{
GxIAPICPP::gxstring ModelName = vectorDeviceInfo[i].GetModelName();
QString ModelName2 = static_cast<QString>(ModelName);
GxIAPICPP::gxstring CameraVendor = vectorDeviceInfo[i].GetVendorName();
QString CameraVendor2 = static_cast<QString>(CameraVendor);
GxIAPICPP::gxstring UserID = vectorDeviceInfo[i].GetUserID();
QString UserID2 = static_cast<QString>(UserID);
GxIAPICPP::gxstring DeviceID = vectorDeviceInfo[i].GetDeviceID();
QString DeviceID2 = static_cast<QString>(DeviceID);
GxIAPICPP::gxstring DeviceIP = vectorDeviceInfo[i].GetIP();
QString DeviceIP2 = static_cast<QString>(DeviceIP);
GxIAPICPP::gxstring DeviceSN = vectorDeviceInfo[i].GetSN();
QString DeviceSN2 = static_cast<QString>(DeviceSN);
GxIAPICPP::gxstring DeviceDisplayName = vectorDeviceInfo[i].GetDisplayName();
QString DeviceDisplayName2 = static_cast<QString>(DeviceDisplayName);
bool DeviceStatus = vectorDeviceInfo[i].GetAccessStatus();
QString devInfo;
devInfo += QString("%1:%2;").arg(XML_TLAYER_TYPE) //传输协议
.arg(XML_TLAYER_U3V);
devInfo += QString("%1:%2;").arg(XML_DRIVER_TYPE) //驱动类型
.arg(m_cCameraType);
devInfo += QString("%1:%2;").arg(XML_DEVICE_MODEL_NAME)//设备型号
.arg(ModelName2);
devInfo += QString("%1:%2;").arg(XML_DEVICE_USER_DEFINE)//自定义名称
.arg(UserID2);
devInfo += QString("%1:%2;").arg(XML_DEVICE_ID) //序列号
.arg(DeviceSN2);
devInfo += QString("%1:%2;").arg(XML_DEVICE_VENDOR) //生产厂家
.arg(CameraVendor2);
devInfo += QString("%1:%2;").arg(XML_DEVICE_VERSION) //设备版本
.arg(DeviceDisplayName2);
devInfo += QString("%1:%2;").arg(XML_DEVICE_STATUS) //设备状态
.arg(DeviceStatus);
qDebug()<<"=>GetDeviceDaHengU3V:"<<devInfo;
lInfo << devInfo;
}
}
}
#endif
return ret;
}
- 首先也是要进行初始化,并获得设备信息对象。
IGXFactory::GetInstance().Init();
GxIAPICPP::gxdeviceinfo_vector vectorDeviceInfo;
IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);
- 然后就是获取设备信息的那部分了。注意一些强制类型转换。这部分难度不大,应该可以完成。
实时采图
下面这个函数是主动采图的开启与停止函数,所谓主动采图,就是通过按钮,用户主动去控制采图的过程,控制定时器定时器的开关,从而得到在一定的时间内可以连续采图,得到较好的连续的图像。
首先上开启采图的完整代码:
qint32 MDeviceDahengG3UC::AcquisitionStart()
{
qint32 ret = RETURN_FAIL;
#ifdef WIN32_DAHENG_GEV
if(m_bLoaded)
{
if(m_bStopWork)
{
m_objFeatureControlPtr = m_objDevicePtr->GetRemoteFeatureControl();
m_objImageProcessPtr = m_objDevicePtr->CreateImageProcessConfig();
bool m_bIsColorFilter = m_objFeatureControlPtr->IsImplemented("PixelColorFilter");
//开启流通道采集
if(!m_objStreamPtr.IsNull())
{
qDebug()<<"MDeviceDahengG3UC::AcquisitionStart m_objStreamPtr";
//objStreamPtr->SetAcqusitionBufferNumber(10);//必须在调用StartGrab开启流通道的采集之前,设置采集buffer个数,否则设置无效。
m_objStreamPtr->StartGrab();
}
//给设备发送开采命令
if(!m_objFeatureControlPtr.IsNull())
{
m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();
ret = RETURN_OK;
}
if(m_pConfigureObj->UpdateConfigureFromDevice())
m_pConfigureObj->SlotConfigChanged();
if(m_pConfigureObj->UpdateSimplyConfigureFromDevice())
m_pConfigureObj->SlotSimplyConfigChanged();
m_bStopWork =false;
}
}
#endif
return ret;
}
qint32 MDeviceDahengG3UC::AcquisitionStop()
{
qint32 ret = RETURN_FAIL;
#ifdef WIN32_DAHENG_GEV
if(m_bLoaded)
{
if(!m_bStopWork)
{
//停采
if((!m_objFeatureControlPtr.IsNull())&&(!m_objStreamPtr.IsNull()))
{
m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();
m_objStreamPtr->StopGrab();
//关闭流通道
m_objStreamPtr->Close();
}
if(m_pConfigureObj->UpdateConfigureFromDevice())
m_pConfigureObj->SlotConfigChanged();
if(m_pConfigureObj->UpdateSimplyConfigureFromDevice())
m_pConfigureObj->SlotSimplyConfigChanged();
ret = RETURN_OK;
m_bStopWork = true;
}
}
#endif
return ret;
}
- 首先,开启流通道采集
if(!m_objStreamPtr.IsNull())
{
qDebug()<<"MDeviceDahengG3UC::AcquisitionStart m_objStreamPtr";
//objStreamPtr->SetAcqusitionBufferNumber(10);//必须在调用StartGrab开启流通道的采集之前,设置采集buffer个数,否则设置无效。
m_objStreamPtr->StartGrab();
}
- 给设备发送开始开采命令
m_objFeatureControlPtr = m_objDevicePtr->GetRemoteFeatureControl();
if(!m_objFeatureControlPtr.IsNull())
{
m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();
ret = RETURN_OK;
}
- 在这里调用相机参数配置的函数,
if(m_pConfigureObj->UpdateConfigureFromDevice())
m_pConfigureObj->SlotConfigChanged();
if(m_pConfigureObj->UpdateSimplyConfigureFromDevice())
m_pConfigureObj->SlotSimplyConfigChanged();
- 给设备发送关闭开采的命令
m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();
m_objStreamPtr->StopGrab();
//关闭流通道
m_objStreamPtr->Close();
将采集到的Buffer传上显示层
先上完整的代码:
CaptureImageEx
qint32 MDeviceDahengG3UC::CaptureImageEx(QImage *img, MFrameInfo *info)
{
qint32 ret = RETURN_FAIL;
#ifdef WIN32_DAHENG_GEV
if(m_bLoaded)
{
QImage::Format format = QImage::Format_Indexed8;
if(m_cPixelFormat.compare("RGB888")==0)
format = QImage::Format_RGB888;
if((img->width() !=m_iWidth) ||
(img->height() !=m_iHeight) ||
(img->format() !=format) )
{
qDebug()<<"--> HiDahengG3V:CaptureImageEx reset image memory! ";
*img = QImage(m_iWidth,m_iHeight,format);
if(format == QImage::Format_Indexed8)
img->setColorTable(m_vColorTabel);
}
CImageDataPointer objImageDataPtr;
if(!m_objStreamPtr.IsNull())
{
objImageDataPtr = m_objStreamPtr->GetImage(500);//500ms趨
}
if (objImageDataPtr->GetStatus() == GX_FRAME_STATUS_SUCCESS)
{
//
void *pbit = objImageDataPtr->GetBuffer();
int size = qMin((int)objImageDataPtr->GetPayloadSize(),img->byteCount());//
memcpy(img->bits(),pbit,size);
info->nWidth = (qint32)objImageDataPtr->GetWidth();
info->nHeight = (qint32)objImageDataPtr->GetHeight();
info->nFramerLen = (qint32)objImageDataPtr->GetPayloadSize();
//QMetaEnum metaEnum = QMetaEnum::fromType<GXIAPICPP_API::GX_PIXEL_FORMAT_ENTRY>();
//info->cFormat = metaEnum.valueToKey(objImageDataPtr->GetPixelFormat();
quint64 nformat = objImageDataPtr->GetPixelFormat();
info->cFormat = GetPixelFormat(nformat);
info->fFrameRate = m_fFrameRate;
ret = RETURN_OK;
}
}
#endif
return ret;
}
- 下面我开始解析,首先,你可以注意到这个操作;
*img = QImage(m_iWidth,m_iHeight,format);
这个操作的目的是啥呢?目的是让这个img指向的QImage的宽高处于合理的状态,不然,即使你把buffer进行填充,你有可能发现你的框里面出现了三个重复的这个图像,但这肯定不是你想要的,所以,做一个判断,做一个赋值,这样是正确的操作。
- 我们可以看到,我上面是不是又做了一个采图的操作。也就是:
objImageDataPtr = m_objStreamPtr->GetImage(500);//500ms趨
这个操作,其实我们在Acqusisition
上面就已经实现过,其实已经没有实现的必要了,但加上也可以。我这里这样使用的原因是,我并没有把这个指针objImageDataPtr
声明为全局变量。所以,我这里就得再采一次了,但确实在之前的Acquisition
那里采集更好。
- 然后就是把信息赋值给Info了,这个操作应该不难,并且,把得到的buffer的地址传递给img的这个地址,让其从底层人民传递到皇帝那里,从而把信息传递出去。具体的操作是这个:
void *pbit = objImageDataPtr->GetBuffer();
int size = qMin((int)objImageDataPtr->GetPayloadSize(),img->byteCount());//
memcpy(img->bits(),pbit,size);
info->nWidth = (qint32)objImageDataPtr->GetWidth();
info->nHeight = (qint32)objImageDataPtr->GetHeight();
info->nFramerLen = (qint32)objImageDataPtr->GetPayloadSize();
//QMetaEnum metaEnum = QMetaEnum::fromType<GXIAPICPP_API::GX_PIXEL_FORMAT_ENTRY>();
//info->cFormat = metaEnum.valueToKey(objImageDataPtr->GetPixelFormat();
quint64 nformat = objImageDataPtr->GetPixelFormat();
info->cFormat = GetPixelFormat(nformat);
info->fFrameRate = m_fFrameRate;
总结
这个实时采图说难也不难,根所以,加油,重点应该是在最后的那个CaptureImageEx
,你需要对你的程序有一个较好的设计,让这个函数接口不断的从最上层往下传,从而传递到插件这。
这个部分还有另外两篇,这里给出地址,有需要的可以去看看:
若有错误,欢迎指出,感谢~