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服务器与客户端互发
- 下一篇: Yolov5身份证检测——C++ Ope