目前实验室在做一个项目,需要将深度学习模块嵌入到上位机中,实现检测螺栓目的,并在上位机界面中实时显示。
系统:Windows10
Qt版本:5.14
OpenCV版本:4.3(搭载Dnn模块)
海康网络摄像头
深度网络模型:YoloV3 Kers
可选装加速模块:
CUDA/OpenVino
加速模块挺重要的,对于实际项目来说,实时性是很重要的指标,因此对于这两个模块在Qt上的使用我会接下来继续写博客!
运行环境配置:
Qt和OpenCV环境配置可参考这些链接:QT+opencv的环境配置1
QT+opencv的环境配置2
海康SDK:海康摄像头SDK下载
海康sdk也是一个小坑,目前用的是32位版本的,用64位的时候会报错,欢迎大家一起交流一下!
模型的封装与训练可以看这个博主的很多文章,挺有参考价值的模型训练
海康视频流取流是一个坑,由于海康的视频流是YV12格式,Qt无法直接读取,需要转成RGB,这时候就需要OpenCV对采集到的视频进行处理。可以参考以下文章:海康威视摄像头+OpenCV+VS2017 图像处理小结
OpenCV+海康威视摄像头的实时读取
废话不多说勒直接看代码
#-------------------------------------------------
#
# Project created by QtCreator 2019-07-26T10:07:17
#
#-------------------------------------------------
QT += core gui sql
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Camera
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
HK_camera.cpp \
main.cpp
HEADERS += \
HK_camera.h \
robot.h \
comfunction.h
FORMS += \
HK_camera.ui
INCLUDEPATH += D:\SDK\CH-HCNetSDK_32\includes\
INCLUDEPATH += D:\SDK\CH-HCNetSDK_32\libs\
INCLUDEPATH += D:\SDK\CH-HCNetSDK_32\libs\HCNetSDKCom\
INCLUDEPATH += D:\SDK\CH-HCNetSDK_32\libs\ClientDemoDll\
INCLUDEPATH += D:\Software\opencv4.3\build\include
LIBS +=D:\SDK\CH-HCNetSDK_32\libs\HCNetSDK.lib\
D:\SDK\CH-HCNetSDK_32\libs\PlayCtrl.lib\
D:\SDK\CH-HCNetSDK_32\libs\HCCore.lib\
D:\SDK\CH-HCNetSDK_32\libs\GdiPlus.lib
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_calib3d430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_core430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_dnn430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_features2d430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_flann430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_highgui430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_imgcodecs430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_imgproc430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_ml430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_objdetect430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_photo430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_stitching430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_video430.dll
LIBS += D:\Software\opencv4.3\opencv-build\bin\libopencv_videoio430.dll
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
image.qrc
海康回传视频流代码
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
#else
void MyWidget::DecCBFun(long nPort, char *pBuf, long nSize, FRAME_INFO *pFrameInfo, long nReserved1, long nReserved2)
#endif
{
long lFrameType = pFrameInfo->nType;
if (lFrameType == T_YV12)
{
qDebug()<<"8888";
VideoCapture capture;
Mat dst(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);//这里nHeight为720,nWidth为1280,8UC3表示8bit uchar 无符号类型,3通道值,输出
Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (uchar*)pBuf);//输入
cvtColor(src,dst,CV_YUV2BGR_YV12);
MyWidget::Detection(dst);//目标检测
/******opencv直接显示****/
// imshow("bgr",dst);
// waitKey(1);
//此时是YV12格式的视频数据,保存在pBuf中,可以fwrite(pBuf,nSize,1,Videofile);
//fwrite(pBuf,nSize,1,fp);
}
/***************
else if (lFrameType ==T_AUDIO16)
{
//此时是音频数据,数据保存在pBuf中,可以fwrite(pBuf,nSize,1,Audiofile);
}
else
{
}
*******************/
}
void MyWidget::fRealDataCallBack(LONG lRealPlayHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
DWORD dRet = 0;
BOOL inData = FALSE;
switch (dwDataType)
{
case NET_DVR_SYSHEAD: //系统头
if (nPort >= 0)
{
break; //同一路码流不需要多次调用开流接口
}
if (!PlayM4_GetPort(&nPort)) //获取播放库未使用的通道号
{
break;
}
if (dwBufSize > 0)
{
if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME)) //设置实时流播放模式
{
cout << "PlayM4_SetStreamOpenMode failed " << endl;
break;
}
if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024*100000)) //查询
{
cout << "PlayM4_OpenStream failed " << endl;
dRet = PlayM4_GetLastError(nPort);
break;
}
//设置解码回调函数 只解码不显示
if (!PlayM4_SetDecCallBack(nPort, DecCBFun)) //查询
{
dRet = PlayM4_GetLastError(nPort);
break;
}
//设置解码回调函数 解码且显示
//if (!PlayM4_SetDecCallBackEx(nPort,DecCBFun,NULL,NULL))
//{
// dRet=PlayM4_GetLastError(nPort);
// break;
//}
//打开视频解码
if (!PlayM4_Play(nPort, hWnd))
{
dRet = PlayM4_GetLastError(nPort);
break;
}
//打开音频解码, 需要码流是复合流
/*if (!PlayM4_PlaySound(nPort))
{
dRet = PlayM4_GetLastError(nPort);
break;
}*/
}
break;
case NET_DVR_STREAMDATA: //码流数据
inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
while (!inData)
{
Sleep(10);
inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
cout << "PlayM4_InputData failed 11111" << endl;
qDebug()<<"error"<<PlayM4_GetLastError(nPort);
break;
}
break;
default:
inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
while (!inData)
{
Sleep(10);
inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
cout << "PlayM4_InputData failed 22222" << endl;
break;
}
break;
}
}
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
char tempbuf[256] = { 0 };
switch (dwType)
{
case EXCEPTION_RECONNECT: //预览时重连
cout << "----------reconnect--------" << endl;
break;
default:
break;
}
}
void MyWidget::camera_login()
{
//---------------------------------------
// 初始化
NET_DVR_Init();
//设置连接时间与重连时间
NET_DVR_SetConnectTime(2000, 1);
NET_DVR_SetReconnect(10000, true);
//设置异常消息回调函数
NET_DVR_SetExceptionCallBack_V30(0,NULL,g_ExceptionCallBack,NULL);
//注册设备
LONG lUserID = -1;
NET_DVR_USER_LOGIN_INFO struLoginInfo = {0};
struLoginInfo.bUseAsynLogin = 0; //同步登录方式
strcpy(struLoginInfo.sDeviceAddress, "192.168.0.118"); //设备IP地址
struLoginInfo.wPort = 8000; //设备服务端口
strcpy(struLoginInfo.sUserName, "admin"); //设备登录用户名
strcpy(struLoginInfo.sPassword, "admin12345"); //设备登录密码
//设备信息, 输出参数
NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = {0};
lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
qDebug()<<lUserID<<endl;
if(lUserID < 0)
{
qDebug()<<"login Err="<<NET_DVR_GetLastError();
return;
//NET_DVR_Cleanup();
}
NET_DVR_PREVIEWINFO struPlayInfo={0};
//启动预览
struPlayInfo.hPlayWnd = NULL;
struPlayInfo.lChannel = 1; //预览通道
struPlayInfo.dwStreamType = 0; //0主码流
struPlayInfo.dwLinkMode = 0; //TCP方式
struPlayInfo.bBlocked = 1; //0- 非阻塞取流,1- 阻塞取流
LONG lRealPlayHandle;
qDebug()<<"6666";
lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, fRealDataCallBack, NULL);
if ( lRealPlayHandle < 0)
{
qDebug()<<"error"<<NET_DVR_GetLastError();
qDebug()<<NET_DVR_GetLastError()<<NET_DVR_GetErrorMsg();
return;
}
}
//Mat转成QImage
QImage MyWidget::MatImageToQt(const Mat &src)
{
//CV_8UC1 8位无符号的单通道---灰度图片
if(src.type() == CV_8UC1)
{
//使用给定的大小和格式构造图像
//QImage(int width, int height, Format format)
QImage qImage(src.cols,src.rows,QImage::Format_Indexed8);
//扩展颜色表的颜色数目
qImage.setColorCount(256);
//在给定的索引设置颜色
for(int i = 0; i < 256; i ++)
{
//得到一个黑白图
qImage.setColor(i,qRgb(i,i,i));
}
//复制输入图像,data数据段的首地址
uchar *pSrc = src.data;
//
for(int row = 0; row < src.rows; row ++)
{
//遍历像素指针
uchar *pDest = qImage.scanLine(row);
//从源src所指的内存地址的起始位置开始拷贝n个
//字节到目标dest所指的内存地址的起始位置中
memcmp(pDest,pSrc,src.cols);
//图像层像素地址
pSrc += src.step;
}
return qImage;
}
//为3通道的彩色图片
else if(src.type() == CV_8UC3)
{
//得到图像的的首地址
const uchar *pSrc = (const uchar*)src.data;
//以src构造图片
QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);
//在不改变实际图像数据的条件下,交换红蓝通道
return qImage.rgbSwapped();
}
//四通道图片,带Alpha通道的RGB彩色图像
else if(src.type() == CV_8UC4)
{
const uchar *pSrc = (const uchar*)src.data;
QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32);
//返回图像的子区域作为一个新图像
return qImage.copy();
}
else
{
return QImage();
}
}
接下来会着重写一个如何在Qt中使用Opencv进行深度学习推理,把一些最新加速工具用进来