Qt网络编程——使用OpenCV与TCP搭建图像处理服务器
生活随笔
收集整理的这篇文章主要介绍了
Qt网络编程——使用OpenCV与TCP搭建图像处理服务器
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
前言
前面的博客有写过如果使用TCP搭建一个客户端与服务器,连接并互发信息,这里主是演示,如何把客户端的图像发往服务器,服务器得到图像后,按指令做不同的处理,并返回给客户端处理之后的结果,客户端只负责打开和发送图像,所有关于图像的图像的处理,比如灰度图像,人脸检测啊,都经过服务器处理之后返回给客户端。而服务器用来处理图像的是OpenCV库,
代码注释比较多,具体流程可以看源码,就应该了解。
代码
1.客户端
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 64 QByteArray 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
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转QImage QImage 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::Mat cv::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转换成QImage QImage 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.运行效果
总结
以上是生活随笔为你收集整理的Qt网络编程——使用OpenCV与TCP搭建图像处理服务器的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Qt网络编程——TCP服务器与客户端互发
- 下一篇: YOLOX——Win10下训练自定义VO