1、可以让pc通过udp与FPGA之间双向传输视频/图片。
2、udp传图的数据不经过压缩按:R(8bit)、G(8bit)、B(8bit)一个像素点一个像素点传输。
3、每帧图片得有帧标记(类似vga的hs vs de)。
错误1:opencv编译中ffmpeg插件问题。
videocapture无法打开视频(时间最长,卡了4、5天)
错误2:版本问题(opencv版本和ffmpeg下载的版本不不对应会出现)
Qt版本:mingw730 64bit opencv:4.1.0 64bit
1、实现了udp通讯的搭建
实现了opencv提取图像像素矩阵数据
实现了像素数据到udp数据类型的转换
代码:
2、测试图片:(画图软件绘制的12x12的图)
3、Mat类输出矩阵显示:
4、Mat转换为数组输出:
代码:(待补充)
5、发送较大的图片文件测试
上面测试的小数据量的,但超过一个udp包(65535byte)就会出错
AbstractSocket::SocketError
6、分包发送。图像数据分多次发送。
代码:(待补充)
7、数据包的设置
图片和视频都能发送了。
需要用于区分图片数据的开头结尾。(设置安排如下)
Head+每包数据:
Head : 帧标志+图像长度
(例:一帧数据的第一个包,图像行长为12)
01 00 0C (3byte)
(当前帧第二包及之后)
00 00 0C (3byte)
第1byte: 一帧第一个包的为1,其他包为0。
后2byte: 为图像的行长。
Data : 图像数据(len=包长)
数据格式:第一个像素点R(8bit)+G(8bit)+B(8bit)
代码:(待补充)
注意:frame.cols为int型,要转换
且 int存储方式为高位在后低位在前!
代码:(待补充)
目标:数据接收并转为图片,并窗口显示
涉及: 多包数据整合
需要利用head标记区分图片数据接收开始结束。
Data与head分离
数据类型的转换
1、测试1:单包发送情况
发送数据:(用的上面12x12的图的数据)
结果可行!
QT的debug输出:
效果展示:
2、一帧数据分多包发送的情况
用flag判断一幅图片开始
网络助手手发数据测试:
基本功能完成。
在实际FPGA上板测试后,又添加了一些功能
①保存接收到的图片(save文件里)
②程序运行日志添加(save文件里log.txt),可用于检查软件运行日志(检查错误)
③ 两个图片显示窗口需要清空按钮。
④添加本机的接收端口设置(不设置为固定端口)。
⑤应为有接收丢包的情况,尝试了多线程处理(实现把发送放到了子线程处理)
最终效果:
1、目标功能实现了,
在测试时,使用自发自收时,大图接收时还是有可能丢数据,发送不丢包。(wireshark显示正常)
可能原因:收发数据要分不同线程?(没有专门学过qt c++,不了解)
#include "widget.h"
#include "ui_widget.h"
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QString title ="FPGA UDP传图 本机端口:"+ ui->rxport->text();
setWindowTitle(title);
udpsocket=new QUdpSocket(this);
udpsocket->abort();
portStr = ui->rxport->text().toInt();
udpsocket->bind(QHostAddress::Any,portStr);//绑定当前网卡所有的ip,端口
timer=new QTimer(this);
connect(ui->ptn_quit,SIGNAL(clicked(bool)),this,SLOT(quit())); //退出按钮
connect(udpsocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
connect(ui->ptn_openfile,SIGNAL(clicked(bool)),this,SLOT(openfile()) );// 打开文件
connect(ui->ptn_send,SIGNAL(clicked(bool)),this,SLOT(ptn_send_clicked())); //发送按钮按下
connect(timer,SIGNAL(timeout()),this,SLOT(PlayAndSendData()));//按下按钮后
//当收到数据时readyRead()有效,进而触发跳转到UDPReceive()函数;
connect(udpsocket,SIGNAL(readyRead()),this,SLOT(UDPReceive()));
//新建文件夹保存接收到的图片及log
path=QDir::currentPath();
path=path.replace("/","\\")+"/save";
createFile(path,"log.txt");
}
Widget::~Widget()
{
delete ui;
}
void Widget::openfile()
{
QString path=QDir::currentPath();
path.replace("/","\\");
filename = QFileDialog::getOpenFileName(this,"选择需要播放的视频",path,
"allfiles(*.*);;"
"MP4(*.mp4);;"
"AVI(*.avi)");
}
// ========================== udp发送 ===========================================================
void Widget::SendData() // 每帧执行一次
{
cvtColor(frame,frame,CV_BGR2RGB);
//-------- 分包处理 -------
QByteArray sendbyte;
// 定义包头
QByteArray head;
head.append(1);//一帧第一包的标志
int l=frame.cols; //int的存储是低位在前!! frame.cols是int32(而QByteArray用char16?)
int h=frame.rows;
head.append(dec2hex2(l),2);//写入一行长度(.append为追加行长数据)(char型,直接添加丢失精度)(这里int转hex且取2byte)
head.append(dec2hex2(h),2);
qDebug()<<"第 "< sendLen )
{
// 填充要发送的数据
sendbyte.append (head);//加上每包数据的3byte
sendbyte.append (byte.mid(sendLen,ui->lineEdit_bao->text().toUInt())); // !!!后设置分包大小:frame.cols*3
//发送
int len = udpsocket->writeDatagram(sendbyte,sendbyte.size(),QHostAddress(ip),port);
sendLen += len-head.size();//并减去head的长度
qDebug() <<"本次发送出数据长度为 this time:" << len-head.size() << "已发送数据长度为 Has been sent:" << sendLen << " 此帧像素数据总长度为 The total length:" << ByteLen ;
sendbyte.clear();
udpsocket->flush();
head[0]=0x00;//一帧非第一包标志
}
byte.clear();
//QThread::msleep(delay);
}
// ===================================================================
void Widget::displayError(QAbstractSocket::SocketError)
{
qDebug()<<"Failed to open video";
QString string=udpsocket->errorString();
qDebug()<> 24 & 0xff);
// array[1] = (uchar)(a >> 16 & 0xff);
array[0] = (uchar)(a>>8 & 0xff);
array[1] = (uchar)(a & 0xff);
return array;
}
//============================================ UDP接收图片 =============================================================================
void Widget::UDPReceive() //测试:广播(255.255.255.255)发
{
qDebug()<<"start udp rx";
QByteArray receivebyte;//本次data
//QByteArray datareg;//累积的data
// 让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据
receivebyte.resize(udpsocket->pendingDatagramSize());
// 接收数据报,存到datagram
qint64 len = udpsocket->readDatagram(receivebyte.data(), receivebyte.size());
if(len>0)//如果>0就接收处理
{
// -----------------------------
// 解析出每次的head
int8_t flag =(receivebyte.at(0));
qDebug()<<"帧标记 flag:"<(receivebyte.data())+5; //+5:跳过head
//如果数据只有一帧,显示
qDebug()<<"(第1包)receivebyte.size:"<label_rx->setPixmap(QPixmap::fromImage(image_rx));
ui->label_rx->resize(image_rx.size());
// 图像保存
imageSave(image_rx );
//清空 再重新 累计data
datareg.remove(0,datareg.size());//head.resize(0);???
}
else//如果不够一帧,累计
{
datareg.append(receivebyte.right(receivebyte.size()-5));//只存图像到datareg
}
}
else//不是帧头,累积datar
{
datareg.append(receivebyte.right(receivebyte.size()-5));//只存图像到datareg
//qDebug()<<"本包数据:"<(datareg.data()); //无head
//如果是最后一包数据,显示并重置
qDebug()<<"本次接受数据长度 Length of data accepted this time:"<exists(fileName))
{
qDebug()<<"文件存在 File exists";
return ;
}
//此时,路径下没有fileName文件,使用下面代码在当前路径下创建文件
tempFile->setFileName(fileName);
if(!tempFile->open(QIODevice::WriteOnly|QIODevice::Text))
{
qDebug()<<"Open fail";
}
tempFile->close();
//将程序当前路径设置为原来的路径
tempDir.setCurrent(currentDir);
qDebug()<abort();
QString title ="FPGA UDP传图 本机端口:"+ ui->rxport->text();
setWindowTitle(title);
portStr = ui->rxport->text().toInt();
udpsocket->bind(QHostAddress::Any,portStr);//绑定当前网卡所有的ip,端口
}
void Widget::on_pushButton_clicked()
{
ui->label_rx->clear();
}
void Widget::on_pushButton_2_clicked()
{
ui->label->clear();
}
https://download.csdn.net/download/Iloadingl/86248945