当前位置: 首页 > news >正文

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

需求

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

效果

 思路:

自己取图然后转成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);}}//设置触发模式为offnRet = 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 imagenRet = 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 imagenRet = MV_CC_StopGrabbing(handle);if (MV_OK != nRet){printf("Stop Grabbing fail! nRet [0x%x]\n", nRet);break;}// ch:关闭设备 | Close devicenRet = MV_CC_CloseDevice(handle);if (MV_OK != nRet){printf("ClosDevice fail! nRet [0x%x]\n", nRet);break;}// ch:销毁句柄 | Destroy handlenRet = 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 nameprintf("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);
}

相关文章:

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

需求 在视频窗口上进行绘图&#xff0c;包括圆&#xff0c;矩形&#xff0c;扇形等 效果&#xff1a; 思路&#xff1a; 自己取图然后转成QImage &#xff0c;再向QWidget 进行渲染&#xff0c;根据以往的经验&#xff0c;无法达到很高的帧率。因此决定使用相机SDK自带的渲染…...

FlinkCDC实现主数据与各业务系统数据的一致性(瀚高、TIDB)

文章末尾附有flinkcdc对应瀚高数据库flink-cdc-connector代码下载地址 1、业务需求 目前项目有主数据系统和N个业务系统,为保障“一数一源”,各业务系统表涉及到主数据系统的字段都需用主数据系统表中的字段进行实时覆盖,这里以某个业务系统的一张表举例说明:业务系统表Ta…...

JSP:Servlet

Servlet处理请求过程 B/S请求响应模型 Servlet介绍 JSP是Servlet的一个成功应用&#xff0c;其子集。 JSP页面负责前台用户界面&#xff0c;JavaBean负责后台数据处理&#xff0c;一般的Web应用采用JSPJavaBean就可以设计得很好了。 JSPServletJavaBean是MVC Servlet的核心…...

react中的state

没想到hooks中也有state这一说法 看下面的两个案例 1、无state变化不会执行父子函数 2、有state更改执行父子函数...

VR全景航拍要注意什么,航拍图片如何处理

引言: VR全景航拍技术是当前摄影和航拍领域的新潮流。它采用虚拟现实技术&#xff0c;通过360度全景镜头捕捉画面&#xff0c;可以为观众提供身临其境的视觉体验。在宣传展示中&#xff0c;利用VR全景航拍技术可以为品牌宣传带来更加生动、震撼的视觉效果。 一、航拍注意事项 …...

Spark---集群搭建

Standalone集群搭建与Spark on Yarn配置 1、Standalone Standalone集群是Spark自带的资源调度框架&#xff0c;支持分布式搭建&#xff0c;这里建议搭建Standalone节点数为3台&#xff0c;1台master节点&#xff0c;2台worker节点&#xff0c;这虚拟机中每台节点的内存至少给…...

Linux上通过SSL/TLS和start tls连接到LDAP服务器

一&#xff0c;大致流程。 1.首先在Linux上搭建一个LDAP服务器 2.在LDAP服务器上安装CA证书&#xff0c;服务器证书&#xff0c;因为SSL/TLS&#xff0c;start tls都属于机密通信&#xff0c;需要客户端和服务器都存在一个相同的证书认证双方的身份。3.安装phpldapadmin工具&am…...

【华为OD题库-034】字符串化繁为简-java

题目 给定一个输入字符串&#xff0c;字符串只可能由英文字母(a ~ z、A ~ Z)和左右小括号()组成。当字符里存在小括号时&#xff0c;小括号是成对的&#xff0c;可以有一个或多个小括号对&#xff0c;小括号对不会嵌套&#xff0c;小括号对内可以包含1个或多个英文字母也可以不…...

斯坦福大学引入FlashFFTConv来优化机器学习中长序列的FFT卷积

斯坦福大学的FlashFFTConv优化了扩展序列的快速傅里叶变换(FFT)卷积。该方法引入Monarch分解&#xff0c;在FLOP和I/O成本之间取得平衡&#xff0c;提高模型质量和效率。并且优于PyTorch和FlashAttention-v2。它可以处理更长的序列&#xff0c;并在人工智能应用程序中打开新的可…...

信息系统项目管理师-干系人管理论文提纲

快速导航 1.信息系统项目管理师-项目整合管理 2.信息系统项目管理师-项目范围管理 3.信息系统项目管理师-项目进度管理 4.信息系统项目管理师-项目成本管理 5.信息系统项目管理师-项目质量管理 6.信息系统项目管理师-项目资源管理 7.信息系统项目管理师-项目沟通管理 8.信息系…...

Windmill:最快的自托管开源工作流引擎

我们对 Windmill 进行了基准测试&#xff0c;认为它是 Airflow、Prefect 甚至 Temporal 中最快的自托管通用工作流引擎。对于 Airflow&#xff0c;有速度快了 10 倍&#xff01; 工作流引擎编排工作人员的有向无环图 (DAG) 中定义的作业&#xff0c;同时尊重依赖性。 主要优点…...

线性代数 - 几何原理

目录 序言向量的定义线性组合、张成空间与向量基线性变换和矩阵线性复合变换与矩阵乘法三维空间的线性变换行列式矩阵的秩和逆矩阵维度变换点乘叉乘基变换特征值和特征向量抽象向量空间 序言 欢迎阅读这篇关于线性代数的文章。在这里&#xff0c;我们将从一个全新的角度去探索线…...

火电厂电气部分设计

摘要 本文首先根据任务书上所给系统与线路及所有负荷的参数&#xff0c;分析负荷发展趋势。从负荷增长方面阐明了建站的必要性&#xff0c;然后通过对拟建变电站的概括以及出线方向来考虑&#xff0c;并通过对负荷资料的分析&#xff0c;安全&#xff0c;经济及可靠性方面考虑…...

界面组件DevExpress Reporting v23.1 - Web报表设计器功能升级

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表 界面组件DevExpress Reporting v23.1已经发布一段…...

小程序Canvas 2D问题解决,如安卓drawImage不执行、动态高度设置、高度1365(或4096)限制等

我的最新版小程序想在绘制时使用自定义字体&#xff0c;需要将旧版canvas升级到2d新版&#xff0c;发现了许多问题&#xff0c;下面记录一下并提供解决思路&#xff0c;仅供参考&#xff0c;欢迎提供新思路。 一、开发工具和安卓上drawImage不执行&#xff0c;绘制出来是空白&…...

人工智能对网络安全的影响越来越大

如果问当前IT行业最热门的话题是什么&#xff0c;很少有人会回答除了人工智能&#xff08;AI&#xff09;之外的任何话题。 在不到 12 个月的时间里&#xff0c;人工智能已经从一项只有 IT 专业人员才能理解的技术发展成为从小学生到作家、程序员和艺术家的每个人都使用的工具…...

JavaEE(SpringMVC)期末复习

文章目录 JavaEE期末复习一、单选题&#xff1a; JavaEE期末复习 一、单选题&#xff1a; 1.Spring的核⼼技术是&#xff08; A &#xff09;&#xff1f; A依赖注入 B.JdbcTmplate C.声明式事务 D.资源访问 Spring的核心技术包括依赖注入&#xff08;Dependency Injection&am…...

微服务保护 Sentinel

1.初识Sentinel 文章目录 1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结 1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel 1.4.微服务整合Sentinel 2.流量控制2.1.簇点链…...

【无标题】文本超过一行隐藏,鼠标经过显示提示框

创建一个组件专门用来出来文字的 <template><div class"tooltip-wrap"><el-tooltipref"tlp":content"text"effect"dark":disabled"!tooltipFlag":placement"placement"popper-class"tooltip…...

成为独立开发者有多难

首先自我介绍&#xff1a;我是一名前端开发工程师&#xff0c;7年的前端开发经验。CSDN 九段刀客_js,vue,ReactNative-CSDN博客,80多万的访问量&#xff0c;1万多的粉丝。 相信80%的程序员的终极梦想都是成为一名独立开发者&#xff0c;不用找工作有自己的产品可以有睡后收入。…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...