需求
在视频窗口上进行绘图,包括圆,矩形,扇形等
效果:

思路:
自己取图然后转成QImage ,再向QWidget 进行渲染,根据以往的经验,无法达到很高的帧率。因此决定使用相机SDK自带的渲染功能,也就是传一个句柄给到sdk。但是这样视频渲染出来了,向上绘制图案,会被视频遮挡住,因此这里采用了两个窗口叠加,然后上层窗口设置透明背景的方式来实现。
底层取图窗口代码:
- #ifndef CAMERAWIDGET_H
- #define CAMERAWIDGET_H
-
- #include <QWidget>
- #include <windows.h>
- #include <MvCameraControl.h>
-
- #include "./util/PSEventController.h"
- #include "graphwidget.h"
- class CameraWidget : public QWidget
- {
- Q_OBJECT
- public:
- explicit CameraWidget(QWidget *parent = nullptr);
- ~CameraWidget();
-
- void updatePos(int x,int y);
-
- private:
- bool findDevice();
- bool printDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo);
- void getImage();
- bool openDevice();
- bool closeDevice();
-
- private:
- int nRet = MV_OK;
- void* handle = NULL;
- MV_CC_DEVICE_INFO_LIST stDeviceList = {0};
- std::atomic_bool bExit = true;
- HWND hwnd = NULL;
-
- GraphWidget * graphWidget=nullptr;
- QWidget *videoWidget =nullptr;
-
- std::atomic_bool isCapture{false};
-
- private:
- void initData();
- void mvToQImage(MV_DISPLAY_FRAME_INFO &stDisplayInfo);
-
- public slots:
- void on_psEvent_capture(bool isChecked);
- void on_psEvent_adjustImage(bool isChecked);
- void on_psEvent_fixImage(bool isChecked);
-
- signals:
- void capture(QImage image);
- };
-
- #endif // CAMERAWIDGET_H
-
-
- #include "camerawidget.h"
-
- #include <iostream>
- #include <mutex>
- #include <thread>
- #include <QWidget>
- #include <QPainter>
- #include <QGridLayout>
- #include <QStackedLayout>
- #include <QDebug>
- #include <QGraphicsScene>
- #include <QGraphicsView>
- #include <QGraphicsItem>
-
- CameraWidget::CameraWidget(QWidget *parent)
- : QWidget{parent}
- {
- graphWidget = new GraphWidget(this);
- graphWidget->setWindowFlags(Qt::FramelessWindowHint|Qt::Dialog|Qt::WindowStaysOnTopHint|Qt::SubWindow);
- graphWidget->setAttribute(Qt::WA_TranslucentBackground, true);
- QPalette pal;
- pal.setColor(QPalette::Window,QColor(0,0,0,0));
- graphWidget->setAutoFillBackground(true);
- graphWidget->setPalette(pal);
- graphWidget->show();
-
- hwnd = (HWND)this->winId();
- openDevice();
-
- this->setFixedSize(816,683);
- graphWidget->setFixedSize(this->size());
-
- setUpdatesEnabled(false);
- setAttribute(Qt::WA_OpaquePaintEvent);
-
- initData();
- }
-
- CameraWidget::~CameraWidget()
- {
- closeDevice();
- }
-
-
- bool CameraWidget::findDevice()
- {
- //枚举设备
- nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
- if (MV_OK != nRet)
- {
- printf("Enum Devices fail! nRet [0x%x]\n", nRet);
- return false;
- }
-
- if (stDeviceList.nDeviceNum > 0)
- {
- for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++)
- {
- printf("[device %d]:\n", i);
- MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];
- if (NULL == pDeviceInfo)
- {
- break;
- }
- printDeviceInfo(pDeviceInfo);
- }
- }
- else
- {
- printf("Find No Devices!\n");
- return false;
- }
- return true;
- }
-
- bool CameraWidget::openDevice()
- {
- if(!findDevice()){
- return false;
- }
-
- unsigned int nIndex = 0;
- if (nIndex >= stDeviceList.nDeviceNum)
- {
- printf("Invalid device index!\n");
- return false;
- }
-
- do
- {
- //选择设备并创建句柄
- nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);
- if (MV_OK != nRet)
- {
- printf("Create Handle fail! nRet [0x%x]\n", nRet);
- break;
- }
-
- // ch:打开设备
- nRet = MV_CC_OpenDevice(handle);
- if (MV_OK != nRet)
- {
- printf("Open Device fail! nRet [0x%x]\n", nRet);
- break;
- }
-
- // ch:探测网络最佳包大小(只对GigE相机有效)
- if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE)
- {
- int nPacketSize = MV_CC_GetOptimalPacketSize(handle);
- if (nPacketSize > 0)
- {
- nRet = MV_CC_SetIntValue(handle,"GevSCPSPacketSize",nPacketSize);
- if(nRet != MV_OK)
- {
- printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet);
- }
- }
- else
- {
- printf("Warning: Get Packet Size fail nRet [0x%x]!", nPacketSize);
- }
- }
-
- //设置触发模式为off
- nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 0);
- if (MV_OK != nRet)
- {
- printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet);
- break;
- }
-
- // ch:开始取流 | en:Start grab image
- nRet = MV_CC_StartGrabbing(handle);
- if (MV_OK != nRet)
- {
- printf("Start Grabbing fail! nRet [0x%x]\n", nRet);
- break;
- }
- bExit=false;
-
- std::thread t([&](){
- getImage();
- });
- t.detach();
-
- return true;
- }while (0);
-
-
- if (nRet != MV_OK)
- {
- if (handle != NULL)
- {
- MV_CC_DestroyHandle(handle);
- handle = NULL;
- }
- }
-
- return false;
- }
-
- bool CameraWidget::closeDevice()
- {
- bExit=true;
-
- if (handle == NULL)
- return true;
-
- do{
- // ch:停止取流 | en:Stop grab image
- nRet = MV_CC_StopGrabbing(handle);
- if (MV_OK != nRet)
- {
- printf("Stop Grabbing fail! nRet [0x%x]\n", nRet);
- break;
- }
-
- // ch:关闭设备 | Close device
- nRet = MV_CC_CloseDevice(handle);
- if (MV_OK != nRet)
- {
- printf("ClosDevice fail! nRet [0x%x]\n", nRet);
- break;
- }
-
- // ch:销毁句柄 | Destroy handle
- nRet = MV_CC_DestroyHandle(handle);
- if (MV_OK != nRet)
- {
- printf("Destroy Handle fail! nRet [0x%x]\n", nRet);
- break;
- }
-
- handle = NULL;
- return true;
- }while (0);
-
- if (nRet != MV_OK)
- {
- if (handle != NULL)
- {
- MV_CC_DestroyHandle(handle);
- handle = NULL;
- }
- }
-
- return false;
- }
-
- void CameraWidget::initData()
- {
- PSEventController::subscribe(this,"capture");
- PSEventController::subscribe(this,"adjustImage");
- PSEventController::subscribe(this,"fixImage");
-
- connect(this,&CameraWidget::capture,this,[&](QImage image){
- graphWidget->setBackgroundImage(QPixmap::fromImage(image),true);
- },Qt::UniqueConnection);
- }
-
- void CameraWidget::on_psEvent_capture(bool isChecked)
- {
- if(!isChecked){
- QPixmap backgroundImage;
- graphWidget->setBackgroundImage(backgroundImage,false);
- }else{
- isCapture=true;
- }
- }
-
- void CameraWidget::on_psEvent_adjustImage(bool isChecked)
- {
- graphWidget->setIsScale(true);
- }
-
- void CameraWidget::on_psEvent_fixImage(bool isChecked)
- {
- graphWidget->setIsScale(false);
- }
-
- void CameraWidget::updatePos(int x, int y)
- {
- graphWidget->move(x,y);
- }
-
- bool CameraWidget::printDeviceInfo(MV_CC_DEVICE_INFO *pstMVDevInfo)
- {
- if (NULL == pstMVDevInfo)
- {
- printf("The Pointer of pstMVDevInfo is NULL!\n");
- return false;
- }
- if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
- {
- int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
- int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
- int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
- int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);
-
- // ch:打印当前相机ip和用户自定义名字 | en:print current ip and user defined name
- printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4);
- printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
- }
- else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
- {
- printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
- printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber);
- printf("Device Number: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber);
- }
- else
- {
- printf("Not support.\n");
- }
-
- return true;
- }
-
- //std::once_flag flag;
- void CameraWidget::getImage()
- {
- int nRet = MV_OK;
- MV_FRAME_OUT stImageInfo = {0};
- MV_DISPLAY_FRAME_INFO stDisplayInfo = {0};
-
- while(1)
- {
- nRet = MV_CC_GetImageBuffer(handle, &stImageInfo, 1000);
- if (nRet == MV_OK)
- {
- // printf("Get Image Buffer: Width[%d], Height[%d], FrameNum[%d]\n",
- // stImageInfo.stFrameInfo.nWidth, stImageInfo.stFrameInfo.nHeight, stImageInfo.stFrameInfo.nFrameNum);
-
- if (hwnd)
- {
- stDisplayInfo.hWnd = hwnd;
- stDisplayInfo.pData = stImageInfo.pBufAddr;
- stDisplayInfo.nDataLen = stImageInfo.stFrameInfo.nFrameLen;
- stDisplayInfo.nWidth = stImageInfo.stFrameInfo.nWidth;
- stDisplayInfo.nHeight = stImageInfo.stFrameInfo.nHeight;
- stDisplayInfo.enPixelType = stImageInfo.stFrameInfo.enPixelType;
-
- //转qt QImage 给到绘图窗口
- if(isCapture){
- mvToQImage(stDisplayInfo);
- }
-
-
- //调整窗口尺寸
- // std::call_once(flag, [&](){
- // int cW = stImageInfo.stFrameInfo.nWidth;
- // int cH = stImageInfo.stFrameInfo.nHeight;
- // });
-
- MV_CC_DisplayOneFrame(handle, &stDisplayInfo);
- }
-
- nRet = MV_CC_FreeImageBuffer(handle, &stImageInfo);
- if(nRet != MV_OK)
- {
- printf("Free Image Buffer fail! nRet [0x%x]\n", nRet);
- }
- }
- else
- {
- printf("Get Image fail! nRet [0x%x]\n", nRet);
- }
- if(bExit)
- {
- break;
- }
- }
- }
-
- void CameraWidget::mvToQImage(MV_DISPLAY_FRAME_INFO &stDisplayInfo)
- {
- QImage image;
- if(stDisplayInfo.enPixelType==PixelType_Gvsp_Mono8){
- image= QImage(stDisplayInfo.pData, stDisplayInfo.nWidth, stDisplayInfo.nHeight, QImage::Format_Indexed8);
- }else if(stDisplayInfo.enPixelType==PixelType_Gvsp_RGB8_Packed){
- image= QImage(stDisplayInfo.pData, stDisplayInfo.nWidth, stDisplayInfo.nHeight, QImage::Format_RGB888);
- }
- emit capture(image);
- isCapture=false;
- }
-
上层绘图窗口代码:
- #ifndef CAMERAWIDGET_H
- #define CAMERAWIDGET_H
-
- #include <QWidget>
- #include <windows.h>
- #include <MvCameraControl.h>
-
- #include "./util/PSEventController.h"
- #include "graphwidget.h"
- class CameraWidget : public QWidget
- {
- Q_OBJECT
- public:
- explicit CameraWidget(QWidget *parent = nullptr);
- ~CameraWidget();
-
- void updatePos(int x,int y);
-
- private:
- bool findDevice();
- bool printDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo);
- void getImage();
- bool openDevice();
- bool closeDevice();
-
- private:
- int nRet = MV_OK;
- void* handle = NULL;
- MV_CC_DEVICE_INFO_LIST stDeviceList = {0};
- std::atomic_bool bExit = true;
- HWND hwnd = NULL;
-
- GraphWidget * graphWidget=nullptr;
- QWidget *videoWidget =nullptr;
-
- std::atomic_bool isCapture{false};
-
- private:
- void initData();
- void mvToQImage(MV_DISPLAY_FRAME_INFO &stDisplayInfo);
-
- public slots:
- void on_psEvent_capture(bool isChecked);
- void on_psEvent_adjustImage(bool isChecked);
- void on_psEvent_fixImage(bool isChecked);
-
- signals:
- void capture(QImage image);
- };
-
- #endif // CAMERAWIDGET_H
-
- #include "graphwidget.h"
- #include <QGridLayout>
- #include <QPainter>
- #include <QMouseEvent>
- #include <QDebug>
-
- #include "../graphics/bqgraphicsitem.h"
-
- #define VIEW_CENTER viewport()->rect().center()
- #define VIEW_WIDTH viewport()->rect().width()
- #define VIEW_HEIGHT viewport()->rect().height()
-
- GraphWidget::GraphWidget(QWidget *parent)
- : QGraphicsView{parent}
- {
- setAttribute(Qt::WA_TranslucentBackground);
- setStyleSheet("background: transparent;border:0px");
- setWindowFlags(Qt::FramelessWindowHint);
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setRenderHint(QPainter::Antialiasing);
- scene = new QGraphicsScene(this);
- this->setScene(scene);
- setDragMode(ScrollHandDrag);
- this->setSceneRect(this->geometry());
- centerOn(0, 0);
-
- initData();
- }
-
- void GraphWidget::setIsScale(bool newIsScale)
- {
- isScale = newIsScale;
- if(!isScale){
- revertSize();
- }
- }
-
- void GraphWidget::setBackgroundImage(const QPixmap &newBackgroundImage,bool newIsSetBackground)
- {
- qDebug()<<"setBackgroundImage "<<newIsSetBackground;
- isSetBackground=newIsSetBackground;
- if(backgroundItem){
- scene->removeItem(backgroundItem);
- backgroundItem=nullptr;
- }
-
- if(isSetBackground){
- setAttribute(Qt::WA_TranslucentBackground,false);
- backgroundImage = newBackgroundImage;
- if(!backgroundImage.isNull()){
- backgroundItem = scene->addPixmap(backgroundImage);
- backgroundItem->setPos(-this->width()/2,-this->height()/2);
- backgroundItem->setZValue(-1000);
-
- backgroundItem->setFlags(QGraphicsItem::ItemIsSelectable |
- QGraphicsItem::ItemIsMovable |
- QGraphicsItem::ItemIsFocusable);
- update();
- }
- }else{
- setAttribute(Qt::WA_TranslucentBackground,true);
- revertSize();
- }
-
- }
-
- void GraphWidget::clearItem()
- {
- for(int i=0;i<scene->items().size();i++){
- QGraphicsItem *item = scene->items().at(i);
- if(item->zValue()==-1000){
- continue;
- }else{
- scene->removeItem(scene->items().at(i));
- }
- }
- }
-
- void GraphWidget::on_psEvent_addLine(bool isChecked)
- {
- clearItem();
- BRectangle *m_rectangle = new BRectangle(0, 0, 100, 50, BGraphicsItem::ItemType::Rectangle);
- scene->addItem(m_rectangle);
- }
-
- void GraphWidget::on_psEvent_addCircle(bool isChecked)
- {
- clearItem();
- BConcentricCircle *m_conCircle = new BConcentricCircle(0, 0, 50, 80, BGraphicsItem::ItemType::Concentric_Circle);
- scene->addItem(m_conCircle);
- }
-
- void GraphWidget::on_psEvent_addEllipse(bool isChecked)
- {
- clearItem();
- BEllipse *m_ellipse = new BEllipse(0, 0, 100, 50, BGraphicsItem::ItemType::Ellipse);
- scene->addItem(m_ellipse);
- }
-
- void GraphWidget::on_psEvent_addArc(bool isChecked)
- {
- clearItem();
- BPie *m_pie = new BPie(0, 0, 80, 30, BGraphicsItem::ItemType::Pie);
- scene->addItem(m_pie);
- }
-
- void GraphWidget::wheelEvent(QWheelEvent *event)
- {
- if(isScale){
- static float scale = 1.1;
- auto angle = event->angleDelta();
-
- if(angle.y() > 0)
- {
- this->scale(scale, scale);
- }
- else
- {
- this->scale(1/scale, 1/scale);
- }
- }
- }
-
- void GraphWidget::initData()
- {
- PSEventController::subscribe(this,"addLine");
- PSEventController::subscribe(this,"addCircle");
- PSEventController::subscribe(this,"addEllipse");
- PSEventController::subscribe(this,"addArc");
- }
-
- void GraphWidget::revertSize()
- {
- setTransformationAnchor(QGraphicsView::AnchorViewCenter);
- QMatrix q;
- q.setMatrix(1,this->matrix().m12(),this->matrix().m21(),1,this->matrix().dx(),this->matrix().dy());
- setMatrix(q,false);
- }
主窗口移动和缩放时更新视频窗口位置:
- void MainWindow::updateGraphWidgetPos()
- {
- if(cameraWidget){
- QPoint p = mapToGlobal(cameraWidget->parentWidget()->pos());
- cameraWidget->updatePos(p.x(),p.y()+22);
- }
- }
-
- void MainWindow::resizeEvent(QResizeEvent *e)
- {
- updateGraphWidgetPos();
- return QWidget::resizeEvent(e);
- }
-
- void MainWindow::moveEvent(QMoveEvent *e)
- {
- updateGraphWidgetPos();
- return QWidget::moveEvent(e);
- }