Qt环境下调用OpenCV和海康摄像头数据实现目标检测

项目目的

目前实验室在做一个项目,需要将深度学习模块嵌入到上位机中,实现检测螺栓目的,并在上位机界面中实时显示。

Step1 运行环境和准备

系统: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+海康威视摄像头的实时读取
废话不多说勒直接看代码

Step2 代码整体结构

Ui界面设置
Qt环境下调用OpenCV和海康摄像头数据实现目标检测_第1张图片
Pro文件

#-------------------------------------------------
#
# 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和海康摄像头数据实现目标检测_第2张图片

后期展望

接下来会着重写一个如何在Qt中使用Opencv进行深度学习推理,把一些最新加速工具用进来

你可能感兴趣的:(Qt)