Qt5&OpenCV3 UDP协议实现实时视频传输与通信

简介: 打算在树莓派上挂载摄像头,通过WIFI模块传输到上位机。局域网内带宽不是问题,为了保证实时性,也没有必要进行复杂的视频编码和解码,于是通过截图然后使用UDP协议传输应该是可以的。

打算在树莓派上挂载摄像头,通过WIFI模块传输到上位机。局域网内带宽不是问题,为了保证实时性,也没有必要进行复杂的视频编码和解码,于是通过截图然后使用UDP协议传输应该是可以的。所以最近试探性地使用了Qt和opencv进行测试,上位机接收到视频帧后使用Haar人脸识别后再传回一个坐标给下位机,结果还行。

img_21040723cca2777ccd7af10aa28d544d.png
cv界女神lena

1.下位机(图像采集端)

Qt中使用QUdpSocket类来发送和接收UDP数据报。Socket就是套接字,简单来说就是一个IP地址加上port端口号。它支持IPv4广播,简单起见这里使用广播模式。VideoCapture类用于获取视频或者摄像头设备。

private:
    Ui::Sender *ui;
    QUdpSocket *sender;
    QUdpSocket *receiver;
    cv::Mat frame;
    int timerID;
    cv::VideoCapture capture;
  • 在.h文件中声明两个QUdpSocket和QVideoCapture成员变量
Sender::Sender(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Sender)
{
    ui->setupUi(this);
    sender = new QUdpSocket(this);
    capture.open(0);
    if(!capture.isOpened())
    {
        cout << "open failed" <<endl;
    }
    int delay =1000/10;
    timerID = this->startTimer(delay);

    receiver = new QUdpSocket(this);
    receiver->bind(45455, QUdpSocket::ShareAddress);
    connect(receiver, &QUdpSocket::readyRead, this, &Sender::processPendingDatagram);
}
  • VideoCapture对象使用open()方法来打开视频或者摄像头。传入int是指定设备ID,可以在设备管理器查找,一般是0。也可以传入路径来打开视频文件。startTimer方法是打开Qt的软件定时器,参数是毫秒,这里用来采集摄像头的图像。1000/10即10FPS。

  • 为了方便处理,这里创建两个QUdpSocket对象,一个用于传输,一个用于接收,并分开两个端口。bind方法第一个参数是端口号,QUdpSocket::ShareAddress指允许其他服务器绑定到相同的地址和端口上。

  • 每次有数据报到来时,QUdpSocket都会发射readyRead()信号。连接这个信号到自定义的槽中,便可进行读取操作。

void Sender::timerEvent( QTimerEvent *event)
{
    if(event->timerId() == timerID)
    {
        if(capture.isOpened())
        {
            capture.read(frame);
        }
        cvtColor(frame,frame,CV_BGR2RGB); //BGRtoRGB
        QImage image((unsigned char *)(frame.data),
                         frame.cols,frame.rows,
                         QImage::Format_RGB888);
        ui->label->setPixmap(QPixmap::fromImage(image));
        ui->label->resize(image.width(),image.height());

        QByteArray byte;
        //字节数组 要进行传输必须先转换成这个格式
        QBuffer buff(&byte);
        // 建立一个用于IO读写的缓冲区
        image.save(&buff,"JPEG");
        // image先向下转为byte的类型,再存入buff

        QByteArray compressByte = qCompress(byte,1);
        //数据压缩算法
        QByteArray base64Byte = compressByte.toBase64();

        //数据加密算法
        sender->writeDatagram(base64Byte.data(),base64Byte.size(),
                             QHostAddress::Broadcast, 45454);
    }
}
  • VideoCapture类使用read()方法来读取视频帧,存入一个Mat中。Mat存储图像默认是BGR编码,为了方便转换为Qimage格式进行操作,需要使用cvtColor改变编码模式。

  • Mat中的data()方法返回数据的一个uchar型的指针。使用这个指针,指定行数列数和编码模式,就可以构造一个Qimage对象,并在Qlabel中显示出来。

  • QBuffer类用于各种IO的读写。Qimagesave()方法将数据转为byte并存入一个QBuffer对象中。对byte对象进行简单的编码后便可进行发送。

  • 类似的,QByteArray有一个data()方法来返回uchar指针。writeDatagram用于发送数据报,QHostAddress::Broadcast指使用广播模式。

void Sender::processPendingDatagram()
{
    QByteArray datagram;

    // 让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据
    datagram.resize(receiver->pendingDatagramSize());

    // 接收数据报,将其存放到datagram中
    receiver->readDatagram(datagram.data(), datagram.size());

    ui->label_2->setText(datagram);
}
  • 自定义的槽用于接收数据报。pendingDatagramSize()获得数据报的大小。readDatagram将数据存入一个QByteArray对象中。

2.上位机端(视频处理端)

Receiver::Receiver(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Receiver)
{
    ui->setupUi(this);
    sender = new QUdpSocket(this);
    receiver = new QUdpSocket(this);
    receiver->bind(45454, QUdpSocket::ShareAddress);
    connect(receiver, &QUdpSocket::readyRead, this, &Receiver::processPendingDatagram);
}

与发送端类似。

void Receiver::processPendingDatagram()
{
    QByteArray datagram;

    // 让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据
    datagram.resize(receiver->pendingDatagramSize());

    // 接收数据报,将其存放到datagram中
    receiver->readDatagram(datagram.data(), datagram.size());

    QByteArray decryptedByte;
    decryptedByte=QByteArray::fromBase64(datagram.data());
    QByteArray uncompressByte=qUncompress(decryptedByte);
    QImage image;
    image.loadFromData(uncompressByte);

    Mat matImage(image.height(),image.width(),CV_8UC4,(void*)image.constBits(),
                 image.bytesPerLine());
}
  • 解码解压后,需要将QImage对象转为Mat用于识别。这里使用的是Mat一个比较复杂的重载构造。第三个参数是数据类型,Qimage中的数据是ARGB888,占32位。第四个参数需要一个指向数据块的指针,constBits方法返回一个void指针。第四个参数需要一行的数据量,bytePerLine()方法可以获取。
    Mat matImage_gray;
    char itc[10];

    if(frameCount >= 5)
    {
        cvtColor(matImage, matImage_gray, CV_BGR2GRAY);//转为灰度图
        equalizeHist(matImage_gray, matImage_gray);//直方图均衡化,增加对比度方便处理

        CascadeClassifier face_cascade;    //载入分类器

        if (!face_cascade.load(FACE_CLASSIFIER_PATH))
        {
            cout << "Load haarcascade_frontalface_alt failed!" << endl;
        }

        //检测关于脸部位置
        face_cascade.detectMultiScale(matImage_gray, faceRect, 1.1, 2, 0, Size(30, 30));
        for (size_t i = 0; i < faceRect.size(); i++)
        {
            //用矩形画出检测到的位置
            rectangle(matImage, faceRect[i], Scalar(0, 0, 255));      
            sprintf(itc,"%d,%d",(faceRect[i].br().x+faceRect[i].tl().x)/2,
                    (faceRect[i].br().y+faceRect[i].tl().y)/2);
            cout << itc <<endl;
            sender->writeDatagram(itc,7,QHostAddress::Broadcast, 45455);
        }
        frameCount=0;
        cvtColor(matImage,matImage,CV_BGR2RGB); //BGRtoRGB
        image = QImage((unsigned char *)(matImage.data),
                         matImage.cols,matImage.rows,
                         QImage::Format_RGB888);
        //sender->write()
    }
    else
    {
        for (size_t i = 0; i < faceRect.size(); i++)
        {
            //用矩形画出检测到的位置
            rectangle(matImage, faceRect[i], Scalar(0, 0, 255));      
        }
        cvtColor(matImage,matImage,CV_BGR2RGB); //BGRtoRGB
        image = QImage((unsigned char *)(matImage.data),
                         matImage.cols,matImage.rows,
                         QImage::Format_RGB888);
        frameCount++;

    }
    ui->label->setPixmap(QPixmap::fromImage(image));
    ui->label->resize(image.width(),image.height());
}

这里使用机器学习算法级联增强分类器Haar特征图像模型。opencv官方例程中已经帮我们训练好了,直接使用即可。

  • 识别需要一定时间,这里每5帧处理一次。
  • FACE_CLASSIFIER_PATH是官方例程中的xml文件路径
  • 将检测到的(人脸)矩形中心传回下位机端

3.实验结果

img_a255c901c8d596888da04b1fe72f2fe1.png
上位机端

img_366cc7dea9353109987f69a49a5eb7f2.png
下位机端,左下角是坐标

ok,实验结果还是很成功的,下一步就是移植到树莓派上了。

目录
相关文章
|
17天前
|
计算机视觉 数据格式
使用opencv在Qt控件上播放mp4文件
使用opencv在Qt控件上播放mp4文件
24 2
|
2月前
|
运维 监控 网络协议
QT实现TCP通信:从基础到高级的全面解析
QT实现TCP通信:从基础到高级的全面解析
177 0
|
2月前
|
网络协议 网络安全 网络性能优化
使用到UDP协议的情况下该如何防护
使用到UDP协议的情况下该如何防护
|
2月前
|
机器学习/深度学习 算法 数据可视化
计算机视觉+深度学习+机器学习+opencv+目标检测跟踪+一站式学习(代码+视频+PPT)-2
计算机视觉+深度学习+机器学习+opencv+目标检测跟踪+一站式学习(代码+视频+PPT)
96 0
|
2月前
|
机器学习/深度学习 Ubuntu Linux
计算机视觉+深度学习+机器学习+opencv+目标检测跟踪+一站式学习(代码+视频+PPT)-1
计算机视觉+深度学习+机器学习+opencv+目标检测跟踪+一站式学习(代码+视频+PPT)
55 1
|
3月前
|
存储 监控 开发工具
Baumer工业相机堡盟工业相机如何联合NEOAPI SDK和OpenCV实现相机图像转换为AVI视频格式(C++)
Baumer工业相机堡盟工业相机如何联合NEOAPI SDK和OpenCV实现相机图像转换为AVI视频格式(C++)
35 0
|
30天前
|
机器学习/深度学习 API vr&ar
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
105 4
|
30天前
|
存储 C++ 网络架构
C++ Qt开发:QUdpSocket实现组播通信
Qt教程:使用`QUdpSocket`实现UDP组播通信。通过设置套接字选项、绑定端口、加入和离开组播组,以及发送和接收数据报,简化跨平台窗体应用开发。关键函数包括`setSocketOption`设置多播TTL,`bind`绑定地址和端口,`joinMulticastGroup`加入组播,`leaveMulticastGroup`退出,`writeDatagram`发送,和`readDatagram`接收数据报。
19 1
C++ Qt开发:QUdpSocket实现组播通信
|
1月前
|
安全 Linux 网络安全
Qt SSL/TLS 安全通信类:构建安全网络应用的关键组件
Qt SSL/TLS 安全通信类:构建安全网络应用的关键组件
62 0
|
1月前
|
网络协议 数据格式

推荐镜像

更多