• Qt 基于海康相机的视频绘图


    需求

    在视频窗口上进行绘图,包括圆,矩形,扇形等

    效果

     思路:

    自己取图然后转成QImage ,再向QWidget 进行渲染,根据以往的经验,无法达到很高的帧率。因此决定使用相机SDK自带的渲染功能,也就是传一个句柄给到sdk。但是这样视频渲染出来了,向上绘制图案,会被视频遮挡住,因此这里采用了两个窗口叠加,然后上层窗口设置透明背景的方式来实现。

    底层取图窗口代码:

    1. #ifndef CAMERAWIDGET_H
    2. #define CAMERAWIDGET_H
    3. #include <QWidget>
    4. #include <windows.h>
    5. #include <MvCameraControl.h>
    6. #include "./util/PSEventController.h"
    7. #include "graphwidget.h"
    8. class CameraWidget : public QWidget
    9. {
    10. Q_OBJECT
    11. public:
    12. explicit CameraWidget(QWidget *parent = nullptr);
    13. ~CameraWidget();
    14. void updatePos(int x,int y);
    15. private:
    16. bool findDevice();
    17. bool printDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo);
    18. void getImage();
    19. bool openDevice();
    20. bool closeDevice();
    21. private:
    22. int nRet = MV_OK;
    23. void* handle = NULL;
    24. MV_CC_DEVICE_INFO_LIST stDeviceList = {0};
    25. std::atomic_bool bExit = true;
    26. HWND hwnd = NULL;
    27. GraphWidget * graphWidget=nullptr;
    28. QWidget *videoWidget =nullptr;
    29. std::atomic_bool isCapture{false};
    30. private:
    31. void initData();
    32. void mvToQImage(MV_DISPLAY_FRAME_INFO &stDisplayInfo);
    33. public slots:
    34. void on_psEvent_capture(bool isChecked);
    35. void on_psEvent_adjustImage(bool isChecked);
    36. void on_psEvent_fixImage(bool isChecked);
    37. signals:
    38. void capture(QImage image);
    39. };
    40. #endif // CAMERAWIDGET_H
    41. #include "camerawidget.h"
    42. #include <iostream>
    43. #include <mutex>
    44. #include <thread>
    45. #include <QWidget>
    46. #include <QPainter>
    47. #include <QGridLayout>
    48. #include <QStackedLayout>
    49. #include <QDebug>
    50. #include <QGraphicsScene>
    51. #include <QGraphicsView>
    52. #include <QGraphicsItem>
    53. CameraWidget::CameraWidget(QWidget *parent)
    54. : QWidget{parent}
    55. {
    56. graphWidget = new GraphWidget(this);
    57. graphWidget->setWindowFlags(Qt::FramelessWindowHint|Qt::Dialog|Qt::WindowStaysOnTopHint|Qt::SubWindow);
    58. graphWidget->setAttribute(Qt::WA_TranslucentBackground, true);
    59. QPalette pal;
    60. pal.setColor(QPalette::Window,QColor(0,0,0,0));
    61. graphWidget->setAutoFillBackground(true);
    62. graphWidget->setPalette(pal);
    63. graphWidget->show();
    64. hwnd = (HWND)this->winId();
    65. openDevice();
    66. this->setFixedSize(816,683);
    67. graphWidget->setFixedSize(this->size());
    68. setUpdatesEnabled(false);
    69. setAttribute(Qt::WA_OpaquePaintEvent);
    70. initData();
    71. }
    72. CameraWidget::~CameraWidget()
    73. {
    74. closeDevice();
    75. }
    76. bool CameraWidget::findDevice()
    77. {
    78. //枚举设备
    79. nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
    80. if (MV_OK != nRet)
    81. {
    82. printf("Enum Devices fail! nRet [0x%x]\n", nRet);
    83. return false;
    84. }
    85. if (stDeviceList.nDeviceNum > 0)
    86. {
    87. for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++)
    88. {
    89. printf("[device %d]:\n", i);
    90. MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];
    91. if (NULL == pDeviceInfo)
    92. {
    93. break;
    94. }
    95. printDeviceInfo(pDeviceInfo);
    96. }
    97. }
    98. else
    99. {
    100. printf("Find No Devices!\n");
    101. return false;
    102. }
    103. return true;
    104. }
    105. bool CameraWidget::openDevice()
    106. {
    107. if(!findDevice()){
    108. return false;
    109. }
    110. unsigned int nIndex = 0;
    111. if (nIndex >= stDeviceList.nDeviceNum)
    112. {
    113. printf("Invalid device index!\n");
    114. return false;
    115. }
    116. do
    117. {
    118. //选择设备并创建句柄
    119. nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);
    120. if (MV_OK != nRet)
    121. {
    122. printf("Create Handle fail! nRet [0x%x]\n", nRet);
    123. break;
    124. }
    125. // ch:打开设备
    126. nRet = MV_CC_OpenDevice(handle);
    127. if (MV_OK != nRet)
    128. {
    129. printf("Open Device fail! nRet [0x%x]\n", nRet);
    130. break;
    131. }
    132. // ch:探测网络最佳包大小(只对GigE相机有效)
    133. if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE)
    134. {
    135. int nPacketSize = MV_CC_GetOptimalPacketSize(handle);
    136. if (nPacketSize > 0)
    137. {
    138. nRet = MV_CC_SetIntValue(handle,"GevSCPSPacketSize",nPacketSize);
    139. if(nRet != MV_OK)
    140. {
    141. printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet);
    142. }
    143. }
    144. else
    145. {
    146. printf("Warning: Get Packet Size fail nRet [0x%x]!", nPacketSize);
    147. }
    148. }
    149. //设置触发模式为off
    150. nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 0);
    151. if (MV_OK != nRet)
    152. {
    153. printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet);
    154. break;
    155. }
    156. // ch:开始取流 | en:Start grab image
    157. nRet = MV_CC_StartGrabbing(handle);
    158. if (MV_OK != nRet)
    159. {
    160. printf("Start Grabbing fail! nRet [0x%x]\n", nRet);
    161. break;
    162. }
    163. bExit=false;
    164. std::thread t([&](){
    165. getImage();
    166. });
    167. t.detach();
    168. return true;
    169. }while (0);
    170. if (nRet != MV_OK)
    171. {
    172. if (handle != NULL)
    173. {
    174. MV_CC_DestroyHandle(handle);
    175. handle = NULL;
    176. }
    177. }
    178. return false;
    179. }
    180. bool CameraWidget::closeDevice()
    181. {
    182. bExit=true;
    183. if (handle == NULL)
    184. return true;
    185. do{
    186. // ch:停止取流 | en:Stop grab image
    187. nRet = MV_CC_StopGrabbing(handle);
    188. if (MV_OK != nRet)
    189. {
    190. printf("Stop Grabbing fail! nRet [0x%x]\n", nRet);
    191. break;
    192. }
    193. // ch:关闭设备 | Close device
    194. nRet = MV_CC_CloseDevice(handle);
    195. if (MV_OK != nRet)
    196. {
    197. printf("ClosDevice fail! nRet [0x%x]\n", nRet);
    198. break;
    199. }
    200. // ch:销毁句柄 | Destroy handle
    201. nRet = MV_CC_DestroyHandle(handle);
    202. if (MV_OK != nRet)
    203. {
    204. printf("Destroy Handle fail! nRet [0x%x]\n", nRet);
    205. break;
    206. }
    207. handle = NULL;
    208. return true;
    209. }while (0);
    210. if (nRet != MV_OK)
    211. {
    212. if (handle != NULL)
    213. {
    214. MV_CC_DestroyHandle(handle);
    215. handle = NULL;
    216. }
    217. }
    218. return false;
    219. }
    220. void CameraWidget::initData()
    221. {
    222. PSEventController::subscribe(this,"capture");
    223. PSEventController::subscribe(this,"adjustImage");
    224. PSEventController::subscribe(this,"fixImage");
    225. connect(this,&CameraWidget::capture,this,[&](QImage image){
    226. graphWidget->setBackgroundImage(QPixmap::fromImage(image),true);
    227. },Qt::UniqueConnection);
    228. }
    229. void CameraWidget::on_psEvent_capture(bool isChecked)
    230. {
    231. if(!isChecked){
    232. QPixmap backgroundImage;
    233. graphWidget->setBackgroundImage(backgroundImage,false);
    234. }else{
    235. isCapture=true;
    236. }
    237. }
    238. void CameraWidget::on_psEvent_adjustImage(bool isChecked)
    239. {
    240. graphWidget->setIsScale(true);
    241. }
    242. void CameraWidget::on_psEvent_fixImage(bool isChecked)
    243. {
    244. graphWidget->setIsScale(false);
    245. }
    246. void CameraWidget::updatePos(int x, int y)
    247. {
    248. graphWidget->move(x,y);
    249. }
    250. bool CameraWidget::printDeviceInfo(MV_CC_DEVICE_INFO *pstMVDevInfo)
    251. {
    252. if (NULL == pstMVDevInfo)
    253. {
    254. printf("The Pointer of pstMVDevInfo is NULL!\n");
    255. return false;
    256. }
    257. if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
    258. {
    259. int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
    260. int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
    261. int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
    262. int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);
    263. // ch:打印当前相机ip和用户自定义名字 | en:print current ip and user defined name
    264. printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4);
    265. printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
    266. }
    267. else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
    268. {
    269. printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
    270. printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber);
    271. printf("Device Number: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber);
    272. }
    273. else
    274. {
    275. printf("Not support.\n");
    276. }
    277. return true;
    278. }
    279. //std::once_flag flag;
    280. void CameraWidget::getImage()
    281. {
    282. int nRet = MV_OK;
    283. MV_FRAME_OUT stImageInfo = {0};
    284. MV_DISPLAY_FRAME_INFO stDisplayInfo = {0};
    285. while(1)
    286. {
    287. nRet = MV_CC_GetImageBuffer(handle, &stImageInfo, 1000);
    288. if (nRet == MV_OK)
    289. {
    290. // printf("Get Image Buffer: Width[%d], Height[%d], FrameNum[%d]\n",
    291. // stImageInfo.stFrameInfo.nWidth, stImageInfo.stFrameInfo.nHeight, stImageInfo.stFrameInfo.nFrameNum);
    292. if (hwnd)
    293. {
    294. stDisplayInfo.hWnd = hwnd;
    295. stDisplayInfo.pData = stImageInfo.pBufAddr;
    296. stDisplayInfo.nDataLen = stImageInfo.stFrameInfo.nFrameLen;
    297. stDisplayInfo.nWidth = stImageInfo.stFrameInfo.nWidth;
    298. stDisplayInfo.nHeight = stImageInfo.stFrameInfo.nHeight;
    299. stDisplayInfo.enPixelType = stImageInfo.stFrameInfo.enPixelType;
    300. //转qt QImage 给到绘图窗口
    301. if(isCapture){
    302. mvToQImage(stDisplayInfo);
    303. }
    304. //调整窗口尺寸
    305. // std::call_once(flag, [&](){
    306. // int cW = stImageInfo.stFrameInfo.nWidth;
    307. // int cH = stImageInfo.stFrameInfo.nHeight;
    308. // });
    309. MV_CC_DisplayOneFrame(handle, &stDisplayInfo);
    310. }
    311. nRet = MV_CC_FreeImageBuffer(handle, &stImageInfo);
    312. if(nRet != MV_OK)
    313. {
    314. printf("Free Image Buffer fail! nRet [0x%x]\n", nRet);
    315. }
    316. }
    317. else
    318. {
    319. printf("Get Image fail! nRet [0x%x]\n", nRet);
    320. }
    321. if(bExit)
    322. {
    323. break;
    324. }
    325. }
    326. }
    327. void CameraWidget::mvToQImage(MV_DISPLAY_FRAME_INFO &stDisplayInfo)
    328. {
    329. QImage image;
    330. if(stDisplayInfo.enPixelType==PixelType_Gvsp_Mono8){
    331. image= QImage(stDisplayInfo.pData, stDisplayInfo.nWidth, stDisplayInfo.nHeight, QImage::Format_Indexed8);
    332. }else if(stDisplayInfo.enPixelType==PixelType_Gvsp_RGB8_Packed){
    333. image= QImage(stDisplayInfo.pData, stDisplayInfo.nWidth, stDisplayInfo.nHeight, QImage::Format_RGB888);
    334. }
    335. emit capture(image);
    336. isCapture=false;
    337. }

    上层绘图窗口代码:

    1. #ifndef CAMERAWIDGET_H
    2. #define CAMERAWIDGET_H
    3. #include <QWidget>
    4. #include <windows.h>
    5. #include <MvCameraControl.h>
    6. #include "./util/PSEventController.h"
    7. #include "graphwidget.h"
    8. class CameraWidget : public QWidget
    9. {
    10. Q_OBJECT
    11. public:
    12. explicit CameraWidget(QWidget *parent = nullptr);
    13. ~CameraWidget();
    14. void updatePos(int x,int y);
    15. private:
    16. bool findDevice();
    17. bool printDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo);
    18. void getImage();
    19. bool openDevice();
    20. bool closeDevice();
    21. private:
    22. int nRet = MV_OK;
    23. void* handle = NULL;
    24. MV_CC_DEVICE_INFO_LIST stDeviceList = {0};
    25. std::atomic_bool bExit = true;
    26. HWND hwnd = NULL;
    27. GraphWidget * graphWidget=nullptr;
    28. QWidget *videoWidget =nullptr;
    29. std::atomic_bool isCapture{false};
    30. private:
    31. void initData();
    32. void mvToQImage(MV_DISPLAY_FRAME_INFO &stDisplayInfo);
    33. public slots:
    34. void on_psEvent_capture(bool isChecked);
    35. void on_psEvent_adjustImage(bool isChecked);
    36. void on_psEvent_fixImage(bool isChecked);
    37. signals:
    38. void capture(QImage image);
    39. };
    40. #endif // CAMERAWIDGET_H
    41. #include "graphwidget.h"
    42. #include <QGridLayout>
    43. #include <QPainter>
    44. #include <QMouseEvent>
    45. #include <QDebug>
    46. #include "../graphics/bqgraphicsitem.h"
    47. #define VIEW_CENTER viewport()->rect().center()
    48. #define VIEW_WIDTH viewport()->rect().width()
    49. #define VIEW_HEIGHT viewport()->rect().height()
    50. GraphWidget::GraphWidget(QWidget *parent)
    51. : QGraphicsView{parent}
    52. {
    53. setAttribute(Qt::WA_TranslucentBackground);
    54. setStyleSheet("background: transparent;border:0px");
    55. setWindowFlags(Qt::FramelessWindowHint);
    56. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    57. setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    58. setRenderHint(QPainter::Antialiasing);
    59. scene = new QGraphicsScene(this);
    60. this->setScene(scene);
    61. setDragMode(ScrollHandDrag);
    62. this->setSceneRect(this->geometry());
    63. centerOn(0, 0);
    64. initData();
    65. }
    66. void GraphWidget::setIsScale(bool newIsScale)
    67. {
    68. isScale = newIsScale;
    69. if(!isScale){
    70. revertSize();
    71. }
    72. }
    73. void GraphWidget::setBackgroundImage(const QPixmap &newBackgroundImage,bool newIsSetBackground)
    74. {
    75. qDebug()<<"setBackgroundImage "<<newIsSetBackground;
    76. isSetBackground=newIsSetBackground;
    77. if(backgroundItem){
    78. scene->removeItem(backgroundItem);
    79. backgroundItem=nullptr;
    80. }
    81. if(isSetBackground){
    82. setAttribute(Qt::WA_TranslucentBackground,false);
    83. backgroundImage = newBackgroundImage;
    84. if(!backgroundImage.isNull()){
    85. backgroundItem = scene->addPixmap(backgroundImage);
    86. backgroundItem->setPos(-this->width()/2,-this->height()/2);
    87. backgroundItem->setZValue(-1000);
    88. backgroundItem->setFlags(QGraphicsItem::ItemIsSelectable |
    89. QGraphicsItem::ItemIsMovable |
    90. QGraphicsItem::ItemIsFocusable);
    91. update();
    92. }
    93. }else{
    94. setAttribute(Qt::WA_TranslucentBackground,true);
    95. revertSize();
    96. }
    97. }
    98. void GraphWidget::clearItem()
    99. {
    100. for(int i=0;i<scene->items().size();i++){
    101. QGraphicsItem *item = scene->items().at(i);
    102. if(item->zValue()==-1000){
    103. continue;
    104. }else{
    105. scene->removeItem(scene->items().at(i));
    106. }
    107. }
    108. }
    109. void GraphWidget::on_psEvent_addLine(bool isChecked)
    110. {
    111. clearItem();
    112. BRectangle *m_rectangle = new BRectangle(0, 0, 100, 50, BGraphicsItem::ItemType::Rectangle);
    113. scene->addItem(m_rectangle);
    114. }
    115. void GraphWidget::on_psEvent_addCircle(bool isChecked)
    116. {
    117. clearItem();
    118. BConcentricCircle *m_conCircle = new BConcentricCircle(0, 0, 50, 80, BGraphicsItem::ItemType::Concentric_Circle);
    119. scene->addItem(m_conCircle);
    120. }
    121. void GraphWidget::on_psEvent_addEllipse(bool isChecked)
    122. {
    123. clearItem();
    124. BEllipse *m_ellipse = new BEllipse(0, 0, 100, 50, BGraphicsItem::ItemType::Ellipse);
    125. scene->addItem(m_ellipse);
    126. }
    127. void GraphWidget::on_psEvent_addArc(bool isChecked)
    128. {
    129. clearItem();
    130. BPie *m_pie = new BPie(0, 0, 80, 30, BGraphicsItem::ItemType::Pie);
    131. scene->addItem(m_pie);
    132. }
    133. void GraphWidget::wheelEvent(QWheelEvent *event)
    134. {
    135. if(isScale){
    136. static float scale = 1.1;
    137. auto angle = event->angleDelta();
    138. if(angle.y() > 0)
    139. {
    140. this->scale(scale, scale);
    141. }
    142. else
    143. {
    144. this->scale(1/scale, 1/scale);
    145. }
    146. }
    147. }
    148. void GraphWidget::initData()
    149. {
    150. PSEventController::subscribe(this,"addLine");
    151. PSEventController::subscribe(this,"addCircle");
    152. PSEventController::subscribe(this,"addEllipse");
    153. PSEventController::subscribe(this,"addArc");
    154. }
    155. void GraphWidget::revertSize()
    156. {
    157. setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    158. QMatrix q;
    159. q.setMatrix(1,this->matrix().m12(),this->matrix().m21(),1,this->matrix().dx(),this->matrix().dy());
    160. setMatrix(q,false);
    161. }

    主窗口移动和缩放时更新视频窗口位置:

    1. void MainWindow::updateGraphWidgetPos()
    2. {
    3. if(cameraWidget){
    4. QPoint p = mapToGlobal(cameraWidget->parentWidget()->pos());
    5. cameraWidget->updatePos(p.x(),p.y()+22);
    6. }
    7. }
    8. void MainWindow::resizeEvent(QResizeEvent *e)
    9. {
    10. updateGraphWidgetPos();
    11. return QWidget::resizeEvent(e);
    12. }
    13. void MainWindow::moveEvent(QMoveEvent *e)
    14. {
    15. updateGraphWidgetPos();
    16. return QWidget::moveEvent(e);
    17. }

  • 相关阅读:
    关于Vue3和Vue-rounter的几个理解
    Windows批处理:bat文件学习
    【PostgreSQL内核学习(十七)—— (AutoAnalyze)】
    【Django】开发日报_1.1_Day:模板语法
    【面试题】Ajax
    flex布局原理,常见的父项属性,flex布局子项常见属性,利用flex布局和float制作的有轮播图的携程旅行首页
    大模型提示工程之Prompt框架和示例
    vue优化之如何管理系统变量
    BOM之location对象
    【面试题】网络TCP之拥塞窗口(第四篇)
  • 原文地址:https://blog.csdn.net/weixin_38416696/article/details/134523776