实现客户端调用摄像头,并以帧的形式将每一帧传输到服务端,服务端将图片进行某些处理后再返回给客户端。(客户端与服务端通信代码部分参考《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<
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()
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 }
手机扫一扫
移动阅读更方便
你可能感兴趣的文章