您好,登录后才能下订单哦!
# Qt Onvif抓拍图片怎么实现
## 前言
在安防监控、智能家居等领域,通过ONVIF协议与网络摄像头交互是常见需求。Qt作为跨平台开发框架,结合ONVIF协议实现抓拍功能具有广泛应用价值。本文将详细介绍基于Qt实现ONVIF协议图片抓拍的全过程。
---
## 一、ONVIF协议基础
### 1.1 ONVIF协议简介
ONVIF(Open Network Video Interface Forum)是一个全球开放的行业论坛,旨在促进网络视频设备之间的互操作性。其核心是通过标准化接口实现:
- 设备发现
- 设备管理
- 视频流控制
- 事件处理等
### 1.2 抓拍相关服务
实现抓拍主要涉及以下ONVIF服务:
- **Media服务**:获取视频流URI
- **Imaging服务**:图像参数设置
- **PTZ服务**(可选):云台控制
### 1.3 通信方式
ONVIF基于SOAP协议,使用XML格式数据通过HTTP/HTTPS传输。
---
## 二、开发环境准备
### 2.1 Qt开发环境
```bash
# 示例:通过apt安装Qt开发环境(Ubuntu)
sudo apt install qt5-default qtcreator
sudo apt install gsoap
sudo apt install libssl-dev
使用gSOAP生成基础代码:
wsdl2h -c -s -t typemap.dat -o onvif.h https://www.onvif.org/ver10/media/wsdl/media.wsdl
soapcpp2 -j -CL -I/usr/local/share/gsoap/import onvif.h
在.pro
文件中添加:
QT += network xml
LIBS += -lgsoap++
INCLUDEPATH += $$PWD/gsoap_generated
struct OnvifAuthInfo {
QString endpoint; // 设备地址
QString username; // 用户名
QString password; // 密码
};
QList<QHostAddress> discoverDevices()
{
QUdpSocket socket;
QByteArray probeMsg = constructProbeMessage();
socket.writeDatagram(probeMsg, QHostAddress("239.255.255.250"), 3702);
QList<QHostAddress> devices;
while(socket.waitForReadyRead(1000)){
while(socket.hasPendingDatagrams()){
QByteArray datagram;
datagram.resize(socket.pendingDatagramSize());
QHostAddress sender;
socket.readDatagram(datagram.data(), datagram.size(), &sender);
if(parseDiscoveryResponse(datagram)){
devices.append(sender);
}
}
}
return devices;
}
QString getMediaProfile(struct soap* soap, const OnvifAuthInfo& auth)
{
_trt__GetProfiles request;
_trt__GetProfilesResponse response;
// 设置认证信息
soap_wsse_add_UsernameTokenDigest(soap, nullptr,
auth.username.toStdString().c_str(),
auth.password.toStdString().c_str());
if(soap_call___trt__GetProfiles(soap, auth.endpoint.toStdString().c_str(),
nullptr, &request, response) == SOAP_OK)
{
if(response.Profiles.empty()) return "";
return QString::fromStdString(response.Profiles[0]->token);
}
return "";
}
QString getSnapshotUri(struct soap* soap, const OnvifAuthInfo& auth,
const QString& profileToken)
{
_trt__GetSnapshotUri request;
_trt__GetSnapshotUriResponse response;
request.ProfileToken = profileToken.toStdString();
if(soap_call___trt__GetSnapshotUri(soap, auth.endpoint.toStdString().c_str(),
nullptr, &request, response) == SOAP_OK)
{
return QString::fromStdString(response.MediaUri->Uri);
}
return "";
}
bool captureImage(const OnvifAuthInfo& auth, const QString& savePath)
{
struct soap* soap = soap_new();
QString profileToken = getMediaProfile(soap, auth);
if(profileToken.isEmpty()) return false;
QString snapshotUri = getSnapshotUri(soap, auth, profileToken);
if(snapshotUri.isEmpty()) return false;
// 创建带认证的HTTP请求
QNetworkRequest request(QUrl(snapshotUri));
QString authData = QString("%1:%2").arg(auth.username).arg(auth.password);
request.setRawHeader("Authorization", "Basic " +
authData.toLocal8Bit().toBase64());
// 同步请求图像数据
QNetworkAccessManager manager;
QNetworkReply* reply = manager.get(request);
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if(reply->error() == QNetworkReply::NoError){
QFile file(savePath);
if(file.open(QIODevice::WriteOnly)){
file.write(reply->readAll());
file.close();
return true;
}
}
return false;
}
class OnvifCaptureWindow : public QMainWindow
{
Q_OBJECT
public:
explicit OnvifCaptureWindow(QWidget *parent = nullptr);
private slots:
void onDiscoverClicked();
void onCaptureClicked();
private:
QLineEdit* ipEdit;
QLineEdit* userEdit;
QLineEdit* passEdit;
QPushButton* discoverBtn;
QPushButton* captureBtn;
QListWidget* deviceList;
};
void OnvifCaptureWindow::onDiscoverClicked()
{
deviceList->clear();
QList<QHostAddress> devices = discoverDevices();
foreach(const QHostAddress& addr, devices){
deviceList->addItem(addr.toString());
}
}
void OnvifCaptureWindow::onCaptureClicked()
{
OnvifAuthInfo auth;
auth.endpoint = "http://" + ipEdit->text() + "/onvif/Media";
auth.username = userEdit->text();
auth.password = passEdit->text();
QString savePath = QFileDialog::getSaveFileName(this, "保存图片",
"", "JPEG Files (*.jpg)");
if(!savePath.isEmpty()){
if(captureImage(auth, savePath)){
QMessageBox::information(this, "成功", "图片抓拍成功!");
}else{
QMessageBox::warning(this, "错误", "抓拍失败,请检查设备信息");
}
}
}
现象:返回401 Unauthorized
解决方案:
1. 检查用户名/密码是否正确
2. 确认设备是否开启ONVIF认证
3. 尝试使用Wireshark抓包分析
排查步骤:
1. 确认设备IP地址正确
2. 检查防火墙设置
3. 使用curl
测试基本连接:
curl -v http://<设备IP>/onvif/Media
可通过Imaging服务调整参数:
void setImageSettings(struct soap* soap, const OnvifAuthInfo& auth,
const QString& profileToken)
{
_timg__GetImagingSettings request;
_timg__GetImagingSettingsResponse response;
request.VideoSourceToken = profileToken.toStdString();
if(soap_call___timg__GetImagingSettings(soap, auth.endpoint.toStdString().c_str(),
nullptr, &request, response) == SOAP_OK)
{
// 修改亮度、对比度等参数
response.ImagingSettings->Brightness = 0.5;
// ...其他参数设置
}
}
void startTimedCapture(int intervalSec)
{
QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, [this](){
QString filename = QString("capture_%1.jpg")
.arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"));
captureImage(currentAuth, filename);
});
timer->start(intervalSec * 1000);
}
建议实现方案:
1. 使用QMap<QString, OnvifAuthInfo>
管理多个设备
2. 为每个摄像头创建独立线程
3. 实现批量抓拍功能
cv::Mat qtImageToMat(const QImage& qimage)
{
return cv::Mat(qimage.height(), qimage.width(),
CV_8UC3, const_cast<uchar*>(qimage.bits()),
qimage.bytesPerLine());
}
void processCapturedImage(const QString& path)
{
QImage img(path);
if(!img.isNull()){
cv::Mat frame = qtImageToMat(img);
// 进行OpenCV图像处理...
}
}
本文详细介绍了基于Qt实现ONVIF协议图片抓拍的完整流程。关键点包括: 1. 正确配置gSOAP生成ONVIF框架代码 2. 实现设备发现和认证机制 3. 获取快照URI并发送带认证的HTTP请求
实际开发中还需考虑异常处理、多线程优化等问题。建议参考ONVIF官方文档不断完善功能。
注意事项: - 不同厂商设备可能存在实现差异 - 生产环境建议添加HTTPS支持 - 注意处理设备断线重连等异常情况
通过本文介绍的方法,开发者可以快速构建基于ONVIF协议的监控系统抓拍功能。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。