基于Qt的tcp客户端和服务器实现摄像头帧数据处理(客户端部分)
程序员文章站
2024-01-27 21:40:46
项目简述 项目简述 实现客户端调用摄像头,并以帧的形式将每一帧传输到服务端,服务端将图片进行某些处理后再返回给客户端。(客户端与服务端通信代码部分参考《Qt5 开发及实例》) 项目步骤 客户端的编写 通过Qt Designer画出如下界面 项目步骤 客户端的编写 在客户端工程文件“client、pr ......
-
项目简述
实现客户端调用摄像头,并以帧的形式将每一帧传输到服务端,服务端将图片进行某些处理后再返回给客户端。(客户端与服务端通信代码部分参考《Qt5 开发及实例》)
-
项目步骤
-
客户端的编写
- 通过Qt Designer画出如下界面
- 在客户端工程文件“client、pro”中加入 QT += network语句,并将环境路径配置好。
- 在头文件“client.h”中加入如下代码:
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 };
- 在源文件“client.cpp”的构造函数中加入如下代码:
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 }
槽函数nextFrame()通过调用openCV,实现获取每一帧图像:
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 }
函数Mat2QImage(cv::Mat cvImg)代码如下:
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 }
以上代码中,槽函数slotEnter()实现连接和断开服务器功能(但现在断开后再连上数据传输有问题),代码如下:
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 }
槽函数“slotConnected()”是connected()信号的响应槽,当与服务器连接成功后,客户端进行一些操作,具体代码如下:
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 }
槽函数“slotDisconnected()”是disconnected()信号的响应槽,当与服务器断开连接时,进行如下操作:
1 void Client::slotDisconnected() 2 { 3 ui->handPushButton->setEnabled(false); 4 ui->facePushButton->setEnabled(false); 5 ui->ConnectPushButton->setText(tr("Enter")); 6 }
当发出connectedToServer()信号后,槽函数slotSend()响应,具体代码如下:
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 }
其中函数picToData(cv::Mat frame)代码如下:
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 }
函数dataReceived()当客户端收到服务端数据时响应,代码如下:
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 }
最后的emit connectedToServer();实现每次发送一次数据给服务端后,等待服务端响应后再发送数据,避免一直发数据,同时又接收数据而出现问题。
最后,将处理后的图像显示,调用函数ShowImage(QByteArray ba)实现,代码如下:
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 }
最后的最后,不要忘了调用析构函数:
1 Client::~Client() 2 { 3 delete ui; 4 delete tcpSocket; 5 delete timer; 6 delete serverIP; 7 }
上一篇: 来看能应对复杂地形的软性立方机器人
下一篇: C#实现给定字符串生成MD5哈希的方法