欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)

程序员文章站 2022-05-23 21:44:44
项目简述 项目简述 实现客户端调用摄像头,并以帧的形式将每一帧传输到服务端,服务端将图片进行某些处理后再返回给客户端。(客户端与服务端通信代码部分参考《Qt5 开发及实例》) 项目步骤 客户端的编写 通过Qt Designer画出如下界面 项目步骤 客户端的编写 在客户端工程文件“client、pr ......
  • 项目简述

  实现客户端调用摄像头,并以帧的形式将每一帧传输到服务端,服务端将图片进行某些处理后再返回给客户端。(客户端与服务端通信代码部分参考《Qt5 开发及实例》)

  • 项目步骤

  • 客户端的编写

  •   通过Qt Designer画出如下界面

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)

  • 在客户端工程文件“client、pro”中加入 QT += network语句,并将环境路径配置好。
  • 在头文件“client.h”中加入如下代码:
    基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
     1 class Client : public QWidget
     2 {
     3     Q_OBJECT
     4 
     5 public:
     6     explicit Client(QWidget *parent = 0);
     7     ~Client();
     8 
     9 private slots:
    10     void slotEnter();
    11     void slotConnected();
    12     void slotDisconnected();
    13     void dataReceived();
    14     void slotSend();
    15     void nextFrame();
    16 signals:
    17     void connectedToServer(void);
    18 
    19 private:
    20     Ui::Client *ui;
    21     bool status;
    22     int port;
    23     QHostAddress *serverIP;
    24     QString userName;
    25     QTcpSocket *tcpSocket;
    26 
    27     qint64 receiveBlockSize;
    28 
    29     Mat frame;
    30     VideoCapture cap;
    31     QTimer* timer;
    32     QImage image;
    33 
    34     void ShowImage(QByteArray ba);
    35     QByteArray picToData(cv::Mat frame);
    36     QImage Mat2QImage(cv::Mat cvImg);
    37 };
    View Code

     

  • 在源文件“client.cpp”的构造函数中加入如下代码:
    基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
     1 Client::Client(QWidget *parent) :
     2     QWidget(parent),
     3     ui(new Ui::Client)
     4 {
     5     ui->setupUi(this);
     6 
     7     setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint);
     8     setFixedSize(this->width(),this->height());
     9 
    10     status=false;
    11     port=8010;
    12     ui->portLineEdit->setText(QString::number(port));
    13 
    14     serverIP=new QHostAddress();
    15     ui->serverIPLineEdit->setText("127.0.0.1");
    16     connect(ui->ConnectPushButton,SIGNAL(clicked(bool)),this,SLOT(slotEnter()));
    17 
    18     receiveBlockSize=0;
    19 
    20     timer=new QTimer(this);
    21     timer->start(30);
    22 
    23     cap.open(0);
    24     if(!cap.isOpened())
    25     {
    26         QMessageBox::information(this,tr("error"),tr("cam don't open"));
    27     }
    28 
    29     connect(timer,&QTimer::timeout,this,&Client::nextFrame);
    30 }
    View Code

     

槽函数nextFrame()通过调用openCV,实现获取每一帧图像:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 void Client::nextFrame()
 2 {
 3     cap>>frame;
 4     if (!frame.empty())
 5     {
 6         image = Mat2QImage(frame);
 7     
 8         //使图像适应label的大小
 9         QPixmap *pixmap = new QPixmap(QPixmap::fromImage(image));
10         pixmap->scaled(ui->capturedImage->size(), Qt::KeepAspectRatio);
11         ui->capturedImage->setScaledContents(true);
12         ui->capturedImage->setPixmap(*pixmap);
13         delete pixmap;//避免内存泄漏
14     }
15 }
View Code

 

函数Mat2QImage(cv::Mat cvImg)代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 QImage Client::Mat2QImage(cv::Mat cvImg)
 2 {
 3     QImage qImg;
 4     if(cvImg.channels()==3)                             //3 channels color image
 5     {
 6 
 7         cv::cvtColor(cvImg,cvImg,CV_BGR2RGB);
 8         qImg =QImage((const unsigned char*)(cvImg.data),
 9                      cvImg.cols, cvImg.rows,
10                      cvImg.cols*cvImg.channels(),
11                      QImage::Format_RGB888);
12     }
13     else if(cvImg.channels()==1)                    //grayscale image
14     {
15         qImg =QImage((const unsigned char*)(cvImg.data),
16                      cvImg.cols,cvImg.rows,
17                      cvImg.cols*cvImg.channels(),
18                      QImage::Format_Indexed8);
19     }
20     else
21     {
22         qImg =QImage((const unsigned char*)(cvImg.data),
23                      cvImg.cols,cvImg.rows,
24                      cvImg.cols*cvImg.channels(),
25                      QImage::Format_RGB888);
26     }
27     return qImg;
28 }
View Code

 

  以上代码中,槽函数slotEnter()实现连接和断开服务器功能(但现在断开后再连上数据传输有问题),代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 void Client::slotEnter()
 2 {
 3     if(!status)
 4     {
 5         QString ip=ui->serverIPLineEdit->text();
 6         if(!serverIP->setAddress(ip))
 7         {
 8             QMessageBox::information(this,tr("error"),
 9                                      tr("server ip address error"));
10             return;
11         }
12 
13         tcpSocket=new QTcpSocket(this);
14 
15         connect(tcpSocket,&QAbstractSocket::connected,this,&Client::slotConnected);
16 
17         connect(tcpSocket,&QAbstractSocket::disconnected,this,&Client::slotDisconnected);
18 
19         connect(tcpSocket,&QIODevice::readyRead,this,&Client::dataReceived);
20 
21         connect(this,&Client::connectedToServer,this,&Client::slotSend);
22 
23         tcpSocket->connectToHost(*serverIP,port);
24 
25 
26         status=true;
27     }
28     else
29     {
30 
31         tcpSocket->disconnectFromHost();
32 
33         status=false;
34     }
35 }
View Code

 

槽函数“slotConnected()”是connected()信号的响应槽,当与服务器连接成功后,客户端进行一些操作,具体代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
1 void Client::slotConnected()
2 {
3     ui->handPushButton->setEnabled(true);
4     ui->facePushButton->setEnabled(true);
5     ui->ConnectPushButton->setText(tr("Leave"));
6     //ui->ConnectPushButton->setEnabled(false);
7     emit connectedToServer();
8 }
View Code

 

槽函数“slotDisconnected()”是disconnected()信号的响应槽,当与服务器断开连接时,进行如下操作:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
1 void Client::slotDisconnected()
2 {
3     ui->handPushButton->setEnabled(false);
4     ui->facePushButton->setEnabled(false);
5     ui->ConnectPushButton->setText(tr("Enter"));
6 }
View Code

  

  当发出connectedToServer()信号后,槽函数slotSend()响应,具体代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 void Client::slotSend()
 2 {
 3     if(frame.empty())
 4         return;
 5 
 6     QByteArray byteArrayData=picToData(frame);
 7     QByteArray ba;
 8     QDataStream out(&ba,QIODevice::WriteOnly);
 9     out.setVersion(QDataStream::Qt_5_6);
10 
11     out<<(quint64)0;
12     out<<byteArrayData;
13     out.device()->seek(0);
14     out<<(quint64)(ba.size()-sizeof(quint64));
15     tcpSocket->write(ba);
16 }
View Code

 

  其中函数picToData(cv::Mat frame)代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 QByteArray Client::picToData(Mat frame)
 2 {
 3     QImage img((unsigned const char*)frame.data,frame.cols,frame.rows,QImage::Format_RGB888);
 4 
 5     QByteArray block;
 6     QBuffer buf(&block);
 7     img.save(&buf,"JPEG");//按照JPG解码保存数据
 8     QByteArray cc = qCompress(block,1);
 9     QByteArray hh;
10     hh=cc.toBase64();//base64数据
11     return hh;
12 }
View Code

 

  函数dataReceived()当客户端收到服务端数据时响应,代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 void Client::dataReceived()
 2 {
 3     ui->noteLabel->setText(tr("receive data"));
 4     QDataStream in(tcpSocket);
 5     QByteArray message;//存放从服务器接收到的字符串
 6     in.setVersion(QDataStream::Qt_5_6);
 7     if (receiveBlockSize==0)
 8     {
 9         //判断接收的数据是否有两字节(文件大小信息)
10         //如果有则保存到basize变量中,没有则返回,继续接收数据
11         if (tcpSocket->bytesAvailable()<(int)sizeof(quint64))
12         {
13             return;
14         }
15         in>>receiveBlockSize;
16     }
17     //如果没有得到全部数据,则返回继续接收数据
18     if (tcpSocket->bytesAvailable()<receiveBlockSize)
19     {
20         //qDebug()<<"tcpSocket->bytesAvailable()<receiveBlockSize";
21         return;
22     }
23     in>>message;//将接收到的数据存放到变量中
24     ShowImage(message);
25     receiveBlockSize=0;
26     emit connectedToServer();
27 }
View Code

  最后的emit connectedToServer();实现每次发送一次数据给服务端后,等待服务端响应后再发送数据,避免一直发数据,同时又接收数据而出现问题。

  最后,将处理后的图像显示,调用函数ShowImage(QByteArray ba)实现,代码如下:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
 1 void Client::ShowImage(QByteArray ba)
 2 {
 3     QString ss=QString::fromLatin1(ba.data(),ba.size());
 4     QByteArray rc;
 5     rc=QByteArray::fromBase64(ss.toLatin1());
 6     QByteArray rdc=qUncompress(rc);
 7     QImage img;
 8     img.loadFromData(rdc);
 9     ui->processedImage->setPixmap(QPixmap::fromImage(img));
10     ui->processedImage->resize(img.size());
11     update();
12 }
View Code

  

  最后的最后,不要忘了调用析构函数:

基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
1 Client::~Client()
2 {
3     delete ui;
4     delete tcpSocket;
5     delete timer;
6     delete serverIP;
7 }
View Code