#include "Hd_CameraModule_HIK_General.h" using namespace std; #include #include #include #include #include enum statusEnum { CONNECTED, RECONNECTING, DISCONNECTED }; enum triggerModeEnum { SOFTWARE, HARDWARE, EMPTY }; statusEnum cameraStatus = DISCONNECTED; triggerModeEnum triggerMode = HARDWARE; int getImageTimeOut = 3000; bool runFlag = true; //总取图数 int imgIndex = 0; MV_CC_DEVICE_INFO_LIST m_stDevList; // ch:设备信息列表结构体变量,用来存储设备列表 string Username = ""; int cameraIndex = -1; QString lastError; QQueue queuepic; QMutex* n_mutex = new QMutex(); void* handle = NULL; void CloseDevice(); //0106 断线重连 void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser); void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFrameInfo0, void* pUser0); //搜索所有设备 int SearchDevice(){ if (m_stDevList.nDeviceNum == 0) { memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); // ch:枚举子网内所有设备 | en:Enumerate all devices within subnet int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_stDevList); if (MV_OK != nRet) { return -1; } } for (int i = 0; i < m_stDevList.nDeviceNum; i++) { //SN unsigned char* name = m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.chSerialNumber; string userName = static_cast((LPCSTR)name); if (Username == userName) { return i; break; } name = m_stDevList.pDeviceInfo[i]->SpecialInfo.stUsb3VInfo.chSerialNumber; userName = static_cast((LPCSTR)name); if (Username == userName) { return i; break; } } return -1; } bool connectDevice() { int cameraIndex = SearchDevice(); if (cameraIndex == -1){ //未匹配到相机 qDebug() << "[Waring] " << "Hd_CameraModule_HIK not match --CameraName:" << QString::fromStdString(Username); lastError = "找不到相机" + QString::fromStdString(Username); return false; } if (handle != NULL) { MV_CC_StopGrabbing(handle); MV_CC_CloseDevice(handle); MV_CC_DestroyHandle(handle); } //打开相机 MV_CC_CreateHandle(&handle, m_stDevList.pDeviceInfo[cameraIndex]); int nRet = MV_CC_OpenDevice(handle); ////触发模式 if(triggerMode == SOFTWARE) { MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_ON); MV_CC_SetEnumValue(handle, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE); } else if(triggerMode == HARDWARE) { MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_ON); MV_CC_SetEnumValue(handle, "TriggerSource", MV_TRIGGER_SOURCE_LINE0); } // //加载用户集,相机设置应当UserSet1 nRet = MV_CC_SetEnumValueByString(handle, "UserSetSelector", "UserSet1"); if (MV_OK != nRet)qDebug() << "Set UserSetSelector fail! nRet " << nRet; // ch:注册抓图回调 | en:Register image callback MV_CC_SetIntValueEx(handle, "GevHeartbeatTimeout", 3000);//心跳日志 nRet = MV_CC_RegisterImageCallBackEx(handle, ImageCallBackEx, handle);//注册回调 if (MV_OK != nRet)return false; nRet = MV_CC_RegisterExceptionCallBack(handle, ReconnectDevice, NULL);//断线重连 if (MV_OK != nRet)return false; MV_CC_StartGrabbing(handle); cameraStatus = CONNECTED; return true; } void SetExposureTime(float exposureValue) { qDebug() << "[INFO] " << "exposureValue:" << exposureValue; MV_CC_SetEnumValue(handle, "ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF); MV_CC_SetFloatValue(handle, "ExposureTime", exposureValue); } void SetGain(float GainValue) { qDebug() << "[INFO] " << "GainValue:" << GainValue; MV_CC_SetEnumValue(handle, "GainAuto", 0); MV_CC_SetFloatValue(handle, "Gain", GainValue); } //断线重连 void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser){ qDebug() << "[Error] " << " MV camera disconnects!"; if (nMsgType == MV_EXCEPTION_DEV_DISCONNECT && cameraStatus == CONNECTED){ cameraStatus = RECONNECTING; // 启动后台线程负责重连,避免在回调线程中长时间阻塞 std::thread([]() { qDebug() << "[Info] start reconnect thread"; // 确保设备资源关闭 CloseDevice(); int times = 0; while (runFlag) { qDebug() << "[Info] reconnect times:" << times++; if (connectDevice()) { qWarning() << "[Hd_CameraModule_HIK] reconnect success!"; cameraStatus = CONNECTED; return; } } }).detach(); } } //回调函数; //经测试,相机像素格式Mono8、RGB8是可以正常转换的,YUV422还需要优化;RGB8格式比YUV422帧率低;BGR格式会导致异色问题 void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFrameInfo0, void* pUser0) { Q_UNUSED(pUser0) int frameNum = 0; double time_Start = (double)clock(); QMutexLocker locker(n_mutex); cv::Mat srcImage = cv::Mat(); //qDebug() << "[info]" << __FUNCTION__ << " line:" << __LINE__ << "--- start"; if (pFrameInfo0) { //获取的是单通道灰度图 if (pFrameInfo0->enPixelType == PixelType_Gvsp_Mono8)//获取当前采集到的图像格式 { srcImage = cv::Mat(pFrameInfo0->nHeight, pFrameInfo0->nWidth, CV_8UC1, pData0); } //获取的是RGB8图 else if (pFrameInfo0->enPixelType == PixelType_Gvsp_BGR8_Packed) { srcImage = cv::Mat(pFrameInfo0->nHeight, pFrameInfo0->nWidth, CV_8UC3, pData0); //cvtColor(srcImage, srcImage, COLOR_RGB2BGR);//导致异色 } else if (IsColor(pFrameInfo0->enPixelType)) //其它格式彩色图 { MvGvspPixelType enDstPixelType = PixelType_Gvsp_Undefined; enDstPixelType = PixelType_Gvsp_BGR8_Packed; unsigned char* pConvertData = NULL; pConvertData = (unsigned char*)malloc(pFrameInfo0->nWidth * pFrameInfo0->nHeight * 3); if (NULL == pConvertData) { printf("malloc pConvertData fail!\n"); } else { unsigned int nConvertDataSize = 0; nConvertDataSize = pFrameInfo0->nWidth * pFrameInfo0->nHeight * 3; // ch:像素格式转换 | en:Convert pixel format MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 }; frameNum = pFrameInfo0->nFrameNum; //ch:帧号 | en:frame number stConvertParam.nWidth = pFrameInfo0->nWidth; //ch:图像宽 | en:image width stConvertParam.nHeight = pFrameInfo0->nHeight; //ch:图像高 | en:image height stConvertParam.pSrcData = pData0; //ch:输入数据缓存 | en:input data buffer stConvertParam.nSrcDataLen = pFrameInfo0->nFrameLen; //ch:输入数据大小 | en:input data size stConvertParam.enSrcPixelType = pFrameInfo0->enPixelType; //ch:输入像素格式 | en:input pixel format stConvertParam.enDstPixelType = enDstPixelType; //ch:输出像素格式 | en:output pixel format stConvertParam.pDstBuffer = pConvertData; //ch:输出数据缓存 | en:output data buffer stConvertParam.nDstBufferSize = nConvertDataSize; //ch:输出缓存大小 | en:output buffer size int nRet = MV_CC_ConvertPixelType(handle, &stConvertParam); Mat mysrcImage = cv::Mat(pFrameInfo0->nHeight, pFrameInfo0->nWidth, CV_8UC3, stConvertParam.pDstBuffer); mysrcImage.copyTo(srcImage); free(pConvertData); pConvertData = NULL; stConvertParam = { 0 }; } } } if (srcImage.empty()) { srcImage = cv::Mat(5, 5, CV_8UC1).setTo(0); } queuepic.push_back(srcImage); pData0 = { 0 }; double time_End = (double)clock(); qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getImg,Time:" << (time_End - time_Start) << "ms" << "--CameraName:" << QString::fromStdString(Username) << " imgIndex:" << imgIndex << "nFrameNum : " << frameNum; } //关闭设备 void CloseDevice() { // 如果 handle 已经为空,直接返回 if (handle == nullptr) return; // 使用 SDK 正确的停止/关闭/销毁流程,避免使用 CloseHandle 等不匹配的 API MV_CC_StopGrabbing(handle); MV_CC_CloseDevice(handle); MV_CC_DestroyHandle(handle); handle = nullptr; } Hd_CameraModule_HIK_General::~Hd_CameraModule_HIK_General() { runFlag = false; CloseDevice(); if (n_mutex) { delete n_mutex; n_mutex = nullptr; } qDebug() << "[INFO] " << " delete success!"; } //从类中读数据到实例对象 bool Hd_CameraModule_HIK_General::readData(std::vector& mat, QByteArray& data) { if(data == "getStatus"){ data = lastError.toUtf8(); return true; } if (data == "clearCache") { queuepic.clear(); return true; } if (queuepic.isEmpty()) { return false; } QMutexLocker locker(n_mutex); cv::Mat img = queuepic.dequeue(); mat.push_back(img); qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg readData--CameraName:" << QString::fromStdString(Username) << "--imgIndex : " << imgIndex; return true; } //实例对象把数据写入到类;加载模型后会运行此函数 bool Hd_CameraModule_HIK_General::writeData(std::vector& mat, QByteArray& data) { Q_UNUSED(mat); if (data.contains("deviceList")) { auto result = QByteArrayToMV_CC_DeviceInfoList(data.mid(10)); if (result.nDeviceNum != 0) { m_stDevList = result; return true; } } return true; } //传入initParas函数,格式为:相机key+参数名1#参数值1#参数值2+参数名2#参数值1... bool Hd_CameraModule_HIK_General::initParas(QByteArray& initData) { QByteArrayList valueList = initData.split('#'); if (valueList.size() < 3) { qDebug() << "[Error] " << " Hd_CameraModule_HIK initParas fail--params error!参数数量不满足"; lastError = "参数数量不满足"; return false; } Username = valueList[0]; auto triggerModeStr = valueList[1]; if (triggerModeStr == "HARDWARE") triggerMode = HARDWARE; else if (triggerModeStr == "SOFTWARE") triggerMode = SOFTWARE; else if (triggerModeStr == "EMPTY") triggerMode = EMPTY; else { qDebug() << "[Error] " << " Hd_CameraModule_HIK initParas fail--params error!触发模式参数错误"; lastError = "触发模式参数错误"; return false; } getImageTimeOut = valueList[2].toInt(); imgIndex = 0; return connectDevice(); } bool Hd_CameraModule_HIK_General::run() { qDebug() << "[INFO] " << " run start" << QString::fromStdString(Username); if (triggerMode == SOFTWARE) MV_CC_SetCommandValue(handle, "TriggerSoftware"); try { int sleepMs = 10; int cnt = 0; int timeoutCnt = getImageTimeOut / sleepMs; while (true) { QMutexLocker locker(n_mutex); if (queuepic.size() > 0)return true; if (cnt++ > timeoutCnt) { queuepic.push_back(cv::Mat(200, 200, CV_8UC1, cv::Scalar(0))); //返回空图 qDebug() << QString::fromStdString(Username) << "timeout" ; return true; } QThread::msleep(sleepMs); } } catch (QString ev) { qDebug() << "[Error] " << " ev:" << ev; } return true; } //相机连接状态:正常连接或断开 bool Hd_CameraModule_HIK_General::checkStatus() { //status = moduleStatus; return status; } Hd_CameraModule_HIK_General* create() { qDebug() << "[INFO] " << "Hd_CameraModule_HIK create success!"; return new Hd_CameraModule_HIK_General; } void destory(Hd_CameraModule_HIK_General* ptr) { if (ptr) { delete ptr; ptr = nullptr; } qDebug() << "[INFO] " << "Hd_CameraModule_HIK destory success!"; }