1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Qt网络编程——使用OpenCV与TCP搭建图像处理服务器

Qt网络编程——使用OpenCV与TCP搭建图像处理服务器

时间:2021-04-05 10:32:46

相关推荐

Qt网络编程——使用OpenCV与TCP搭建图像处理服务器

前言

前面的博客有写过如果使用TCP搭建一个客户端与服务器,连接并互发信息,这里主是演示,如何把客户端的图像发往服务器,服务器得到图像后,按指令做不同的处理,并返回给客户端处理之后的结果,客户端只负责打开和发送图像,所有关于图像的图像的处理,比如灰度图像,人脸检测啊,都经过服务器处理之后返回给客户端。而服务器用来处理图像的是OpenCV库,

代码注释比较多,具体流程可以看源码,就应该了解。

代码

1.客户端

client.h

#ifndef CLIENT_H#define CLIENT_H#include <QMainWindow>#include <QTcpSocket>#include <QHostAddress>#include <QMessageBox>#include <QFile>#include <QFileDialog>#include <QCompleter>#include <QBuffer>#include <QDebug>#include <QTextEdit>#include <QImageReader>#include <QTextCursor>#include <QColor>QT_BEGIN_NAMESPACEnamespace Ui {class Client; }QT_END_NAMESPACEclass Client : public QMainWindow{Q_OBJECTpublic:Client(QWidget *parent = nullptr);~Client();void initUI();void insertImage(QTextEdit *ui_text, QImage &image);void transformImage(QImage &image,qint64 mask);QByteArray getImageData(const QImage &image);void transformImage(QImage &image, QByteArray &block,int mask);QImage getImage(const QString &data);QByteArray imageToBuffer(QImage &image);private slots:void on_buttonConnect_clicked();//连接服务器void on_buttonDisconnect_clicked();//断开连接void on_buttonOpenImage_clicked();//打开图像void readServerMessage();//读取消息void on_buttonFaceDetection_clicked();//人脸检测void on_buttonGaryImage_clicked();//灰度图像void on_buttonCannyImage_clicked();//边缘检测private:QTcpSocket *tcpClient;QImage image;Ui::Client *ui;QByteArray outBlock; //数据缓冲区,即存放每次要发送的数据块int totalBytes; // 发送数据的总大小int mask;QString fileName;int imageSize;int bytesReceived;};#endif // CLIENT_H

client.cpp

#include "client.h"#include "ui_client.h"Client::Client(QWidget *parent): QMainWindow(parent), ui(new Ui::Client){ui->setupUi(this);initUI();tcpClient = new QTcpSocket(this);//取消原有连接tcpClient->abort();bytesReceived = 0;imageSize = 0;connect(tcpClient, SIGNAL(readyRead()), this, SLOT(readServerMessage()));}//UI界面相关void Client::initUI(){ui->buttonDisconnect->setEnabled(false);}Client::~Client(){delete ui;}//连接服务器void Client::on_buttonConnect_clicked(){tcpClient->connectToHost(ui->lineEditServerIP->text(),ui->lineEditServerPorts->text().toInt());if(tcpClient->waitForConnected(1000)){ui->buttonConnect->setEnabled(false);ui->buttonDisconnect->setEnabled(true);ui->textEditStatus->append("连接服务器成功!");}else{ui->textEditStatus->append("连接失败,请检查IP地址和端口!");}}//断开void Client::on_buttonDisconnect_clicked(){tcpClient->disconnectFromHost();//断开成功if (tcpClient->state() == QAbstractSocket::UnconnectedState || tcpClient->waitForDisconnected(1000)){ui->buttonConnect->setEnabled(true);ui->textEditStatus->append("连接已断开!");ui->buttonDisconnect->setEnabled(false);ui->buttonConnect->setEnabled(true);}else{ui->textEditStatus->append("无法断开与服务器的连接!");}}void Client::on_buttonFaceDetection_clicked(){}void Client::on_buttonGaryImage_clicked(){transformImage(image,outBlock,10);if(image.isNull()){ui->textEditStatus->append("当前图像不能发送!");return;}if(outBlock.size() != 0){tcpClient->write(outBlock);ui->textEditStatus->append("图像发送成功!");ui->textEditInput->clear();}}void Client::on_buttonCannyImage_clicked(){transformImage(image,outBlock,9);if(image.isNull()){ui->textEditStatus->append("当前图像不能发送!");return;}if(outBlock.size() != 0){tcpClient->write(outBlock);ui->textEditStatus->append("图像发送成功!");ui->textEditInput->clear();}}//接收服务器端的信息并显示void Client::readServerMessage(){QDataStream in(tcpClient);in.setVersion(QDataStream::Qt_5_7);QImage imageData;QString imageContent;// 如果已接收到的数据小于16个字节,保存到文件头结构if (bytesReceived <= sizeof(int)*3){if((tcpClient->bytesAvailable() >= sizeof(int)*3)&& (imageSize == 0)){// 接收数据总大小信息和文件名大小信息in >>mask>> totalBytes >> imageSize;bytesReceived += sizeof(int) * 3;}if((tcpClient->bytesAvailable() >= imageSize) && (imageSize != 0)){// 接收文件,并建立文件in >> imageContent;imageData = getImage(imageContent);if(imageData.isNull()){ui->textEditStatus->append("没有接收图像数据!");}insertImage(ui->textEditAccept,imageData);bytesReceived += imageSize;if(bytesReceived == totalBytes){ui->textEditStatus->append("接收文件成功");totalBytes = 0;bytesReceived = 0;imageSize = 0;}}}}//QTextEdit显示图像void Client::insertImage(QTextEdit *ui_text_edit, QImage &image){QUrl Uri;QTextDocument * textDocument = ui_text_edit->document();textDocument->addResource( QTextDocument::ImageResource, Uri, QVariant ( image ) );QTextCursor cursor = ui_text_edit->textCursor();QTextImageFormat imageFormat;imageFormat.setWidth( image.width() );imageFormat.setHeight( image.height() );cursor.insertImage(imageFormat);}//打开图像按键void Client::on_buttonOpenImage_clicked(){fileName = QFileDialog::getOpenFileName(this);if (!fileName.isEmpty()){image = QImage(fileName);if(image.isNull()){ui->textEditStatus->append("打开图像失败!");return;}else{QBuffer buffer(&outBlock);buffer.open(QIODevice::WriteOnly);image.save(&buffer, "JPG");insertImage(ui->textEditInput,image);ui->textEditStatus->append("打开图像成功!");}}else{ui->textEditStatus->append("打开路径失败,请确定是否是图像路径!");return;}}//图像转换/*QImage &image 输入图像*QByteArray &block 输出流*int mask 指令*/void Client::transformImage(QImage &image, QByteArray &block,int mask){int total_size = 0;QDataStream sendOut(&block, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_5_7);//获得图片数据QString imageData = getImageData(image);// 保留总大小信息空间、图像大小信息空间,然后输入图像信息sendOut << int(0) << int(0) << int(0) << imageData;// 这里的总大小是总大小信息、图像大小信息和实际图像信息的总和total_size += block.size();sendOut.device()->seek(0);int image_size = int((block.size() - sizeof(int)*3));// 返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间sendOut << mask << total_size << image_size;}//图像转Base 64QByteArray Client::getImageData(const QImage &image){QByteArray imageData;//开缓冲区QBuffer buffer(&imageData);//存入缓冲区image.save(&buffer, "jpg");//转成Base64imageData = imageData.toBase64();return imageData;}//Base64 转图像QImage Client::getImage(const QString &data){QByteArray imageData = QByteArray::fromBase64(data.toLatin1());QImage image;image.loadFromData(imageData);return image;}

2.服务器端

server.h

#ifndef SERVER_H#define SERVER_H#include <QMainWindow>#include <QTcpServer>#include <QTcpSocket>#include <QNetworkInterface>#include <QMessageBox>#include <QFileDialog>#include <iostream>#include <QBuffer>#include <opencv2/opencv.hpp>#include <QTextEdit>#include <QFile>QT_BEGIN_NAMESPACEnamespace Ui {class Server; }QT_END_NAMESPACEclass Server : public QMainWindow{Q_OBJECTpublic:Server(QWidget *parent = nullptr);~Server();void initUI();//界面初始化QImage base64ToQImage(const QString &data);//base64转QImagevoid insertImage(QTextEdit *ui_text_edit, QImage &image);//QTextEdit显示图像cv::Mat QImage2cvMat(QImage &image);//QImage转MatQImage cvMat2QImage(const cv::Mat& mat);//Mat转QImagevoid transformImage(QImage &image, QByteArray &block,int mask);//封装发送数据QByteArray QImageToBase64(const QImage &image);//QImage转Base64void edgeDetection(cv::Mat &cv_src,cv::Mat &cv_dst);private:QTcpServer *tcpServer;//服务器类QList<QTcpSocket*> tcpClient;QTcpSocket *currentClient;int mask,total_size;int total_bytes;int bytes_received;int image_size;QString image_content;QByteArray out_block; //数据缓冲区,即存放每次要发送的数据块QImage image_data;QString file_name;QImage image;QByteArray buffer_all;private slots:void on_buttonMonitor_clicked();//监听事件void on_buttonDisconnect_clicked();//断开事件void newConnectionSlot();//新客户端连接事件void readMessageData();//接收信息void on_buttonOpenImage_clicked();//打开图像void on_buttonSendMessage_clicked();//发送图像private:Ui::Server *ui;};#endif // SERVER_H

server.cpp

#include "server.h"#include "ui_server.h"Server::Server(QWidget *parent): QMainWindow(parent), ui(new Ui::Server){ui->setupUi(this);initUI();tcpServer = new QTcpServer(this);image_size = 0;total_bytes = 0;bytes_received = 0;//有新的连接时的槽函数connect(tcpServer,SIGNAL(newConnection()),this, SLOT(newConnectionSlot()));}void Server::initUI(){ui->buttonDisconnect->setEnabled(false);}Server::~Server(){delete ui;}//启动服务器开始监听void Server::on_buttonMonitor_clicked(){//监听所有IP地址bool monitor = tcpServer->listen(QHostAddress::Any,ui->lineEditServerPorts->text().toInt());//按键状态if(monitor){ui->buttonMonitor->setEnabled(false);ui->buttonDisconnect->setEnabled(true);ui->textEditStatus->append("开始监端口......");}else{ui->textEditStatus->append("启动服务器失败!");}}//有新客户端连接时void Server::newConnectionSlot(){//返回套接字指针currentClient = tcpServer->nextPendingConnection();tcpClient.append(currentClient);ui->comboBoxIP->addItem(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1])\.arg(currentClient->peerPort()));//读取消息处理connect(currentClient, SIGNAL(readyRead()), this, SLOT(readMessageData()));}//断开与所有客户端的连接void Server::on_buttonDisconnect_clicked(){//如果有客户端连接if(tcpClient.length() > 0){for(int i = 0; i < tcpClient.length();i++){//断开tcpClient.at(i)->disconnectFromHost();//等待bool dis = tcpClient.at(i)->waitForDisconnected(1000);if(dis){//删除客户端tcpClient.removeAt(i);tcpServer->close();ui->buttonMonitor->setEnabled(true);ui->buttonDisconnect->setEnabled(false);ui->textEditStatus->append("断开连接成功!");ui->buttonMonitor->setEnabled(true);}else{ui->textEditStatus->append("断开连接失败!");}}}else{ui->textEditStatus->append("当前没有连接的客户端!");ui->buttonMonitor->setEnabled(true);}}//接收消息并显示到界面void Server::readMessageData(){//由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端//遍历所有连接的客户端for(int i=0; i<tcpClient.length(); i++){QDataStream in(tcpClient.at(i));// 如果已接收到的数据小于16个字节,保存到文件头结构if (bytes_received <= sizeof(int)*3){if((currentClient->bytesAvailable() >= sizeof(int)*3)&& (image_size == 0)){// 接收数据总大小信息和文件名大小信息in >>mask>>total_bytes >> image_size;bytes_received += sizeof(int) * 3;ui->textEditStatus->append("开始接收图像......");}if((currentClient->bytesAvailable() >= image_size) && (image_size != 0)){// 接收文件,并建立文件in >> image_content;image_data = base64ToQImage(image_content);insertImage(ui->textEditAccept,image_data);bytes_received += image_size;if(!image_data.isNull()){ui->textEditStatus->append("接收文件成功");//判断转过来的指令,对图像进行处理switch (mask){case 9:{QByteArray out;//使用opencv处理图像cv::Mat cv_src = QImage2cvMat(image_data);cv::Mat cv_dst;edgeDetection(cv_src,cv_dst);QImage qt_image = cvMat2QImage(cv_dst);//处理完成之后返回给客户端transformImage(qt_image,out,9);//返回给之前发送信息的客户端tcpClient.at(i)->write(out);ui->textEditStatus->append("处理并返回图像成功!");mask = 0;break;}case 10:{QByteArray out;//使用opencv处理图像cv::Mat cv_src = QImage2cvMat(image_data);cv::Mat cv_dst;cv::cvtColor(cv_src,cv_dst,cv::COLOR_BGR2GRAY);QImage qt_image = cvMat2QImage(cv_dst);//处理完成之后返回给客户端transformImage(qt_image,out,10);//返回给之前发送信息的客户端tcpClient.at(i)->write(out);ui->textEditStatus->append("处理并返回图像成功!");mask = 0;break;}}}if(bytes_received == total_bytes){total_bytes = 0;bytes_received = 0;image_size = 0;}}}}}void Server::edgeDetection(cv::Mat &cv_src, cv::Mat &cv_dst){cv::Mat cv_gray,cv_edge;cv::cvtColor(cv_src,cv_gray,cv::COLOR_BGR2GRAY);cv::blur(cv_gray, cv_gray, cv::Size(3, 3));//调用Canny算子cv::Canny(cv_edge, cv_dst, 10, 30, 3);}//Base64转QImageQImage Server::base64ToQImage(const QString &data){QByteArray imageData = QByteArray::fromBase64(data.toLatin1());QImage image;image.loadFromData(imageData);return image;}//QTextEdit显示图像void Server::insertImage(QTextEdit *ui_text_edit, QImage &image){QUrl Uri;QTextDocument * textDocument = ui_text_edit->document();textDocument->addResource( QTextDocument::ImageResource, Uri, QVariant ( image ) );QTextCursor cursor = ui_text_edit->textCursor();QTextImageFormat imageFormat;imageFormat.setWidth( image.width() );imageFormat.setHeight( image.height() );cursor.insertImage(imageFormat);}// QImage转换成cv::Matcv::Mat Server::QImage2cvMat(QImage &image){cv::Mat mat;switch (image.format()){case QImage::Format_ARGB32:case QImage::Format_RGB32:case QImage::Format_ARGB32_Premultiplied:mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());break;case QImage::Format_RGB888:mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);break;case QImage::Format_Indexed8:mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());break;}return mat;}// cv::Mat转换成QImageQImage Server::cvMat2QImage(const cv::Mat& mat){if (mat.type() == CV_8UC1)// 单通道{QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);image.setColorCount(256); // 灰度级数256for (int i = 0; i < 256; i++){image.setColor(i, qRgb(i, i, i));}uchar *pSrc = mat.data;// 复制mat数据for (int row = 0; row < mat.rows; row++){uchar *pDest = image.scanLine(row);memcpy(pDest, pSrc, mat.cols);pSrc += mat.step;}return image;}else if (mat.type() == CV_8UC3) // 3通道{const uchar *pSrc = (const uchar*)mat.data;// 复制像素QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888); // R, G, B 对应 0,1,2return image.rgbSwapped(); // rgbSwapped是为了显示效果色彩好一些。}else if (mat.type() == CV_8UC4) // 4通道{const uchar *pSrc = (const uchar*)mat.data;// 复制像素QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32); // B,G,R,A 对应 0,1,2,3return image.copy();}else{return QImage();}}//转换封装图像数据void Server::transformImage(QImage &image, QByteArray &block,int mask){int total = 0;QDataStream sendOut(&block, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_5_7);//获得图片数据QString imageData = QImageToBase64(image);// 保留总大小信息空间、图像大小信息空间,然后输入图像信息sendOut << int(0) << int(0) << int(0) << imageData;// 这里的总大小是总大小信息、图像大小信息和实际图像信息的总和total += block.size();sendOut.device()->seek(0);// 返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间sendOut << mask << total << int((block.size() - sizeof(int)*3));}QByteArray Server::QImageToBase64(const QImage &image){QByteArray imageData;//开缓冲区QBuffer buffer(&imageData);//存入缓冲区image.save(&buffer, "jpg");//转成Base64imageData = imageData.toBase64();return imageData;}//打开图像void Server::on_buttonOpenImage_clicked(){file_name = QFileDialog::getOpenFileName(this);if (!file_name.isEmpty()){image = QImage(file_name);if(image.isNull()){ui->textEditStatus->append("打开图像失败!");return;}else{insertImage(ui->textEditInput,image);ui->textEditStatus->append("打开图像成功!");ui->buttonSendMessage->setEnabled(true);}}else{ui->textEditStatus->append("打开路径失败,请确定是否是图像路径!");return;}}void Server::on_buttonSendMessage_clicked(){transformImage(image_data,out_block,10);//如果选择全部发送信息if(ui->comboBoxIP->currentIndex() == 0){for(int i = 0; i < tcpClient.length(); i++){tcpClient.at(i)->write(out_block);ui->textEditStatus->append("信息发送成功!");ui->textEditInput->clear();}}//指定接收的客户端/*else{//得到选择的IP地址QString client_IP = ui->comboBoxIP->currentText().split(":").at(0);//得到端口int client_port = ui->comboBoxIP->currentText().split(":").at(1).toInt();//遍历连接到的客户端for(int i = 0; i < tcpClient.length(); i++){if(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]==client_IP\&& tcpClient[i]->peerPort()==client_port){tcpClient.at(i)->write(input_data.toLatin1());ui->textEditStatus->append("发送信息到:"+client_IP+"成功!");//ui->textEditInput->clear();input_data.clear();return; //ip:port唯一,无需继续检索}}}*/}

3.运行效果

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。