From a91f5f5b0476adead2adb2546ccf628ac47c71cb Mon Sep 17 00:00:00 2001 From: tangchao0503 <735056338@qq.com> Date: Mon, 29 Jan 2024 17:21:07 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=B7=BB=E5=8A=A0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8E=A7=E5=88=B6=E6=8E=A8=E6=B5=81=E5=8F=82?= =?UTF-8?q?=E6=95=B0=EF=BC=9B=202=E3=80=81=E8=A7=A3=E5=86=B3=E9=81=A5?= =?UTF-8?q?=E6=8E=A7=E5=99=A8=E8=A7=A3=E7=A0=81=E6=97=B6=E5=B8=A7=E5=BA=8F?= =?UTF-8?q?=E6=B7=B7=E4=B9=B1=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=88gop=5Fsiz?= =?UTF-8?q?e=20=3D=201=EF=BC=89=EF=BC=9B=203=E3=80=81=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Header_Files/configfile.h | 2 + Header_Files/rgbImage.h | 41 +-- Header_Files/ximeaimager.h | 38 ++- Source_Files/configfile.cpp | 58 +++++ Source_Files/rgbImage.cpp | 223 ++++------------- Source_Files/ximeaimager.cpp | 468 ++++++++++++++++++++++------------- 6 files changed, 445 insertions(+), 385 deletions(-) diff --git a/Header_Files/configfile.h b/Header_Files/configfile.h index 55061a2..3f9dc0c 100644 --- a/Header_Files/configfile.h +++ b/Header_Files/configfile.h @@ -42,6 +42,8 @@ public: bool getBufferPolicy(int &bufferPolicy); bool getAcqBufferSize(int &acqBufferSize); + bool getPushFlowParam(int &flowSwitch, int &rgbHeight, int &framerateVideo); + bool createConfigFile(); bool updateConfigFile(); diff --git a/Header_Files/rgbImage.h b/Header_Files/rgbImage.h index 4a53f21..2d9071f 100644 --- a/Header_Files/rgbImage.h +++ b/Header_Files/rgbImage.h @@ -24,28 +24,6 @@ extern "C" #include "libavdevice/avdevice.h" } -class Encode -{ -public: - Encode(); - void initffmpeg(int width, int height); - - FILE *fp; - AVCodecContext *avcodeccontext; - void savedata(AVFrame *frame); - void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, FILE *outfile); - AVPacket m_avpacket; - AVFrame *inpic; - AVFrame *outpic; - - bool isinit; - int index1; - -private: -}; - - - using namespace cv; class rgbImage :public QObject { @@ -55,7 +33,8 @@ public: rgbImage(QWidget* pParent = NULL); ~rgbImage(); - void SetRgbImageWidthAndHeight(int BandCount, int Sample, int FrameNumber); + void SetRgbImageWidthAndHeight(int BandCount, int Width, int height); + void SetRgbBandNumber(int redBandNumber, int greenBandNumber, int blueBandNumber); void FillRgbImage(unsigned short *datacube); void FillFocusGrayImage(unsigned short *datacube); void FillFocusGrayQImage(unsigned short * datacube); @@ -71,24 +50,16 @@ public: cv::Mat *m_matFocusGrayImage;//用于调焦时,显示一帧的灰度图 //cv::Mat m_matFocusGrayImage;//用于调焦时,显示一帧的灰度图 - CvVideoWriter *m_frame_writer; VideoWriter m_VideoWriter; // VideoWriter m_video("appsrc ! autovideoconvert ! filesink location=/media/nvme/delete/live.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(640, 480)); // VideoWriter video("test.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(640, 480));// - - - - //控制该填充rgb图像第几帧(行)数据 //以下两种情况需要重置为0:1)调用函数SetRgbImageWidthAndHeight;2)每次开始填充数据前 int m_iFrameCounter; - - int m_iFramerate;// - protected: private: @@ -99,15 +70,15 @@ private: void initffmpeg(); + int m_iRedBandNumber; + int m_iGreenBandNumber; + int m_iBlueBandNumber; + public slots: signals : void sendstr(QString str); void sendstr1(QString str); void refreslabelimg(QImage* img1); - - }; - - #endif //XIMEAAIRBORNESYSTEM_RGBIMAGE_H diff --git a/Header_Files/ximeaimager.h b/Header_Files/ximeaimager.h index 819e17d..5cd2c58 100644 --- a/Header_Files/ximeaimager.h +++ b/Header_Files/ximeaimager.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -125,6 +127,32 @@ public slots: signals: }; +class PushFlow : public QObject +{ +Q_OBJECT + +public: + PushFlow(); + void setParm(rgbImage * img, int width, int height, int framerateVideo); + void exitPushFlow(); + void setVedioFilePath(QString path); + +private: + QString m_QVedioFilePath; + bool isExitPushFlow; + + rgbImage * m_rgbImage; + + int m_iWidth; + int m_iHeight; + int m_iFramerateVideo; + +public slots: + void encodePushFlow(); + +signals: +}; + class XimeaImager : public QObject { Q_OBJECT @@ -148,6 +176,9 @@ public: int getWindowEndBand(); double geWavelengthAtBand(int x); + static int findClosestIndex(const std::vector& numbers, double target); + void getRgbBandNumber(int &redBandNumber, int &greenBandNumber, int &blueBandNumber); + void stopRecord(); int getFrameCounter(); void writeXiApiErrorCodes(QString filePath, int xiApiErrorCodes); @@ -155,8 +186,6 @@ public: int getMaxValueOfOneFrame(unsigned short * data, int numberOfPixel); int getImagerState() const; - - Encode ffmpegEncode; private: //0-61:ximea官方错误代码;99:发生的ximea官方错误代码,没有处理;100:未打开;101:打开;102:设置帧率;103:自动曝光;104:正在采集; int m_iImagerState; @@ -174,6 +203,10 @@ private: queue * m_qFrameCounter; MemoryPool * m_pool; + QThread * m_pushFlowThread; + PushFlow * m_pushFlow; + int m_iFlowSwitch; + QString m_baseFileName; QString m_ximeaTemperatureCSVPath; @@ -212,6 +245,7 @@ signals: void recordXimeaTemperatureSignal(QString); void startWriteDiskSignal(); + void startPushFlowSignal(); void autoExposeMaxValueOfOneFrame(int, double); void frameRateSignal(double); diff --git a/Source_Files/configfile.cpp b/Source_Files/configfile.cpp index 3f57ec7..2f76fad 100644 --- a/Source_Files/configfile.cpp +++ b/Source_Files/configfile.cpp @@ -166,6 +166,64 @@ bool Configfile::getEffectiveWindowRoi(int &width, int &offsetx) return true; } +bool Configfile::getPushFlowParam(int &flowSwitch, int &rgbHeight, int &framerateVideo) +{ + Setting& root = cfg.getRoot(); + + if (!root.exists("push_flow_param")) + { + // 配置项不存在,添加配置项 + Setting & push_flow_param = root.add("push_flow_param", Setting::TypeGroup); + + push_flow_param.add("flow_switch", Setting::TypeInt) = 0; + push_flow_param.add("rgb_height", Setting::TypeInt) = 720; + push_flow_param.add("framerate_video", Setting::TypeInt) = 5; + + // 保存修改后的配置到文件 + try + { + QList fileInfo = getFileInfo(QString::fromStdString(m_configfilePath)); + bool ret = createDir(fileInfo[0]); + + cfg.writeFile(m_configfilePath.c_str()); + std::cout << "Config item 'push_flow_param' added." << std::endl; + + flowSwitch = 0; + rgbHeight = 720; + framerateVideo = 5; + + return true; + } + catch (const libconfig::FileIOException &fioex) + { + std::cerr << "I/O error while writing file." << std::endl; + return false; + } + } + else + { + try + { + const Setting &push_flow_param = root["push_flow_param"]; + + if(!(push_flow_param.lookupValue("rgb_height", rgbHeight) + && push_flow_param.lookupValue("framerate_video", framerateVideo) + && push_flow_param.lookupValue("flow_switch", flowSwitch) + )) + { + return false; + } + } + catch(const SettingNotFoundException &nfex) + { + // Ignore. + return false; + } + + return true; + } +} + bool Configfile::getWindowOffsety_HeightOfSpectral(int &offsety, int &height, string spectralBinString) { const Setting& root = cfg.getRoot(); diff --git a/Source_Files/rgbImage.cpp b/Source_Files/rgbImage.cpp index 13dbdd9..b5bf7df 100644 --- a/Source_Files/rgbImage.cpp +++ b/Source_Files/rgbImage.cpp @@ -4,138 +4,16 @@ #include "../Header_Files/rgbImage.h" -Encode::Encode() -{ - fp= fopen("/media/nvme/delete/av.h264","wb"); - index1 = 0; -} - -void Encode::initffmpeg(int width, int height) -{ - const AVCodec *codec; - - int i, ret, x, y, got_output; - - std::cout<<"init ok"; - codec = avcodec_find_encoder(AV_CODEC_ID_H264); - if (!codec) - { - fprintf(stderr, "Codec not found\n"); - exit(1); - } - - // 根据编码器,创建相对应的编码器上下文 - avcodeccontext = avcodec_alloc_context3(codec); - if (!avcodeccontext) { - fprintf(stderr, "Could not allocate video codec context\n"); - exit(1); - } - avcodeccontext->bit_rate = 400000; - - avcodeccontext->width = width; - avcodeccontext->height = height; - /* frames per second */ - //时间基,每一秒25帧,每一刻度25分之1(时间基根据帧率而变化) - avcodeccontext->time_base = (AVRational){1, 25}; - //帧率 - avcodeccontext->framerate = (AVRational){25, 1}; - - /* emit one intra frame every ten frames - * check frame pict_type before passing frame - * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I - * then gop_size is ignored and the output of encoder - * will always be I frame irrespective to gop_size - */ - //多少帧产生一组关键帧 - avcodeccontext->gop_size = 10; - //b帧,参考帧 - avcodeccontext->max_b_frames = 1; - //编码的原始数据的YUV格式 - avcodeccontext->pix_fmt = AV_PIX_FMT_YUV420P; - - //如果编码器id 是 h264 - if (codec->id == AV_CODEC_ID_H264) - // preset表示采用一个预先设定好的h264参数集,级别是slow,slow表示压缩速度是慢的,慢的可以保证视频质量,用快的会降低视频质量 - av_opt_set(avcodeccontext->priv_data, "preset", "slow", 0); - - /* open it */ - //打开编码器 - if (avcodec_open2(avcodeccontext, codec, NULL) < 0) { - fprintf(stderr, "Could not open codec\n"); - exit(1); - } - // avcodeccontext=c; - std::cout<<"init ok"; - inpic = av_frame_alloc(); - outpic = av_frame_alloc(); - -//avpicture_fill sets all of the data pointers in the AVFrame structures -//to the right places in the data buffers. It does not copy the data so -//the QImage and out_buffer still need to live after calling these. - inpic->width=width; - inpic->height=height; - inpic->format=AV_PIX_FMT_ARGB; - inpic->linesize[0]=width; - outpic->width=width; - outpic->height=height; - outpic->format=AV_PIX_FMT_YUV420P; - outpic->linesize[0]=width; - isinit= true; -} - -void Encode::savedata(AVFrame *frame) -{ - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = NULL; // packet data will be allocated by the encoder - pkt.size = 0; - frame->pts = index1; - - AVCodecInternal *avci = avcodeccontext->internal; -// if (avci->draining) -// return AVERROR_EOF; -// if (avci->buffer_frame->data[0]) -// return AVERROR(EAGAIN); - encode(avcodeccontext,frame,&pkt,fp); - - av_packet_unref(&pkt); - index1++; -} - -void Encode::encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, FILE *outfile) -{ - int ret; - /* send the frame to the encoder */ - if (frame) - // printf("Send frame %3\"PRId64\"\n", frame->pts); - ret = avcodec_send_frame(enc_ctx, frame); - if (ret < 0) { - fprintf(stderr, "Error sending a frame for encoding\n"); - exit(1); - } - while (ret >= 0) { - ret = avcodec_receive_packet(enc_ctx, pkt); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) - return; - else if (ret < 0) { - fprintf(stderr, "Error during encoding\n"); - exit(1); - } - // printf("Write packet %3\"PRId64\" (size=%5d)\n", pkt->pts, pkt->size); - fwrite(pkt->data, 1, pkt->size, outfile); - QByteArray buf; - buf.append((char *)pkt->data,pkt->size); -// emit senddata(buf); - av_packet_unref(pkt); - } -} - rgbImage::rgbImage(QWidget* pParent) { m_QRgbImage = nullptr; m_matRgbImage = nullptr; m_matFocusGrayImage = nullptr; m_qimageFocusGrayImage = nullptr; + + m_iRedBandNumber = 0; + m_iGreenBandNumber = 0; + m_iBlueBandNumber = 0; } rgbImage::~rgbImage() @@ -143,8 +21,18 @@ rgbImage::~rgbImage() } +void rgbImage::SetRgbBandNumber(int redBandNumber, int greenBandNumber, int blueBandNumber) +{ + m_iRedBandNumber = redBandNumber; + m_iGreenBandNumber = greenBandNumber; + m_iBlueBandNumber = blueBandNumber; -void rgbImage::SetRgbImageWidthAndHeight(int BandCount, int Sample, int FrameNumber) +// std::cout<<"rgbImage::SetRgbBandNumber,红波段的波段号:"<< redBandNumber <size(); - m_VideoWriter.open(filename, codec, fps, Size(20, 1368), true); +// int codec = VideoWriter::fourcc('H', '2', '6', '4'); // select desired codec (must be available at runtime) +// double fps = 20.0;// framerate of the created video stream +// std::string filename = "appsrc ! autovideoconvert ! filesink location=/media/nvme/live.mp4";//https://blog.csdn.net/ancientapesman/article/details/117324638 +//// std::string filename = "/media/nvme/live.mp4"; +// auto ddddd=m_matRgbImage->size(); +// m_VideoWriter.open(filename, codec, fps, Size(20, 1368), true); // VideoWriter video("test.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(640, 480)); @@ -178,33 +66,26 @@ void rgbImage::SetRgbImageWidthAndHeight(int BandCount, int Sample, int FrameNum if (m_qimageFocusGrayImage == nullptr) { - m_qimageFocusGrayImage = new QImage(Sample, BandCount, QImage::Format_RGB32); + m_qimageFocusGrayImage = new QImage(Width, BandCount, QImage::Format_RGB32); } if (m_matFocusGrayImage == nullptr) { - m_matFocusGrayImage = new Mat(BandCount, Sample, CV_16U, Scalar(0)); + m_matFocusGrayImage = new Mat(BandCount, Width, CV_16U, Scalar(0)); - //cv::Mat matAdjustPreview = Mat::zeros(BandCount, Sample, CV_16U); + //cv::Mat matAdjustPreview = Mat::zeros(BandCount, Width, CV_16U); } - //cv::Mat matAdjustPreview = Mat::zeros(BandCount, Sample, CV_16U); + //cv::Mat matAdjustPreview = Mat::zeros(BandCount, Width, CV_16U); //m_matFocusGrayImage = matAdjustPreview; - - - - - - - - std::cout << "设置帧数:" << FrameNumber << std::endl; + std::cout << "高光谱rgb图像设置高度:" << height << std::endl; m_iFrameCounter = 0;//每次都重置为0 - m_iSampleNumber = Sample; + m_iSampleNumber = Width; m_iBandNumber = BandCount; - m_iFrameNumber = FrameNumber; + m_iFrameNumber = height; //std::cout << "rgb影像内存地址为:" << m_QRgbImage << std::endl; } @@ -216,13 +97,13 @@ void rgbImage::FillOnerowofRgbImage(cv::Mat * matRgbImage, int rowNumber, unsign // for (int j = 0; j < m_iSampleNumber; j++) // { // //取值:一帧影像中,从左到右的rgb像元值 -// r = *(datacube + 121 * m_iSampleNumber + j)*255/4096; -// g = *(datacube + 79 * m_iSampleNumber + j)*255/4096; -// b = *(datacube + 40 * m_iSampleNumber + j)*255/4096; +// r = *(datacube + m_iRedBandNumber * m_iSampleNumber + j)*255/4096; +// g = *(datacube + m_iGreenBandNumber * m_iSampleNumber + j)*255/4096; +// b = *(datacube + m_iBlueBandNumber * m_iSampleNumber + j)*255/4096; // -//// r = *(datacube + 121 * m_iSampleNumber + j); -//// g = *(datacube + 79 * m_iSampleNumber + j); -//// b = *(datacube + 40 * m_iSampleNumber + j); +//// r = *(datacube + m_iRedBandNumber * m_iSampleNumber + j); +//// g = *(datacube + m_iGreenBandNumber * m_iSampleNumber + j); +//// b = *(datacube + m_iBlueBandNumber * m_iSampleNumber + j); // // //将像元值赋值到cv::Mat中,操作像元值:https://zhuanlan.zhihu.com/p/51842288 // //int dataType = m_matRgbImage->type();//当数据类型为CV_16UC3时,返回18 @@ -257,10 +138,10 @@ void rgbImage::FillOnerowofRgbImage(cv::Mat * matRgbImage, int rowNumber, unsign for (int j = 0; j < m_iSampleNumber; j++) { - //取值:一帧影像中,从左到右的rgb像元值 - r = *(datacube + 121 * m_iSampleNumber + j)*255/4096; - g = *(datacube + 79 * m_iSampleNumber + j)*255/4096; - b = *(datacube + 40 * m_iSampleNumber + j)*255/4096; + //取值:一帧影像中,从左到右的rgb像元值,线性拉伸 + r = *(datacube + m_iRedBandNumber * m_iSampleNumber + j)*255/4096; + g = *(datacube + m_iGreenBandNumber * m_iSampleNumber + j)*255/4096; + b = *(datacube + m_iBlueBandNumber * m_iSampleNumber + j)*255/4096; *p_row0_b = b; *p_row0_g = g; @@ -320,25 +201,15 @@ QImage rgbImage::Mat2QImage(cv::Mat cvImg)//https://www.cnblogs.com/annt/p/ant00 void rgbImage::FillRgbImage(unsigned short *datacube) { - //通过行赋值将前m_iFrameNumber-1行向上移动一行,https://blog.csdn.net/u014686356/article/details/65937750 - //经tc验证:此行代码工作异常, -// m_matRgbImage->rowRange(0, m_matRgbImage->rows - 1).copyTo(m_matRgbImage->rowRange(1, m_matRgbImage->rows)); -// -// QString savePath_cv_3 = "/media/nvme/delete/" + QString::number(m_iFrameCounter) + "full0_cv.jpg"; -// cv::imwrite(savePath_cv_3.toStdString(), *m_matRgbImage); -// -// cv::Mat upperPart = m_matRgbImage->rowRange(0, m_matRgbImage->rows - 1); -// QString savePath_cv_ = "/media/nvme/delete/" + QString::number(m_iFrameCounter) + "upperPart_cv.jpg"; -// cv::imwrite(savePath_cv_.toStdString(), upperPart); -// -// // 将上半部分的数据复制到下一行 -// upperPart.copyTo(m_matRgbImage->rowRange(1, m_matRgbImage->rows));//????????????????????????????????????????? -// QString savePath_cv_2 = "/media/nvme/delete/" + QString::number(m_iFrameCounter) + "full_cv.jpg"; -// cv::imwrite(savePath_cv_2.toStdString(), *m_matRgbImage); + //从第二行开始,向下移动一行,https://blog.csdn.net/u014686356/article/details/65937750 +// m_matRgbImage->rowRange(0, m_matRgbImage->rows - 1).copyTo(m_matRgbImage->rowRange(1, m_matRgbImage->rows));//经tc验证:此行代码工作异常,为啥不加.clone()就异常?????? +// m_matRgbImage->rowRange(0, m_matRgbImage->rows - 1).clone().copyTo(m_matRgbImage->rowRange(1, m_matRgbImage->rows));//此方式,ximea帧率130hz,1min左右就出现漏帧 - for (int i = m_matRgbImage->rows - 2; i >= 0; --i) +// cv::Mat upperPart = m_matRgbImage->rowRange(0, m_matRgbImage->rows - 1).clone();//此方式,ximea帧率130hz,1min左右就出现漏帧 +// upperPart.copyTo(m_matRgbImage->rowRange(1, m_matRgbImage->rows)); + + for (int i = m_matRgbImage->rows - 2; i >= 0; --i)//此方式,ximea帧率130hz,4.5min左右出现漏帧 → 此方式效率最高 { -// std::cout << "大于:" << i << std::endl; m_matRgbImage->row(i).copyTo(m_matRgbImage->row(i+1)); } diff --git a/Source_Files/ximeaimager.cpp b/Source_Files/ximeaimager.cpp index f3daf73..903b849 100644 --- a/Source_Files/ximeaimager.cpp +++ b/Source_Files/ximeaimager.cpp @@ -47,6 +47,12 @@ XimeaImager::XimeaImager() writeData2DiskThread->start(QThread::HighestPriority); connect(this, SIGNAL(startWriteDiskSignal()), writeData2Disk, SLOT(write2Disk())); + m_pushFlowThread=new QThread(); + m_pushFlow = new PushFlow(); + m_pushFlow->moveToThread(m_pushFlowThread); + m_pushFlowThread->start(); + connect(this, SIGNAL(startPushFlowSignal()), m_pushFlow, SLOT(encodePushFlow())); + m_pool = new MemoryPool; q = new queue; m_qFrameCounter = new queue; @@ -100,8 +106,21 @@ void XimeaImager::openImger() ret = m_configfile.getEffectiveWindow(width, offsetx, height, offsety); if (ret) { + int rgbHeight; + int framerateVideo; + m_configfile.getPushFlowParam(m_iFlowSwitch, rgbHeight, framerateVideo); + std::cout <<"rgbHeight:" << rgbHeight << ", framerateVideo:" << framerateVideo << std::endl; + m_imager.setEffectiveWindow(offsetx, width, offsety, height); - m_rgbImage->SetRgbImageWidthAndHeight(height, width, 720); + m_rgbImage->SetRgbImageWidthAndHeight(height, width, rgbHeight); + m_pushFlow->setParm(m_rgbImage,width,rgbHeight,framerateVideo); + + int redBandNumber; + int greenBandNumber; + int blueBandNumber; + getRgbBandNumber(redBandNumber, greenBandNumber, blueBandNumber); + m_rgbImage->SetRgbBandNumber(redBandNumber, greenBandNumber, blueBandNumber); + std::cout<<"height:"<< height < wavelengths; + if (m_imager.getSpectralBin() == 1) + { + for (int i = getWindowStartBand(); i < getWindowEndBand(); i++) + { + wavelengths.push_back(geWavelengthAtBand(i)); + } + } + else if (m_imager.getSpectralBin() == 2) + { + for (int i = m_iOffsetyOfSpectralBin2; i < m_iOffsetyOfSpectralBin2 + m_iHeightOfSpectralBin2; i++) + { + if (i*2 + 1 > m_iOffsetyOfSpectralBin1 + m_iHeightOfSpectralBin1) + { + printf("XimeaImager::writeHdr 出现错误:窗口中,光谱 bin1 波段数小于 bin2 的 2 倍。\n"); + break; + } + wavelengths.push_back((geWavelengthAtBand(i*2) + geWavelengthAtBand(i*2 + 1)) / 2); + } + } + + //envi打开文件时的红绿蓝波长(nm) + int r_envi = 640; + int g_envi = 550; + int b_envi = 470; + redBandNumber = findClosestIndex(wavelengths, r_envi); + greenBandNumber = findClosestIndex(wavelengths, g_envi); + blueBandNumber = findClosestIndex(wavelengths, b_envi); + +// std::cout<<"红波段的波段号:"<< redBandNumber <& numbers, double target) +{ + if (numbers.empty()) { + // 处理空向量的情况 + return -1; + } + + double minDifference = std::abs(numbers[0] - target); + int closestIndex = 0; + + for (int i = 1; i < numbers.size(); ++i) + { + double currentDifference = std::abs(numbers[i] - target); + if (currentDifference < minDifference) + { + minDifference = currentDifference; + closestIndex = i; + } + } + + return closestIndex; +} + int XimeaImager::getMaxValueOfOneFrame(unsigned short * data, int numberOfPixel) { //排序 @@ -592,124 +670,13 @@ void XimeaImager::startRecord(double TimeDifferenceBetweensOSAndSbg,QString base FILE *hFile=fopen(imageFileName.toStdString().c_str(),"w+b"); double * imageBuffer = new double[number_WriteDisk]; - //tc+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // 创建输出视频的AVFormatContext - const char* outputVideoPath = "/media/nvme/delete/300tc.h264"; - int width = 1368; - int height = 720; - int framerateVideo = 10; - - AVFormatContext* formatContext = nullptr; - avformat_alloc_output_context2(&formatContext, nullptr, "mp4", outputVideoPath); - if (!formatContext) + QString vedioFileName=m_baseFileName+".h264"; + m_pushFlow->setVedioFilePath(vedioFileName); + if(m_iFlowSwitch == 1) { - qDebug() << "Error: Failed to allocate output context"; - return; + emit startPushFlowSignal(); } - // 查找H.264编码器 - const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); - if (!codec) - { - qDebug() << "Error: Codec not found"; - avformat_free_context(formatContext); - return; - } - - // 创建输出视频流 - AVStream* videoStream = avformat_new_stream(formatContext, codec); - if (!videoStream) - { - qDebug() << "Error: Failed to create video stream"; - avformat_free_context(formatContext); - return; - } - // 设置视频流的参数,例如分辨率、帧率等 - videoStream->codecpar->width = width; - videoStream->codecpar->height = height; - videoStream->codecpar->codec_id = AV_CODEC_ID_H264; // 设置为H.264编解码器 - videoStream->codecpar->format = AV_PIX_FMT_YUV420P; // 设置为YUV420P像素格式 - - // 配置视频流的参数 - AVCodecContext* codecContext = avcodec_alloc_context3(codec); - if (!codecContext) - { - qDebug() << "Error: Failed to allocate codec context"; - avformat_free_context(formatContext); - return; - } - - // 设置视频流的参数,例如分辨率、帧率等 - codecContext->width = width; - codecContext->height = height; - codecContext->time_base = {1, framerateVideo}; // 30 frames per second - codecContext->pix_fmt = AV_PIX_FMT_YUV420P; // 设置为YUV420P格式 - codecContext->gop_size = 1;//多少帧产生一组关键帧 - codecContext->max_b_frames = 1;//b帧,参考帧 -// codecContext->bit_rate = 1000000; // 设置比特率为 1000000 - - // 打开视频编码器 - if (avcodec_open2(codecContext, codec, nullptr) < 0) - { - qDebug() << "Error: Failed to open codec"; - avcodec_free_context(&codecContext); - avformat_free_context(formatContext); - return; - } - - // 打开输出文件 - if (avio_open(&formatContext->pb, outputVideoPath, AVIO_FLAG_WRITE) < 0) - { - qDebug() << "Error: Failed to open output file"; - avcodec_close(codecContext); - avcodec_free_context(&codecContext); - avformat_free_context(formatContext); - return; - } - - // 写入文件头 - avformat_write_header(formatContext, nullptr); - - // 使用sws_scale进行颜色空间转换 - SwsContext* swsContext = sws_getContext(width, height, AV_PIX_FMT_BGR24, - width, height, AV_PIX_FMT_YUV420P, - SWS_BICUBIC, nullptr, nullptr, nullptr); - if (!swsContext) - { - qDebug() << "Error: Failed to create sws context"; - avio_closep(&formatContext->pb); - avcodec_close(codecContext); - avcodec_free_context(&codecContext); - avformat_free_context(formatContext); - return; - } - - // 创建 AVFrame 作为目标图像 - AVFrame* dstFrame = av_frame_alloc(); - av_image_alloc(dstFrame->data, dstFrame->linesize, width, height, AV_PIX_FMT_YUV420P, 1); - - // 设置目标图像参数 - dstFrame->width = width; - dstFrame->height = height; - dstFrame->format = AV_PIX_FMT_YUV420P; - - AVFrame* frame = av_frame_alloc(); - av_image_alloc(frame->data, frame->linesize, width, height, AV_PIX_FMT_BGR24, 1); - - double framerate = getFramerate(); - int pushFlowFactor = framerate/framerateVideo; - - FILE * fp= fopen("/media/nvme/delete/300tc_fp.h264","wb"); - - QUdpSocket * m_udpSocket = new QUdpSocket(); - m_udpSocket->bind(PUSH_FLOW_PORT, QUdpSocket::ShareAddress); - QHostAddress m_clientIpAddress=QHostAddress(QHostAddress::LocalHost); -// QHostAddress m_clientIpAddress("192.168.1.21"); -// QHostAddress m_clientIpAddress("192.168.111.1"); - int udpSendCounter=0; - int encodeCounter=0; - //tc-------------------------------------------------------------------------------------------------------------------------- - m_imager.start(); struct timeval timeStart, timeEnd; double runTime=0; @@ -725,48 +692,11 @@ void XimeaImager::startRecord(double TimeDifferenceBetweensOSAndSbg,QString base } fwrite(m_imager.m_image.bp,1,m_iFrameSizeInByte, hFile); //构造rgb图像,用于推流到m300遥控器 - m_rgbImage->FillRgbImage((unsigned short *)m_imager.m_image.bp); - - - if (m_iFrameCounter % pushFlowFactor == 0) + if(m_iFlowSwitch == 1) { - memcpy(frame->data[0], m_rgbImage->m_matRgbImage->data, m_rgbImage->m_matRgbImage->rows * m_rgbImage->m_matRgbImage->step[0]); -// memcpy(frame->data[0], m_rgbImage->m_Qphoto.bits(), m_rgbImage->m_Qphoto.byteCount()); - - // 使用sws_scale进行颜色空间转换 - sws_scale(swsContext, frame->data, frame->linesize, 0, height, - dstFrame->data, dstFrame->linesize); - dstFrame->pts = encodeCounter; - - // 将AVFrame编码为视频帧 - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = nullptr; - pkt.size = 0; - - if (avcodec_send_frame(codecContext, dstFrame) == 0 && - avcodec_receive_packet(codecContext, &pkt) == 0) - { -// fwrite(pkt.data, 1, pkt.size, fp); -// - m_udpSocket->writeDatagram((const char *)pkt.data,pkt.size,m_clientIpAddress, PUSH_FLOW_PORT); -// -// std::cout<< "第 " << m_iFrameCounter<< " 帧," << "编码第 " << udpSendCounter << " 帧数据大小: " << pkt.size << std::endl; -// std::cout<< "pkt.pts: " << pkt.pts << std::endl; -// std::cout<< "pkt.dts: " << pkt.dts << std::endl << std::endl; - udpSendCounter++; - - // 将编码后的帧写入文件 -// pkt.stream_index = videoStream->index; -// av_interleaved_write_frame(formatContext, &pkt); -// av_write_frame(formatContext, &pkt); - - av_packet_unref(&pkt); - } - encodeCounter++; + m_rgbImage->FillRgbImage((unsigned short *)m_imager.m_image.bp); } - indexofbuff = m_iFrameCounter % number_WriteDisk; if (indexofbuff == 0) @@ -801,23 +731,7 @@ void XimeaImager::startRecord(double TimeDifferenceBetweensOSAndSbg,QString base writeData2Disk->exitWriteData2Disk(); writeHdr(); - fclose(fp); - // 写入文件尾 - av_write_trailer(formatContext); - - // 释放AVFrame和相关资源 - av_freep(&frame->data[0]); - av_frame_free(&frame); - - // 释放资源 - sws_freeContext(swsContext); - av_freep(&dstFrame->data[0]); - av_frame_free(&dstFrame); -// av_packet_free(&pkt); - avcodec_close(codecContext); - avcodec_free_context(&codecContext); - avio_closep(&formatContext->pb); - avformat_free_context(formatContext); + m_pushFlow->exitPushFlow(); delete[] sbgTimeBuffer; @@ -1239,3 +1153,213 @@ void WriteData2Disk::setParm(queue * q, queue * qFrameCounter m_rgbImage = rgbImage; } + +PushFlow::PushFlow() +{ + m_iWidth = 1368; + m_iHeight = 720; + m_iFramerateVideo = 5; +} + +void PushFlow::setParm(rgbImage * img, int width, int height, int framerateVideo) +{ + m_rgbImage = img; + + m_iWidth = width; + m_iHeight = height; + m_iFramerateVideo = framerateVideo; +} + +void PushFlow::setVedioFilePath(QString path) +{ + m_QVedioFilePath = path; +} + +void PushFlow::exitPushFlow() +{ + isExitPushFlow = true; +} + +void PushFlow::encodePushFlow() +{ + // 创建输出视频的AVFormatContext + const char* outputVideoPath = "/media/nvme/delete/300tc.h264"; + + FILE *fp = fopen(m_QVedioFilePath.toStdString().c_str(),"w+b"); + + AVFormatContext* formatContext = nullptr; + avformat_alloc_output_context2(&formatContext, nullptr, "mp4", outputVideoPath); + if (!formatContext) + { + qDebug() << "Error: Failed to allocate output context"; + return; + } + + // 查找H.264编码器 + const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); + if (!codec) + { + qDebug() << "Error: Codec not found"; + avformat_free_context(formatContext); + return; + } + + // 创建输出视频流 + AVStream* videoStream = avformat_new_stream(formatContext, codec); + if (!videoStream) + { + qDebug() << "Error: Failed to create video stream"; + avformat_free_context(formatContext); + return; + } + // 设置视频流的参数,例如分辨率、帧率等 + videoStream->codecpar->width = m_iWidth; + videoStream->codecpar->height = m_iHeight; + videoStream->codecpar->codec_id = AV_CODEC_ID_H264; // 设置为H.264编解码器 + videoStream->codecpar->format = AV_PIX_FMT_YUV420P; // 设置为YUV420P像素格式 + + // 配置视频流的参数 + AVCodecContext* codecContext = avcodec_alloc_context3(codec); + if (!codecContext) + { + qDebug() << "Error: Failed to allocate codec context"; + avformat_free_context(formatContext); + return; + } + + // 设置视频流的参数,例如分辨率、帧率等 + codecContext->width = m_iWidth; + codecContext->height = m_iHeight; + codecContext->time_base = {1, m_iFramerateVideo}; + codecContext->pix_fmt = AV_PIX_FMT_YUV420P; // 设置为YUV420P格式 + codecContext->gop_size = 1;//多少帧产生一组关键帧 + codecContext->max_b_frames = 1;//b帧,参考帧 +// codecContext->bit_rate = 1000000; // 设置比特率为 1000000 + + // 打开视频编码器 + if (avcodec_open2(codecContext, codec, nullptr) < 0) + { + qDebug() << "Error: Failed to open codec"; + avcodec_free_context(&codecContext); + avformat_free_context(formatContext); + return; + } + + // 打开输出文件 + if (avio_open(&formatContext->pb, outputVideoPath, AVIO_FLAG_WRITE) < 0) + { + qDebug() << "Error: Failed to open output file"; + avcodec_close(codecContext); + avcodec_free_context(&codecContext); + avformat_free_context(formatContext); + return; + } + + // 写入文件头 + avformat_write_header(formatContext, nullptr); + + // 使用sws_scale进行颜色空间转换 + SwsContext* swsContext = sws_getContext(m_iWidth, m_iHeight, AV_PIX_FMT_BGR24, + m_iWidth, m_iHeight, AV_PIX_FMT_YUV420P, + SWS_BICUBIC, nullptr, nullptr, nullptr); + if (!swsContext) + { + qDebug() << "Error: Failed to create sws context"; + avio_closep(&formatContext->pb); + avcodec_close(codecContext); + avcodec_free_context(&codecContext); + avformat_free_context(formatContext); + return; + } + + // 创建 AVFrame 作为目标图像 + AVFrame* dstFrame = av_frame_alloc(); + av_image_alloc(dstFrame->data, dstFrame->linesize, m_iWidth, m_iHeight, AV_PIX_FMT_YUV420P, 1); + + // 设置目标图像参数 + dstFrame->width = m_iWidth; + dstFrame->height = m_iHeight; + dstFrame->format = AV_PIX_FMT_YUV420P; + + AVFrame* frame = av_frame_alloc(); + av_image_alloc(frame->data, frame->linesize, m_iWidth, m_iHeight, AV_PIX_FMT_BGR24, 1); + + QUdpSocket * m_udpSocket = new QUdpSocket(); + m_udpSocket->bind(PUSH_FLOW_PORT, QUdpSocket::ShareAddress); + QHostAddress m_clientIpAddress=QHostAddress(QHostAddress::LocalHost); +// QHostAddress m_clientIpAddress("192.168.1.30"); +// QHostAddress m_clientIpAddress("192.168.111.1"); + int udpSendCounter=0; + int encodeCounter=0; + + isExitPushFlow = false; + + unsigned long sleepTime = 1/(float)m_iFramerateVideo * 1000; + std::cout<< "推流帧率: " << m_iFramerateVideo << ", sleepTime:" << sleepTime << "ms." << std::endl; + + while(true) + { + QThread::msleep(sleepTime); + + memcpy(frame->data[0], m_rgbImage->m_matRgbImage->data, m_rgbImage->m_matRgbImage->rows * m_rgbImage->m_matRgbImage->step[0]); +// memcpy(frame->data[0], m_rgbImage->m_Qphoto.bits(), m_rgbImage->m_Qphoto.byteCount()); + + // 使用sws_scale进行颜色空间转换 + sws_scale(swsContext, frame->data, frame->linesize, 0, m_iHeight, + dstFrame->data, dstFrame->linesize); + dstFrame->pts = encodeCounter; + + // 将AVFrame编码为视频帧 + AVPacket pkt; + av_init_packet(&pkt); + pkt.data = nullptr; + pkt.size = 0; + + if (avcodec_send_frame(codecContext, dstFrame) == 0 && + avcodec_receive_packet(codecContext, &pkt) == 0) + { + fwrite(pkt.data, 1, pkt.size, fp); + + m_udpSocket->writeDatagram((const char *)pkt.data,pkt.size,m_clientIpAddress, PUSH_FLOW_PORT); + +// std::cout << "编码第 " << udpSendCounter << " 帧数据大小: " << pkt.size << std::endl; +// std::cout<< "pkt.pts: " << pkt.pts << std::endl; +// std::cout<< "pkt.dts: " << pkt.dts << std::endl << std::endl; + udpSendCounter++; + + // 将编码后的帧写入文件 +// pkt.stream_index = videoStream->index; +// av_interleaved_write_frame(formatContext, &pkt); +// av_write_frame(formatContext, &pkt); + + av_packet_unref(&pkt); + } + encodeCounter++; + + if(isExitPushFlow) + { + std::cout<<"PushFlow::encodePushFlow-----------------------推流线程将退出!"<data[0]); + av_frame_free(&frame); + + // 释放资源 + sws_freeContext(swsContext); + av_freep(&dstFrame->data[0]); + av_frame_free(&dstFrame); +// av_packet_free(&pkt); + avcodec_close(codecContext); + avcodec_free_context(&codecContext); + avio_closep(&formatContext->pb); + avformat_free_context(formatContext); + + std::cout<<"PushFlow::encodePushFlow-----------------------推流线程已经退出!" << std::endl; +}