QT6.0以上版本實現實時圖像傳輸

目錄

    • 服務端
      • 開啟攝像頭,捕獲存儲圖片
      • TCP圖像傳輸
      • 延時函數
    • 客戶端
      • 建立連接
      • 接收數據和處理
      • 緩沖區接收的一些想法

QT借助tcp實現圖像傳輸,達到類似實時監控的目的。
QT到6.0以上后貌似原來的5.0的一些圖像的捕獲的函數都無法使用了,網上好像也沒有人給出相關實現代碼,查看qt文檔找到的方法。
分為兩個部分,一個為客戶端,負責數據的接收和展示,服務端負責數據的發送。

服務端

1、開啟攝像頭,捕獲圖片到本地
2、開啟服務,傳輸圖片

開啟攝像頭,捕獲存儲圖片

//頭文件定義的類私有變量     
QList<QCameraDevice> cameras;
QCamera* camera;//攝像頭設備
QImageCapture* imageCapture;
//頭文件定義的類私有變量 
void MainWindow::camera_getimg()
{cameras = QMediaDevices::videoInputs();//獲取可用攝像頭設備列表for (const QCameraDevice &cameraDevice : cameras){qDebug() << cameraDevice.description();//攝像頭的設備信息ui->history->append(cameraDevice.description()+" init success.");//          ui->Camerlist->addItem(cameraDevice.description());}QMediaCaptureSession *captureSession = new QMediaCaptureSession;camera = new QCamera(cameras.at(0));//cameras.at(0)是默認攝像頭,也可以通過其他方式選擇攝像頭qDebug() << camera->cameraDevice().description();//攝像頭的設備信息,名字//ui->history->append(camera->cameraDevice().description()+" using...");captureSession->setCamera(camera);//use the first one//    captureSession->setVideoOutput(show);//show ui 有專門用來設置顯示ui的imageCapture = new QImageCapture(this);imageCapture->setFileFormat(QImageCapture::JPEG);captureSession->setImageCapture(imageCapture);imageCapture->setQuality(QImageCapture::NormalQuality);//質量選擇imageCapture->setResolution(240,180);//設置圖像尺寸imageCapture->captureToFile("D:/qt_rec.jpg");//捕獲一次圖像并存儲的路徑   camera->start();//啟動攝像頭
}

由于是實時獲取圖像并傳輸到客戶端,客戶端接收然后展示,需要配置定時器,過一段時間就捕獲一次圖片并發送,相關代碼在tcp中實現。

TCP圖像傳輸

//開啟服務unsigned short port  = ui->port->text().toUShort();//獲取portQString  ip_t = ui->ip->text();//獲取ipbool sta = my_s->listen(QHostAddress(ip_t),port);//創建服務qDebug()<<my_s->errorString();if(sta){ser_sta = true;ui->history->append("server open success.");ui->start_bt->setText("關閉服務");// disable the button of start}else{ui->history->append(my_s->errorString());}

發送處理

connect(my_s,&QTcpServer::newConnection,this,[=](){// 自定義匿名的槽函數,用于獲取連接的套接字對象m_tcp = my_s->nextPendingConnection();//m_status->setPixmap(QPixmap(":/img/status_1.png").scaled(20,20));//scaled 一個縮放函數,等比例//檢測是否可以接收數據,也是信號量ui->history->append("a new client connected");connect(m_tcp,&QTcpSocket::readyRead,this,[=](){qDebug() << "cnt_sta: "<<cnt_sta;//read and show the tcp client's data.QByteArray data = m_tcp->readAll();//   m_tcp->write(data);ui->history->append("client: " + data);});connect(m_tcp,&QTcpSocket::disconnected,this,[=](){//disconnected ,set icon.m_status->setPixmap(QPixmap(":/img/status_0.png").scaled(20,20));//scaled 一個縮放函數,等比例m_tcp->close();
//            deletem_tcp->deleteLater();//釋放對象,其實最后m_s釋放的時候,他也會釋放,這里手動釋放,也可以使用delete。});//img//設置100ms 的定時器觸發信號
//        uchar cout = 0;QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, [=](){imageCapture->captureToFile("D:/qt_rec.jpg");//捕獲并存儲一幀圖像cout++;QFile file("D:/qt_rec.jpg");QByteArray data;bool a= file.open(QIODevice::ReadOnly);if(a){data=file.readAll();file.close();}qint64 ssize= data.size();qDebug() <<"jpg size :"<<ssize;QString str_len = "img"+QString::number(ssize)+'\n';m_tcp->write(str_len.toUtf8());//發送圖像大小信息//延時,防止過快的發送信號到達客戶端,客戶端一次性讀取了長度信息和圖像數據Delay_MSec(10);;//延時10msqint32  len=0;if(m_tcp->isValid())//發送圖像數據  下面循環可以直接用len = m_tcp->write(data);替代while(ssize){len = m_tcp->write(data);qDebug() << len;ssize -= len;if(ssize<=0) break;}if(cout==3){cout = 0;//做另外的事情}});timer->start(100);//100ms一次});

延時函數

不阻塞延時

void MainWindow::Delay_MSec(unsigned int msec)
{QEventLoop loop;//定義一個新的事件循環QTimer::singleShot(msec, &loop, SLOT(quit()));//創建一個單次定時器,msec毫秒后執行槽函數,槽函數為循環的退出函數loop.exec();//事件循環開始執行,程序會卡在這里,直到定時時間到,循環退出
}

客戶端

接收圖片,存儲,或者直接展示。
先存儲后展示。

建立連接

    unsigned short port  = ui->port->text().toUShort();QString  ip_t = ui->ip->text();m_tcp->connectToHost(QHostAddress(ip_t),port);qDebug()<<"啟動連接";qDebug()<<m_tcp->errorString();

接收數據和處理

bool tcp_sig =false;//0表示第一次讀,1表示為接下來接收圖片connect(m_tcp,&QTcpSocket::connected,this,[=](){qDebug()<<"連接成功";// 自定義匿名的槽函數,用于獲取連接的套接字對象//檢測是否可以接收數據,也是信號量});connect(m_tcp,&QTcpSocket::errorOccurred,this,[=](){qDebug()<<"連接錯誤";qDebug()<<m_tcp->error();//輸出的錯誤信息更完整});connect(m_tcp,&QTcpSocket::readyRead,this,[=](){//read and show the tcp client's data.//qint32 len,allsize=0,nowsize = 0;if(tcp_sig==0){QByteArray rdata = m_tcp->readLine(1024);
//            qDebug()<<"recive some data len of"<< rdata.size();
//            qDebug()<< rdata;
//第一次讀取區分數據類型,是圖片還是其他數據if(rdata.startsWith("img")){tcp_sig = 1;//是圖片,修改標志rdata.erase(rdata.cbegin(),rdata.cbegin()+3);//去除前向標志rdata.removeLast();//去除回車allsize = rdata.toUInt();qDebug()<< "rec imgsize = "<<allsize;}else if(rdata.startsWith("data")){//QString 其他數據處理rdata.erase(rdata.cbegin(),rdata.cbegin()+4);qDebug()<<" png err";}}else if(tcp_sig == 1){//讀取圖片Delay_MSec(10);//延時一定需要,根據具體情況進行設定大小,這個延時的目的是等待緩沖區接收完發送的數據while(1){QByteArray img_data = m_tcp->readAll();len  = img_data.size();qDebug()<<"img len size" << img_data.size() ;nowsize +=  len;qDebug()<<nowsize;
//                imgarr.append(img_data);if(nowsize>=allsize){QFile file("G:/qt_img/22.jpg");file.open(QIODevice::WriteOnly| QIODevice::Truncate);//QIODevice::Truncate這個必須要len  = file.write(img_data);
//                    img_data.clear();nowsize = 0;qDebug()<< "img loacal size = " <<len ;file.close();QImage image("G:/qt_img/22.jpg");if(image.isNull()){qDebug()<<"test and png err";}else{QPixmap pic=QPixmap::fromImage(image);ui->img_view->setPixmap(pic.scaled(ui->img_view->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));//有更高效的方法,此外還可以不需要先存儲,直接將接收的數據轉為圖片進行顯示}}if(len==0) break;}//QByteArray da = m_tcp->readAll();//本來是想出現錯誤,讀取掉錯亂數據,經過推測和測試沒有作用tcp_sig = 0;nowsize = 0;}else{QByteArray da = m_tcp->readAll();//讀取掉錯亂數據}});

圖片的接收和展示還可以進行簡化,這是一個實現方式。整體上比較簡單吧

其實傳輸過程中的主要問題就是圖片接收的時候,有時候會出現失幀,和讀取不全,圖片只展示一半的問題,顯然是數據沒有讀取完整,或者發送不完整。想通過圖片大小的方式來確保讀取完整的圖片。
通過調試顯示,發送是可以直接完全一次性發送的
接收有時候會出問題,一下多一下少。
而且通過循環的方式來不斷讀取緩沖區直到那么大小的數據的想法是錯誤的。這應該和QTTCP緩沖區接收的實現方式有問題,涉及到信號量readyRead,目前來看這個信號量是在服務端發送出數據,客戶端緩沖區會接收數據,然后就會發送出這個信號量,但是他是接收到數據就發送了,也就是說不保證緩沖區已經完全接收完數據了。你此時通過信號量觸發進行讀取緩沖區接收的數據可能是不完整的,如果圖片過大,而且存在網絡延遲的情況下。這也是在接收的函數中要進行延時的原因,這個才算是解決問題的關鍵,非常重要

緩沖區接收的一些想法

此外,關于緩沖區接收,他似乎分為多個通道,看到文檔中有指定通道進行讀取數據, 但是我沒有深入研究,不知道是怎么使用和實現的。
目前測出的情況是,當接收到readyRead,并使用read之類的讀取函數時,在這個讀取的槽函數中,你所讀取的緩沖區似乎就是固定的了,相當于是某個時刻固定的。比如傳來50k的數據,主緩沖區接收到了20k,此時發送出了readyRead信號,并觸發了槽函數,這個過程假設又接收了10k數據(并行),但是還有20k數據沒有接收(也有可能這個緩沖區是動態變化的,一開始20k,發現不夠用,要動態擴展所以耗時更多),此時調用read之類的函數,相當于給你一個臨時變量固定的一個帶有30k數據的次緩沖區讓你讀取,當你將這個緩沖區讀取完的時候,你就讀不到數據了,應為次緩沖區數據已經被讀取完了,如果此時你設立一個循環,如果讀取的數據沒有達到50k,就繼續讀,但是所讀取的都是次緩沖區,這個是已經空了的,那么你就會進入死循環,一直無法退出。本人有幸進入過,很是掉頭發。
主緩沖區和次緩沖區的存在首先是我自己猜想的,另外幫助理解是主緩沖區相當于定義的全局變量a,而次緩沖區可以理解為某個函數定義的和全局變量名字相同的局部變量a,而在這個函數中,訪問變量a時是訪問局部變量a,而不是全局變量a。主緩沖區就是全局變量a,次緩沖區是局部變量a。
所以說數據過大,或者延時高的情況,readyRead觸發的讀取槽函數時,需要先進行延時的處理,因為你一定調用了read函數,不管是read()、readAll()等,只要調用了read之類的函數,就會固定化次緩沖區的內容。為了確保接收完全,需要進行延遲。當然一次readyRead的觸發,也意味著一次數據的傳輸到來。
觀察過測試輸出結果,如果你一開始讀取30k,并調用了read函數,也就是主緩沖區還剩下20k數據,此時,又發送來10k數據,觸發readyRead,在槽函數中調用readAll,這樣可以讀取到30k的數據,原來的20k加上新傳輸的10k。這也再次證明了前面設想的主次緩沖區的想法。至少存在這么一個機制。

以上是經驗之談,都是測試出來的,沒有去查其具體的機制,如有什么錯誤,歡迎大佬指證,大家要是有什么其他問題,也歡迎評論區留言討論。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21856.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21856.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21856.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

KAN(Kolmogorov-Arnold Network)的理解 3

系列文章目錄 第一部分 KAN的理解——數學背景 第二部分 KAN的理解——網絡結構 第三部分 KAN的實踐——第一個例程 文章目錄 系列文章目錄前言KAN 的第一個例程 get started 前言 這里記錄我對于KAN的探索過程&#xff0c;每次會嘗試理解解釋一部分問題。歡迎大家和我一起討…

百度/迅雷/夸克,網盤免費加速,已破!

哈嘍&#xff0c;各位小伙伴們好&#xff0c;我是給大家帶來各類黑科技與前沿資訊的小武。 之前給大家安利了百度網盤及迅雷的加速方法&#xff0c;詳細方法及獲取參考之前文章&#xff1a; 剛剛&#xff01;度盤、某雷已破&#xff01;速度50M/s&#xff01; 本次主要介紹夸…

Python sorted 用法:深入解析排序函數的奧秘

Python sorted 用法&#xff1a;深入解析排序函數的奧秘 在Python編程中&#xff0c;sorted函數是一個強大的工具&#xff0c;用于對可迭代對象進行排序。然而&#xff0c;它的用法和功能遠不止表面看起來那么簡單。本文將深入剖析sorted函數的四個方面、五個方面、六個方面和…

simulink基礎學習筆記

寫在前面 這個筆記是看B站UP 快樂的宇航boy 所出的simulink基礎教程系列視頻過程中記下來的&#xff0c;寫的很粗糙不完整&#xff0c;也不會補。視頻教程很細跟著做就行。 lesson1-7節的筆記up有&#xff0c;可以加up的群&#xff0c;里面大佬挺活躍的。 lesson8 for循環 For …

【C++初階學習】第十二彈——stack和queue的介紹和使用

C語言棧&#xff1a;數據結構——棧(C語言版)-CSDN博客 C語言隊列&#xff1a;數據結構——隊列&#xff08;C語言版&#xff09;-CSDN博客 前言&#xff1a; 在之前學習C語言的時候&#xff0c;我們已經學習過棧與隊列&#xff0c;并學習過如何使用C語言來實現棧與隊列&…

Python | 平均績點

字符串的概念和特點 字符串既可以使用單引號&#xff0c;也可以使用雙引號""來創建 可以使用運算符來拼接字符串&#xff0c;并返回字符串拼接后的結果。 first_name "Tom" last_name "Jerry" full_name first_name " " &quo…

OCR圖片轉Excel表格:沒結構化的弊端

隨著OCR技術的不斷發展&#xff0c;將表格圖片轉為excel已不再是難題&#xff0c;但是&#xff0c;目前市面上的程序還大多處于僅能將圖片表格轉為普通的excel格式階段&#xff0c;而不能將其結構化&#xff0c;這樣就會產生許多的弊端&#xff0c;具體弊端如下&#xff1a; &l…

數據容器的通用操作、字符串大小比較 總結完畢!

1.數據容器的通用操作 1&#xff09;五類數據容器是否都支持while循環/for循環 五類數據容器都支持for循環遍歷 列表、元組、字符串都支持while循環&#xff0c;集合、字典不支持&#xff08;無法下標索引&#xff09; 盡管遍歷的形式不同&#xff0c;但都支持遍歷操作 2&a…

辦公軟件 Office 安裝教程(親測有效)

Office 現已更名為 Microsoft 365。習慣還是稱作 Office。 1、Office 套裝下載 Windows 的樣子 這里下載的是最新版本的 O365ProPlus 安裝完成后&#xff0c;點擊關閉&#xff08;請先不要打開&#xff09;。 Mac 的樣子 這里下載的是Office for Mac 2019&#xff08;更多版…

QNAP的jellyfin無法使用TMDB刮削的問題

解決方法很簡單&#xff0c;修改QNAP的HOSTS文件 使用DNS Checker工具查詢對應IP&#xff1a; image.tmdb.org api.themoviedb.org www.themoviedb.org 打開hosts文件&#xff0c;添加下列內容&#xff0c;wq保存 [~] # vi /etc/hosts 169.150.249.166 image.tmdb.org 103.2…

速遞FineWeb:一個擁有無限潛力的15T Tokens的開源數據集

大模型技術論文不斷&#xff0c;每個月總會新增上千篇。本專欄精選論文重點解讀&#xff0c;主題還是圍繞著行業實踐和工程量產。若在某個環節出現卡點&#xff0c;可以回到大模型必備腔調或者LLM背后的基礎模型新閱讀。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;則提…

【TB作品】MSP430F149單片機,6通道ADC,串口發送

功能 用6個引腳采集ADC&#xff0c;串口發送到電腦 部分程序 /** Otherwise, the compiler removes it* because it is not used for anything.*/ int kaiguanliang[4]; /* 四個開關量 */unsigned char adok_ifg 1; …

內核宕機自救

【問題】在測試內核級防篡改時&#xff0c;偶爾會遇到內核宕機的問題 【結論】進入緊急救援模式&#xff0c;將服務進程文件的start注釋掉&#xff0c;即可 在Linux系統啟動時&#xff0c;內核啟動順序選擇界面&#xff0c;進入系統歡迎界面按上下左右鍵進入GRUB界面&#xff…

歐佩克+同意集體性減產延長,油價能否穩住?

KlipC報道&#xff1a;歐佩克組織同意將延長目前部分減產協議至2025年&#xff0c;以支撐油價。主要成員國把2023年11月宣布的日均220萬桶的自愿減產措施延長至今年9月底&#xff0c;將在10月份根據市場情況開始縮減自愿減產規模。 高盛分析師表示&#xff0c;“我們認為這次歐…

python常見數據分析函數

apply DataFrame.apply(func, axis0, broadcastFalse, rawFalse, reduceNone, args(), **kwds) 第一個參數是函數 可以在Series或DataFrame上執行一個函數 支持對行、列或單個值進行處理 import numpy as np import pandas as pdf lambda x: x.max()-x.min()df pd.DataFrame(…

高端、大氣、很牛B的免費wordpress模板主題

這是一款專為WordPress打造的極簡主義風格主題&#xff0c;以白色和黑色為主色調&#xff0c;搭配紅色點綴&#xff0c;營造出一種簡潔、專業且具有視覺沖擊力的效果。 該主題的設計理念是“簡單即美”&#xff0c;旨在幫助用戶快速搭建一個美觀、易用的網站。它提供了豐富的自…

動態sql set標簽 , trim標簽

set標簽 來看例子 set標案解決了逗號問題(當if條件不滿足時,逗號無處安放的問題),我認為set標簽可以識別這個問題,并自動忽略這個問題 <update id"update">update employee<set><if test"name!null">name#{name},</if><if te…

使用 Python 處理 Excel 表格數據的實用技巧

簡介 在日常工作中&#xff0c;處理 Excel 表格數據是一項常見的任務。Python 提供了豐富的庫和工具&#xff0c;能夠幫助我們高效地處理 Excel 數據。本篇博客將介紹如何使用 openpyxl 庫來讀取 Excel 表格并進行數據處理&#xff0c;同時展示一個實際案例&#xff0c;說明如…

3079. 求出加密整數的和

給你一個整數數組 nums &#xff0c;數組中的元素都是 正 整數。定義一個加密函數 encrypt &#xff0c;encrypt(x) 將一個整數 x 中 每一個 數位都用 x 中的 最大 數位替換。比方說 encrypt(523) 555 且 encrypt(213) 333 。 請你返回數組中所有元素加密后的 和 。 示例 1&…

HTML基本元素包含HTML表單驗證

可將以下代碼復制另存為一個HTML文件瀏覽器打開自己去看看實際使用效果 <!DOCTYPE html> <html> <head> <meta charset"utf-8"><title>測試</title> </head> <body> <h1>很多事</h1> <h1><b&…