diff --git a/engine/ControlEngine/ControlEngine.cpp b/engine/ControlEngine/ControlEngine.cpp index c6673e0..14dc312 100644 --- a/engine/ControlEngine/ControlEngine.cpp +++ b/engine/ControlEngine/ControlEngine.cpp @@ -47,7 +47,9 @@ void ControlEngine::initParam() this->strDetectDate_ = ""; for (int i = 0; i < this->vecDataSourceConfig_.size(); ++i) { - this->mapDetectNO_[i] = 1; + this->mapDetectNO_[i] = 0; + this->mapIdentifyType_[i] = IDENTIFY_INIT; + } } @@ -62,18 +64,10 @@ void ControlEngine::endIdentify(int iDataSource) outputQueMap_[strPort1_]->push(std::static_pointer_cast(pVDetectInfo), true); outputQueMap_[strPort2_]->push(std::static_pointer_cast(pVDetectInfo), true); - this->mapDetectNO_[iDataSource] = 1; + this->mapDetectNO_[iDataSource] = 0; + this->mapIdentifyType_[iDataSource] = IDENTIFY_INIT; - bool bAllEnd = true; - for (const auto & dataSource_it : this->mapDetectNO_) - { - if (dataSource_it.second != 1) bAllEnd = false; - } - if (bAllEnd) - { - g_identify_type = IDENTIFY_INIT; - this->strDetectDate_ = ""; - } + LogInfo << " 数据源:" << iDataSource << " --- 识别结束!"; } void ControlEngine::sendWSEngine(std::string msg) @@ -83,6 +77,19 @@ void ControlEngine::sendWSEngine(std::string msg) outputQueMap_[strPort0_]->push(std::static_pointer_cast(std::make_shared(msg))); } +bool ControlEngine::isDetecting() +{ + for (const auto & dataSource : this->mapIdentifyType_) + { + if (dataSource.second == IDENTIFY_START) + { + return true; + } + } + + return false; +} + void ControlEngine::detectControl(std::shared_ptr pWSServerOrder) { Json::CharReaderBuilder readerBuilder; @@ -116,24 +123,25 @@ void ControlEngine::detectControl(std::shared_ptr pWSServerOrder) switch (jvOrder["commandType"].asInt()) { case IDENTIFY_START: - if (g_identify_type == IDENTIFY_START) + + if (this->isDetecting()) { std::string msg = "当前正在识别,无需重复发送识别信号"; LogWarn << msg; this->sendWSEngine(msg); break; } - g_identify_type = IDENTIFY_START; + this->mapIdentifyType_ = {{0, IDENTIFY_START}, {1, IDENTIFY_START}, {2, IDENTIFY_START}}; break; case IDENTIFY_STOP: - if (!g_identify_type) + if (!this->isDetecting()) { std::string msg = "当前已停止识别,无需重复发送结束信号"; LogWarn << msg; this->sendWSEngine(msg); break; } - g_identify_type = IDENTIFY_INIT; + this->mapIdentifyType_ = {{0, IDENTIFY_INIT}, {1, IDENTIFY_INIT}, {2, IDENTIFY_INIT}}; break; case IDENTIFY_RECORD: if (!jvOrder.isMember("containerNo")) @@ -181,29 +189,36 @@ APP_ERROR ControlEngine::Process() if (pProcessData->bIsEnd) { - // 仅读是视频模式下会进行 - if (this->mapDetectNO_[pProcessData->iDataSource] == 1) continue; + if (!this->isDetecting()) continue; this->endIdentify(pProcessData->iDataSource); - LogInfo << "数据源:" << pProcessData->iDataSource << " 视频画面播放结束:停止识别!"; + LogInfo << "数据源:" << pProcessData->iDataSource << " 画面读取结束:停止识别!"; continue; } - if (!g_identify_type) + if (!this->isDetecting()) { - if (this->mapDetectNO_[pProcessData->iDataSource] != 1) + if (this->mapDetectNO_[pProcessData->iDataSource] > 0) { this->endIdentify(pProcessData->iDataSource); } + this->strDetectDate_ = ""; + continue; + } + + if (this->mapIdentifyType_[pProcessData->iDataSource] == IDENTIFY_INIT) + { continue; } if (this->mapDetectNO_[pProcessData->iDataSource] > this->identifyConfig_.iMaxIdentifyFrame) { + LogInfo << " 数据源:" << pProcessData->iDataSource << " 超过最大允许识别帧数:" << this->identifyConfig_.iMaxIdentifyFrame << " 停止识别!"; this->endIdentify(pProcessData->iDataSource); - LogInfo << "数据源:" << pProcessData->iDataSource << " 超过最大允许识别帧数:" << this->identifyConfig_.iMaxIdentifyFrame << " 停止识别!"; continue; } + this->mapDetectNO_[pProcessData->iDataSource]++; + cv::Mat image(pProcessData->dataSourceInfo.iHeight, pProcessData->dataSourceInfo.iWidth, CV_8UC3, @@ -252,7 +267,7 @@ APP_ERROR ControlEngine::Process() pSaveImgData->bIsEnd = pProcessData->bIsEnd; outputQueMap_[strPort4_]->push(std::static_pointer_cast(pSaveImgData), true); - this->mapDetectNO_[pProcessData->iDataSource]++; + } } diff --git a/engine/ControlEngine/ControlEngine.h b/engine/ControlEngine/ControlEngine.h index e832836..2af0e80 100644 --- a/engine/ControlEngine/ControlEngine.h +++ b/engine/ControlEngine/ControlEngine.h @@ -36,6 +36,7 @@ private: uint32_t iDetectNO_ = 1; //动态检测数据编号 std::map mapDetectNO_; + std::map mapIdentifyType_; std::string strDetectDate_; std::string strDetectTime_; @@ -45,6 +46,7 @@ private: void endIdentify(int iDataSource); void sendWSEngine(std::string msg); void detectControl(std::shared_ptr pWSServerOrder); + bool isDetecting(); }; diff --git a/engine/DataSourceEngine/VideoEngine.cpp b/engine/DataSourceEngine/VideoEngine.cpp index 1837309..33b34eb 100644 --- a/engine/DataSourceEngine/VideoEngine.cpp +++ b/engine/DataSourceEngine/VideoEngine.cpp @@ -1 +1 @@ -#include "VideoEngine.h" using namespace ai_matrix; namespace { const int LOW_THRESHOLD = 128; const int MAX_THRESHOLD = 4096; const uint16_t DELAY_TIME = 10000; } VideoEngine::VideoEngine() {} VideoEngine::~VideoEngine() {} APP_ERROR VideoEngine::Init() { std::vector vecDataSourceConfig = Config::getins()->getAllDataSourceConfig(); if (vecDataSourceConfig.size() <= this->engineId_) { LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit"; return APP_ERR_OK; } dataSourceConfig_ = vecDataSourceConfig.at(engineId_); strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0"; strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1"; LogInfo << "engineId_:" << engineId_ << " VideoEngine Init ok"; return APP_ERR_OK; } APP_ERROR VideoEngine::DeInit() { ResetCamera(); LogInfo << "engineId_:" << engineId_ << " VideoEngine DeInit ok"; return APP_ERR_OK; } void VideoEngine::ResetCamera() { if (pFormatCtx_ != nullptr) { // clear th cache of the queue avformat_close_input(&pFormatCtx_); pFormatCtx_ = nullptr; } } APP_ERROR VideoEngine::ConnectCamera() { pFormatCtx_ = CreateFormatContext(); // create context if (pFormatCtx_ == nullptr) { LogError << "engineId_:" << engineId_ << " pFormatCtx_ null!"; return APP_ERR_COMM_FAILURE; } //0-代表输入 av_dump_format(pFormatCtx_, 0, dataSourceConfig_.strUrl.c_str(), 0); // get stream infomation int iRet = APP_ERR_OK; iRet = GetStreamInfo(); if (iRet != APP_ERR_OK) { LogError << "engineId_:" << engineId_ << " Stream Info Check failed, iRet = " << iRet; return APP_ERR_COMM_FAILURE; } return APP_ERR_OK; } APP_ERROR VideoEngine::GetStreamInfo() { if (pFormatCtx_ != nullptr) { iVideoStream_ = -1; iAudioStream_ = -1; //frameInfo_.iFrameId = 0; //帧号从0开始 for (unsigned int i = 0; i < pFormatCtx_->nb_streams; i++) { AVStream *inStream = pFormatCtx_->streams[i]; if (inStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { iVideoStream_ = i; frameInfo_.iHeight = inStream->codecpar->height; frameInfo_.iWidth = inStream->codecpar->width; //获取帧率,帧率的打印都在流中的两个成员.且应取平均帧率为先,为{x,0}或者{0,1}则取实时帧率 if (inStream->avg_frame_rate.den == 0 || (inStream->avg_frame_rate.num == 0 && inStream->avg_frame_rate.den == 1)) { frameInfo_.iRate = inStream->r_frame_rate.num / inStream->r_frame_rate.den; } else { frameInfo_.iRate = inStream->avg_frame_rate.num / inStream->avg_frame_rate.den; } frameInfo_.iRate = frameInfo_.iRate == 0 ? 25 : frameInfo_.iRate; LogDebug << "engineId_:" << engineId_ << " width:" << frameInfo_.iWidth << " height:" << frameInfo_.iHeight << " rate:" << frameInfo_.iRate << " iVideoStream_:" << iVideoStream_; } // else if (inStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) // { // iAudioStream_ = i; // LogDebug << "engineId_:" << engineId_ << " iAudioStream_:" << iAudioStream_; // } } if (iVideoStream_ == -1) { LogError << "engineId_:" << engineId_ << " Didn't find a video stream!"; return APP_ERR_COMM_FAILURE; } if (frameInfo_.iHeight < LOW_THRESHOLD || frameInfo_.iWidth < LOW_THRESHOLD || frameInfo_.iHeight > MAX_THRESHOLD || frameInfo_.iWidth > MAX_THRESHOLD) { LogError << "engineId_:" << engineId_ << " Size of frame is not supported in DVPP Video Decode!"; return APP_ERR_COMM_FAILURE; } pCodecParameters_ = pFormatCtx_->streams[iVideoStream_]->codecpar; } return APP_ERR_OK; } AVFormatContext *VideoEngine::CreateFormatContext() { // create message for stream pull AVFormatContext *pFormatContext = nullptr; AVDictionary *pOptions = nullptr; // formatContext = avformat_alloc_context(); if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos) // rtsp { //设置缓存大小,1080p可将值调大 av_dict_set(&pOptions, "buffer_size", "8192000", 0); //以tcp方式打开,如果以udp方式打开将tcp替换为udp av_dict_set(&pOptions, "rtsp_transport", "tcp", 0); //设置超时断开连接时间,单位微秒,3000000表示3秒 av_dict_set(&pOptions, "stimeout", "3000000", 0); //设置最大时延,单位微秒,1000000表示1秒 av_dict_set(&pOptions, "max_delay", "1000000", 0); //自动开启线程数 av_dict_set(&pOptions, "threads", "auto", 0); } //av_register_all(); //注册所有支持的格式(这里一定注册这些,否则会因为协议解析问题报错!!!) //avcodec_register_all(); //注册编解码器 //avformat_network_init(); //注册网格格式,如果为本地文件则可以去掉该代码 int iRet = avformat_open_input(&pFormatContext, dataSourceConfig_.strUrl.c_str(), nullptr, &pOptions); if (nullptr != pOptions) { av_dict_free(&pOptions); } if (iRet != 0) { LogError << "engineId_:" << engineId_ << " Couldn't open input stream " << dataSourceConfig_.strUrl.c_str() << ", iRet=" << iRet; return nullptr; } // pFormatContext->flags |= AVFMT_FLAG_NONBLOCK; // pFormatContext->pb->flags |= AVIO_FLAG_NONBLOCK; // av_dict_set(&pFormatContext->interrupt_callback.callback, "timeout", "3000", 0); // iRet = avio_open2(&pFormatContext->pb, dataSourceConfig_.strUrl.c_str(), AVIO_FLAG_READ, NULL, NULL) < 0; // { // // 处理错误 // LogError << "engineId_:" << engineId_ << "avio_open2 iRet=" << iRet; // return nullptr; // } iRet = avformat_find_stream_info(pFormatContext, nullptr); if (iRet != 0) { LogError << "engineId_:" << engineId_ << " Couldn't find stream information, iRet = " << iRet; return nullptr; } return pFormatContext; } //av_read_frame的中断回调函数 // int VideoEngine::InterruptCallback(void *pData) // { // TimeoutContext* pTimeOutCtx = (TimeoutContext*)pData; // LogDebug << "InterruptCallback i64Timeout:" << pTimeOutCtx->i64Timeout; // return std::chrono::duration_cast( // std::chrono::system_clock::now().time_since_epoch()) // .count() >= pTimeOutCtx->i64Timeout // ? AVERROR_EXIT // : 0; // } APP_ERROR VideoEngine::Process() { int iRet = APP_ERR_OK; // Pull data cyclically AVPacket pkt; while (!isStop_) { //重连相机 if (bConnectFlag_) { iRet = ConnectCamera(); if (iRet == APP_ERR_OK) { LogInfo << "engineId_:" << engineId_ << " Start the stream......"; bConnectFlag_ = false; } else { // outputQueMap_[strPort1_]->push(std::static_pointer_cast(std::make_shared("摄像头连接失败!"))); ResetCamera(); bConnectFlag_ = true; std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒后重连 continue; } } //设置av_read_frame中断函数 (中断函数中超过1s,则中断处理) // TimeoutContext timeoutCtx = { std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 1000 }; // pFormatCtx_->interrupt_callback.callback = &VideoEngine::InterruptCallback; // pFormatCtx_->interrupt_callback.opaque = &timeoutCtx; av_init_packet(&pkt); //init pkt iRet = av_read_frame(pFormatCtx_, &pkt); //需要一直读取,否则获取到的是历史数据 if (iRet != 0) { av_packet_unref(&pkt); if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos) { LogError << "engineId_:" << engineId_ << " Read frame failed, reconnect iRet:" << iRet; //重连相机 ResetCamera(); bConnectFlag_ = true; } else { // LogWarn << "----- 视频播放完毕 -----"; // //重连相机 // ResetCamera(); // bConnectFlag_ = true; //组织数据 std::shared_ptr pProcessData = std::make_shared(); pProcessData->iDataSource = this->engineId_; pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true); pProcessData->bIsEnd = true; //push端口0,视频解码 iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast(pProcessData), true); if (iRet != APP_ERR_OK) { LogError << "数据推动失败,解码引擎关闭..."; } std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒 }; continue; } if (pkt.stream_index == iVideoStream_) //只解码视频流 { // LogDebug << "iRet:" << iRet << " pkt.size:" << pkt.size; if (pkt.size <= 0) { LogError << "engineId_:" << engineId_ << " Invalid pkt.size: " << pkt.size; av_packet_unref(&pkt); continue; } if (dataSourceConfig_.strUrl.find(".mp4") != std::string::npos) { const char szStartCode[4] = {0, 0, 0, 1}; if (bIsAvc_ || memcmp(szStartCode, pkt.data, 4) != 0) { // is avc1 code, have no start code of H264 int iLen = 0; uint8_t *p = pkt.data; bIsAvc_ = true; do { // add start_code for each NAL, one frame may have multi NALs. iLen = ntohl(*((long *)p)); memcpy(p, szStartCode, 4); p += 4; p += iLen; if (p >= pkt.data + pkt.size) { break; } } while (true); } } void* pH264Buffer = nullptr; pH264Buffer = new uint8_t[pkt.size]; memcpy(pH264Buffer, pkt.data, pkt.size); //组织数据 std::shared_ptr pProcessData = std::make_shared(); pProcessData->iDataSource = this->engineId_; pProcessData->pCodecParameters_ = this->pCodecParameters_; pProcessData->dataSourceInfo = frameInfo_; pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true); pProcessData->sourceFrameData.iSize = pkt.size; pProcessData->sourceFrameData.pData.reset(pH264Buffer, [](void* data){if(data) {delete[] data; data = nullptr;}}); //智能指针管理内存 //push端口0,视频解码 iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast(pProcessData), true); if (iRet != APP_ERR_OK) { LogError << "数据推动失败,解码引擎关闭..."; } } else { // LogError << "engineId_:" << engineId_ << " stream err stream_index:" << pkt.stream_index; } av_packet_unref(&pkt); //unref if (dataSourceConfig_.strUrl.find("rtsp:") == std::string::npos) // 如果不是rtsp,定时发送 { std::this_thread::sleep_for(std::chrono::milliseconds(1000 / frameInfo_.iRate)); if (this->isStop_) return APP_ERR_OK; } } return APP_ERR_OK; } \ No newline at end of file +#include "VideoEngine.h" using namespace ai_matrix; namespace { const int LOW_THRESHOLD = 128; const int MAX_THRESHOLD = 4096; const uint16_t DELAY_TIME = 10000; } VideoEngine::VideoEngine() {} VideoEngine::~VideoEngine() {} APP_ERROR VideoEngine::Init() { std::vector vecDataSourceConfig = Config::getins()->getAllDataSourceConfig(); if (vecDataSourceConfig.size() <= this->engineId_) { LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit"; return APP_ERR_OK; } dataSourceConfig_ = vecDataSourceConfig.at(engineId_); strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0"; strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1"; LogInfo << "engineId_:" << engineId_ << " VideoEngine Init ok"; return APP_ERR_OK; } APP_ERROR VideoEngine::DeInit() { ResetCamera(); LogInfo << "engineId_:" << engineId_ << " VideoEngine DeInit ok"; return APP_ERR_OK; } void VideoEngine::ResetCamera() { if (pFormatCtx_ != nullptr) { // clear th cache of the queue avformat_close_input(&pFormatCtx_); pFormatCtx_ = nullptr; } } APP_ERROR VideoEngine::ConnectCamera() { pFormatCtx_ = CreateFormatContext(); // create context if (pFormatCtx_ == nullptr) { LogError << "engineId_:" << engineId_ << " pFormatCtx_ null!"; return APP_ERR_COMM_FAILURE; } //0-代表输入 av_dump_format(pFormatCtx_, 0, dataSourceConfig_.strUrl.c_str(), 0); // get stream infomation int iRet = APP_ERR_OK; iRet = GetStreamInfo(); if (iRet != APP_ERR_OK) { LogError << "engineId_:" << engineId_ << " Stream Info Check failed, iRet = " << iRet; return APP_ERR_COMM_FAILURE; } return APP_ERR_OK; } APP_ERROR VideoEngine::GetStreamInfo() { if (pFormatCtx_ != nullptr) { iVideoStream_ = -1; iAudioStream_ = -1; //frameInfo_.iFrameId = 0; //帧号从0开始 for (unsigned int i = 0; i < pFormatCtx_->nb_streams; i++) { AVStream *inStream = pFormatCtx_->streams[i]; if (inStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { iVideoStream_ = i; frameInfo_.iHeight = inStream->codecpar->height; frameInfo_.iWidth = inStream->codecpar->width; //获取帧率,帧率的打印都在流中的两个成员.且应取平均帧率为先,为{x,0}或者{0,1}则取实时帧率 if (inStream->avg_frame_rate.den == 0 || (inStream->avg_frame_rate.num == 0 && inStream->avg_frame_rate.den == 1)) { frameInfo_.iRate = inStream->r_frame_rate.num / inStream->r_frame_rate.den; } else { frameInfo_.iRate = inStream->avg_frame_rate.num / inStream->avg_frame_rate.den; } frameInfo_.iRate = frameInfo_.iRate == 0 ? 25 : frameInfo_.iRate; LogDebug << "engineId_:" << engineId_ << " width:" << frameInfo_.iWidth << " height:" << frameInfo_.iHeight << " rate:" << frameInfo_.iRate << " iVideoStream_:" << iVideoStream_; } // else if (inStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) // { // iAudioStream_ = i; // LogDebug << "engineId_:" << engineId_ << " iAudioStream_:" << iAudioStream_; // } } if (iVideoStream_ == -1) { LogError << "engineId_:" << engineId_ << " Didn't find a video stream!"; return APP_ERR_COMM_FAILURE; } if (frameInfo_.iHeight < LOW_THRESHOLD || frameInfo_.iWidth < LOW_THRESHOLD || frameInfo_.iHeight > MAX_THRESHOLD || frameInfo_.iWidth > MAX_THRESHOLD) { LogError << "engineId_:" << engineId_ << " Size of frame is not supported in DVPP Video Decode!"; return APP_ERR_COMM_FAILURE; } pCodecParameters_ = pFormatCtx_->streams[iVideoStream_]->codecpar; } return APP_ERR_OK; } AVFormatContext *VideoEngine::CreateFormatContext() { // create message for stream pull AVFormatContext *pFormatContext = nullptr; AVDictionary *pOptions = nullptr; // formatContext = avformat_alloc_context(); if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos) // rtsp { //设置缓存大小,1080p可将值调大 av_dict_set(&pOptions, "buffer_size", "8192000", 0); //以tcp方式打开,如果以udp方式打开将tcp替换为udp av_dict_set(&pOptions, "rtsp_transport", "tcp", 0); //设置超时断开连接时间,单位微秒,3000000表示3秒 av_dict_set(&pOptions, "stimeout", "3000000", 0); //设置最大时延,单位微秒,1000000表示1秒 av_dict_set(&pOptions, "max_delay", "1000000", 0); //自动开启线程数 av_dict_set(&pOptions, "threads", "auto", 0); } //av_register_all(); //注册所有支持的格式(这里一定注册这些,否则会因为协议解析问题报错!!!) //avcodec_register_all(); //注册编解码器 //avformat_network_init(); //注册网格格式,如果为本地文件则可以去掉该代码 int iRet = avformat_open_input(&pFormatContext, dataSourceConfig_.strUrl.c_str(), nullptr, &pOptions); if (nullptr != pOptions) { av_dict_free(&pOptions); } if (iRet != 0) { LogError << "engineId_:" << engineId_ << " Couldn't open input stream " << dataSourceConfig_.strUrl.c_str() << ", iRet=" << iRet; return nullptr; } // pFormatContext->flags |= AVFMT_FLAG_NONBLOCK; // pFormatContext->pb->flags |= AVIO_FLAG_NONBLOCK; // av_dict_set(&pFormatContext->interrupt_callback.callback, "timeout", "3000", 0); // iRet = avio_open2(&pFormatContext->pb, dataSourceConfig_.strUrl.c_str(), AVIO_FLAG_READ, NULL, NULL) < 0; // { // // 处理错误 // LogError << "engineId_:" << engineId_ << "avio_open2 iRet=" << iRet; // return nullptr; // } iRet = avformat_find_stream_info(pFormatContext, nullptr); if (iRet != 0) { LogError << "engineId_:" << engineId_ << " Couldn't find stream information, iRet = " << iRet; return nullptr; } return pFormatContext; } //av_read_frame的中断回调函数 // int VideoEngine::InterruptCallback(void *pData) // { // TimeoutContext* pTimeOutCtx = (TimeoutContext*)pData; // LogDebug << "InterruptCallback i64Timeout:" << pTimeOutCtx->i64Timeout; // return std::chrono::duration_cast( // std::chrono::system_clock::now().time_since_epoch()) // .count() >= pTimeOutCtx->i64Timeout // ? AVERROR_EXIT // : 0; // } APP_ERROR VideoEngine::Process() { int iRet = APP_ERR_OK; // Pull data cyclically AVPacket pkt; while (!isStop_) { //重连相机 if (bConnectFlag_) { iRet = ConnectCamera(); if (iRet == APP_ERR_OK) { LogInfo << "engineId_:" << engineId_ << " Start the stream......"; bConnectFlag_ = false; } else { // outputQueMap_[strPort1_]->push(std::static_pointer_cast(std::make_shared("摄像头连接失败!"))); ResetCamera(); bConnectFlag_ = true; std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒后重连 continue; } } //设置av_read_frame中断函数 (中断函数中超过1s,则中断处理) // TimeoutContext timeoutCtx = { std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 1000 }; // pFormatCtx_->interrupt_callback.callback = &VideoEngine::InterruptCallback; // pFormatCtx_->interrupt_callback.opaque = &timeoutCtx; av_init_packet(&pkt); //init pkt iRet = av_read_frame(pFormatCtx_, &pkt); //需要一直读取,否则获取到的是历史数据 if (iRet != 0) { av_packet_unref(&pkt); if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos) { LogError << "engineId_:" << engineId_ << " Read frame failed, reconnect iRet:" << iRet; //重连相机 ResetCamera(); bConnectFlag_ = true; } else { // LogWarn << "----- 视频播放完毕 -----"; // //重连相机 // ResetCamera(); // bConnectFlag_ = true; std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒 }; //组织数据 std::shared_ptr pProcessData = std::make_shared(); pProcessData->iDataSource = this->engineId_; pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true); pProcessData->bIsEnd = true; //push端口0,视频解码 iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast(pProcessData), true); if (iRet != APP_ERR_OK) { LogError << "数据推动失败,解码引擎关闭..."; } continue; } if (pkt.stream_index == iVideoStream_) //只解码视频流 { // LogDebug << "iRet:" << iRet << " pkt.size:" << pkt.size; if (pkt.size <= 0) { LogError << "engineId_:" << engineId_ << " Invalid pkt.size: " << pkt.size; av_packet_unref(&pkt); continue; } if (dataSourceConfig_.strUrl.find(".mp4") != std::string::npos) { const char szStartCode[4] = {0, 0, 0, 1}; if (bIsAvc_ || memcmp(szStartCode, pkt.data, 4) != 0) { // is avc1 code, have no start code of H264 int iLen = 0; uint8_t *p = pkt.data; bIsAvc_ = true; do { // add start_code for each NAL, one frame may have multi NALs. iLen = ntohl(*((long *)p)); memcpy(p, szStartCode, 4); p += 4; p += iLen; if (p >= pkt.data + pkt.size) { break; } } while (true); } } void* pH264Buffer = nullptr; pH264Buffer = new uint8_t[pkt.size]; memcpy(pH264Buffer, pkt.data, pkt.size); //组织数据 std::shared_ptr pProcessData = std::make_shared(); pProcessData->iDataSource = this->engineId_; pProcessData->pCodecParameters_ = this->pCodecParameters_; pProcessData->dataSourceInfo = frameInfo_; pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true); pProcessData->sourceFrameData.iSize = pkt.size; pProcessData->sourceFrameData.pData.reset(pH264Buffer, [](void* data){if(data) {delete[] data; data = nullptr;}}); //智能指针管理内存 //push端口0,视频解码 iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast(pProcessData), true); if (iRet != APP_ERR_OK) { LogError << "数据推动失败,解码引擎关闭..."; } } else { // LogError << "engineId_:" << engineId_ << " stream err stream_index:" << pkt.stream_index; } av_packet_unref(&pkt); //unref if (dataSourceConfig_.strUrl.find("rtsp:") == std::string::npos) // 如果不是rtsp,定时发送 { std::this_thread::sleep_for(std::chrono::milliseconds(1000 / frameInfo_.iRate)); if (this->isStop_) return APP_ERR_OK; } } return APP_ERR_OK; } \ No newline at end of file diff --git a/engine/DecodeEngine/VideoDecodeEngine.cpp b/engine/DecodeEngine/VideoDecodeEngine.cpp index 7449fce..7f3f5a0 100644 --- a/engine/DecodeEngine/VideoDecodeEngine.cpp +++ b/engine/DecodeEngine/VideoDecodeEngine.cpp @@ -151,7 +151,7 @@ APP_ERROR VideoDecodeEngine::Process() } else { - LogError << " 硬解码失败... 返回失败信息:" << iDecodeRet; + LogError << "数据源:" << pProcessData->iDataSource << " 硬解码失败... 返回失败信息:" << iDecodeRet; } } } diff --git a/engine/DetectDivideEngine/ContainerDivideEngine.cpp b/engine/DetectDivideEngine/ContainerDivideEngine.cpp index 8647b98..b5f3c9e 100644 --- a/engine/DetectDivideEngine/ContainerDivideEngine.cpp +++ b/engine/DetectDivideEngine/ContainerDivideEngine.cpp @@ -1 +1 @@ -#include "ContainerDivideEngine.h" #include using namespace ai_matrix; ContainerDivideEngine::ContainerDivideEngine() {} ContainerDivideEngine::~ContainerDivideEngine() {} APP_ERROR ContainerDivideEngine::Init() { strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0"; strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1"; this->vecDataSourceConfig_ = Config::getins()->getAllDataSourceConfig(); if (this->vecDataSourceConfig_.size() <= this->engineId_) { LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit"; return APP_ERR_OK; } this->dataSourceConfig_ = this->vecDataSourceConfig_.at(engineId_); this->baseConfig_ = Config::getins()->getBaseConfig(); this->identifyConfig_ = Config::getins()->getIdentifyConfig(); this->initParam(); LogInfo << "DetectDivideEngine Init ok"; return APP_ERR_OK; } APP_ERROR ContainerDivideEngine::DeInit() { LogInfo << "DetectDivideEngine DeInit ok"; return APP_ERR_OK; } /** * 初始化参数信息 * inParam : N/A * outParam: N/A * return : N/A */ void ContainerDivideEngine::initParam() { this->iContainerIndex = 0; this->pVStep2OutputDataPre_ = nullptr; this->vecContainerFail_.clear(); } void ContainerDivideEngine::sendBestData(const VSelectBestData& selectBestData) { // 直接发送第一个箱子的箱号 std::shared_ptr pVSelectBestData = std::make_shared(); *pVSelectBestData = selectBestData; outputQueMap_[strPort0_]->push(std::static_pointer_cast(pVSelectBestData), true); LogInfo << " 帧:" << selectBestData.iFrameId << " 数据源:" << selectBestData.iDataSource << " 节:" << selectBestData.iContainerIndex << " 发送识别结果 " << selectBestData.strNumResult; this->stdContainerResult_ = selectBestData.strNumResult; this->iContainerIndex++; } void ContainerDivideEngine::makeResult(const std::shared_ptr& pVStep2OutputData, VSelectBestData & selectBestData) const { selectBestData.strDetectDate = pVStep2OutputData->strDetectDate; selectBestData.strDetectTime = pVStep2OutputData->strDetectTime; selectBestData.iDataSource = pVStep2OutputData->iDataSource; selectBestData.iFrameId = pVStep2OutputData->iFrameId; selectBestData.strNumResult = pVStep2OutputData->step2ResultData.transInfo.strTmpResult; selectBestData.bIsChkFlag = pVStep2OutputData->step2ResultData.transInfo.bIsChkFlag; selectBestData.fSumScore = pVStep2OutputData->step2ResultData.fSubScoreSum; selectBestData.iContainerIndex = this->iContainerIndex; selectBestData.bHaveTwoContainer = pVStep2OutputData->vecCornerResultData.size() > 2; } /** * 依据像素进行集装箱切分 * @param pVStep2OutputData */ void ContainerDivideEngine::divideByPixelInfo(std::shared_ptr pVStep2OutputData) { auto vCompare = [](std::string a, std::string b) { if (a.size() != b.size()) return 999; int count = 0; for (int i = 0; i < a.size(); ++i) { if (a[i] != b[i]) { ++count; } } return count; }; if (!this->pVStep2OutputDataPre_) { if (pVStep2OutputData->step2ResultData.fScore == 0.0f) return; this->pVStep2OutputDataPre_ = pVStep2OutputData; } Step2ResultData step2ResultData = pVStep2OutputData->step2ResultData; Step2ResultData step2ResultData_pre = this->pVStep2OutputDataPre_->step2ResultData; float fCenterX = (step2ResultData.fRBX + step2ResultData.fLTX)/2; float fCenterY = (step2ResultData.fRBY + step2ResultData.fLTY)/2; float fCenterX_Pre = (step2ResultData_pre.fRBX + step2ResultData_pre.fLTX)/2; float fCenterY_Pre = (step2ResultData_pre.fRBY + step2ResultData_pre.fLTY)/2; LogDebug << " 帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " Y差值:" << std::abs(fCenterY - fCenterY_Pre) << " X差值:" << std::abs(fCenterX - fCenterX_Pre); if ((pVStep2OutputData->iDataSource == 0 && std::abs(fCenterY - fCenterY_Pre) > this->identifyConfig_.iTop_Y_SplitSpanPx) || (pVStep2OutputData->iDataSource != 0 && std::abs(fCenterX - fCenterX_Pre) > this->identifyConfig_.iSide_X_SplitSpanPx)) { if (step2ResultData.transInfo.bIsChkFlag) { if (stdContainerResult_ != step2ResultData.transInfo.strTmpResult) { if (stdContainerResult_.empty()) { // 筛选不满足校验的部分 取最长的发送 int iSize = 0, iIndex = 0; std::string strContainer_tmp; for (int i = 0; i < this->vecContainerFail_.size(); i++) { this->vecContainerFail_[i]; if (iSize < this->vecContainerFail_[i].strNumResult.size()) { iSize = this->vecContainerFail_[i].strNumResult.size(); iIndex = i; strContainer_tmp = this->vecContainerFail_[i].strNumResult; } } sendBestData(this->vecContainerFail_[iIndex]); } // 记录第二箱子 this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->sendBestData(selectBestData); } } else { // 筛选不满足校验的部分 取最长的发送 int iSize = 0, iIndex = 0; std::string strContainer_tmp; for (int i = 0; i < this->vecContainerFail_.size(); i++) { this->vecContainerFail_[i]; if (iSize < this->vecContainerFail_[i].strNumResult.size()) { iSize = this->vecContainerFail_[i].strNumResult.size(); iIndex = i; strContainer_tmp = this->vecContainerFail_[i].strNumResult; } } if (this->vecContainerFail_.size() > 0 && this->iContainerIndex < 1) { sendBestData(this->vecContainerFail_[iIndex]); } // 把之前的清理掉再存新箱子的 (有风险,有可能切分错) this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->vecContainerFail_.emplace_back(selectBestData); } } else {// 不满足像素差 if (!this->stdContainerResult_.empty()) { if (step2ResultData.transInfo.bIsChkFlag) { if (this->stdContainerResult_ != step2ResultData.transInfo.strTmpResult) { // if (vCompare(this->stdContainerResult_, step2ResultData.transInfo.strTmpResult) > 2) // { this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->sendBestData(selectBestData); // } } } } else { if (step2ResultData.transInfo.bIsChkFlag) { this->stdContainerResult_ = step2ResultData.transInfo.strTmpResult; this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->sendBestData(selectBestData); } else { VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->vecContainerFail_.emplace_back(selectBestData); } } } } bool ContainerDivideEngine::setectBestByCorner() { if (!this->vecContainer_.empty()) { // 汇总下vecContainer中,有几种正确的识别结果,选出识别次数最多的 std::map mapContainer_count; std::map mapContainer_index; int i = 0; for (auto &container: this->vecContainer_) { mapContainer_count[container.strNumResult]++; mapContainer_index[container.strNumResult] = i; ++i; } std::string strBestContainer; int count = 0; // 取出现次数最高的 且与第一个箱子不一样的 for (auto &it_max: mapContainer_count) { if (it_max.first.empty()) continue; if (it_max.second >= count) { count = it_max.second; strBestContainer = it_max.first; } } this->sendBestData(this->vecContainer_[mapContainer_index[strBestContainer]]); this->vecContainer_.clear(); this->vecContainerFail_.clear(); } else { if (this->vecContainerFail_.empty()) return false; // 汇总下vecContainer中,有几种正确的识别结果,选出识别次数最多的 int iMaxSize = 0, iIndex = 0; std::map mapContainer_index; int i = 0; for (auto &container: this->vecContainerFail_) { if (container.strNumResult.size() > iMaxSize) { iMaxSize = container.strNumResult.size(); iIndex = i; } ++i; } this->sendBestData(this->vecContainerFail_[iIndex]); this->vecContainer_.clear(); this->vecContainerFail_.clear(); } return true; } /** * 依据箱角切分 * @param pVStep2OutputData */ void ContainerDivideEngine::divideByCornerInfo(std::shared_ptr pVStep2OutputData) { Step2ResultData step2ResultData = pVStep2OutputData->step2ResultData; if (pVStep2OutputData->vecCornerResultData.size() > 1 && this->iContainerIndex < 1) { if (pVStep2OutputData->vecCornerResultData.size() > 2) { LogWarn << "帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " ---- >>> 检测到超过2个箱角,疑似画面过于倾斜或模型训练不足!"; return; } float fDiffX = std::abs(pVStep2OutputData->vecCornerResultData[0].fLTX - pVStep2OutputData->vecCornerResultData[1].fLTX); float fDiffY = std::abs(pVStep2OutputData->vecCornerResultData[0].fLTY - pVStep2OutputData->vecCornerResultData[1].fLTY); if (fDiffX > this->identifyConfig_.iMaxContainerSpaceX || fDiffY > this->identifyConfig_.iMaxContainerSpaceY) { LogWarn << "帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " ---- >>> 检测到超过2个间隔过远的箱角,疑似画面过于倾斜看到一个箱子的多个角或模型训练不足!"; return; } LogInfo << "帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " ---- >>> 检测到多个箱角,准备切分"; if (pVStep2OutputData->iDataSource == 0 || this->dataSourceConfig_.strRunDirection == "right") { if (this->vecContainerFail_.empty()) { VSelectBestData selectBestData; selectBestData.strDetectDate = pVStep2OutputData->strDetectDate; selectBestData.strDetectTime = pVStep2OutputData->strDetectTime; selectBestData.iDataSource = pVStep2OutputData->iDataSource; selectBestData.iFrameId = 1; selectBestData.strNumResult = "invalid-?"; selectBestData.iContainerIndex = this->iContainerIndex; selectBestData.bHaveTwoContainer = true; this->vecContainerFail_.emplace_back(selectBestData); } this->setectBestByCorner(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); if (step2ResultData.transInfo.bIsChkFlag) { this->vecContainer_.emplace_back(selectBestData); } else { this->vecContainerFail_.emplace_back(selectBestData); } return; } if (this->dataSourceConfig_.strRunDirection == "left") { LogWarn << "向左行驶的一侧暂不支持箱角识别,请及时修改参数 divide_mode: \"pixel\""; return; } } else { VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); if (step2ResultData.transInfo.bIsChkFlag) { this->vecContainer_.emplace_back(selectBestData); } else { this->vecContainerFail_.emplace_back(selectBestData); } } } /** * 切分车厢 * @param pVStep2OutputData */ void ContainerDivideEngine::divideInfo(std::shared_ptr pVStep2OutputData) { // if (!this->pVStep2OutputDataPre_) return; if (this->iContainerIndex > 1) return; if (this->vecDataSourceConfig_[pVStep2OutputData->iDataSource].strDivideModel == "corner") { this->divideByCornerInfo(pVStep2OutputData); } else { this->divideByPixelInfo(pVStep2OutputData); } } APP_ERROR ContainerDivideEngine::Process() { int iRet = APP_ERR_OK; while (!isStop_) { //pop端口0 std::shared_ptr pVoidData0 = nullptr; iRet = inputQueMap_[strPort0_]->pop(pVoidData0); if (nullptr == pVoidData0) { usleep(1000); continue; } std::shared_ptr pVStep2OutputData = std::static_pointer_cast(pVoidData0); if (pVStep2OutputData->bIsEnd) { std::shared_ptr pVSelectBestData = std::make_shared(); pVSelectBestData->bIsEnd = true; pVSelectBestData->iDataSource = pVStep2OutputData->iDataSource; pVSelectBestData->strDetectDate = pVStep2OutputData->strDetectDate; pVSelectBestData->strDetectTime = pVStep2OutputData->strDetectTime; if (this->iContainerIndex > 1) { outputQueMap_[strPort0_]->push(std::static_pointer_cast(pVSelectBestData), true); this->initParam(); continue; } if (this->vecDataSourceConfig_[pVStep2OutputData->iDataSource].strDivideModel == "corner") { if (!this->vecContainerFail_.empty() || !this->vecContainer_.empty()) { this->setectBestByCorner(); } } else { if (!this->vecContainerFail_.empty()) { // 筛选不满足校验的部分 取最长的发送 int iSize = 0, iIndex = 0; std::string strContainer_tmp; for (int i = 0; i < this->vecContainerFail_.size(); i++) { this->vecContainerFail_[i]; if (iSize < this->vecContainerFail_[i].strNumResult.size()) { iSize = this->vecContainerFail_[i].strNumResult.size(); iIndex = i; strContainer_tmp = this->vecContainerFail_[i].strNumResult; } } this->vecContainerFail_[iIndex].bIsEnd = true; sendBestData(this->vecContainerFail_[iIndex]); this->initParam(); continue; } } outputQueMap_[strPort0_]->push(std::static_pointer_cast(pVSelectBestData), true); this->initParam(); continue; } std::string strFilePath ; strFilePath = this->baseConfig_.strDebugResultPath + "/" + pVStep2OutputData->strDetectDate + "/" + StringUtil::getins()->replace_all_distinct(pVStep2OutputData->strDetectTime, ":", "-") + "/" + std::to_string(pVStep2OutputData->iFrameId) + "_" + std::to_string(pVStep2OutputData->iDataSource) + ".json"; // 先读取文本内容,追加新的信息后再写入 Json::Value jvFrameInfo; if (!FileUtil::getins()->readJsonInfo(jvFrameInfo, strFilePath)) { LogError << "read fail:" << strFilePath; } // jvFrameInfo["isEnd"] = (pVStep2OutputData->bIsEnd || jvFrameInfo["isEnd"].asBool()); // 识别结果存储 float fCenter = pVStep2OutputData->step2ResultData.fLTX + (pVStep2OutputData->step2ResultData.fRBX - pVStep2OutputData->step2ResultData.fLTX) / 2; // this->mapContainerCenterInfo_.insert(std::make_pair(pVStep2OutputData->iFrameId, fCenter)); Json::Value jvInfo; jvInfo["classid"] = pVStep2OutputData->step2ResultData.iClassId; jvInfo["score"] = pVStep2OutputData->step2ResultData.fScore; jvInfo["ltx"] = pVStep2OutputData->step2ResultData.fLTX; jvInfo["lty"] = pVStep2OutputData->step2ResultData.fLTY; jvInfo["rbx"] = pVStep2OutputData->step2ResultData.fRBX; jvInfo["rby"] = pVStep2OutputData->step2ResultData.fRBY; jvFrameInfo["step1"].append(jvInfo); for (auto &step2ResultData : pVStep2OutputData->vecCornerResultData) { float fCenter = step2ResultData.fLTX + (step2ResultData.fRBX - step2ResultData.fLTX) / 2; // this->mapContainerCenterInfo_.insert(std::make_pair(pVStep2OutputData->iFrameId, fCenter)); Json::Value jvInfo; jvInfo["classid"] = step2ResultData.iClassId; jvInfo["score"] = step2ResultData.fScore; jvInfo["ltx"] = step2ResultData.fLTX; jvInfo["lty"] = step2ResultData.fLTY; jvInfo["rbx"] = step2ResultData.fRBX; jvInfo["rby"] = step2ResultData.fRBY; jvFrameInfo["step1"].append(jvInfo); } FileUtil::getins()->writeJsonInfo(jvFrameInfo, strFilePath); if (pVStep2OutputData->step2ResultData.fScore != 0.0f) { LogDebug << " 帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " - "<< pVStep2OutputData->step2ResultData.transInfo.strTmpResult; } this->divideInfo(pVStep2OutputData); } return APP_ERR_OK; } \ No newline at end of file +#include "ContainerDivideEngine.h" #include using namespace ai_matrix; ContainerDivideEngine::ContainerDivideEngine() {} ContainerDivideEngine::~ContainerDivideEngine() {} APP_ERROR ContainerDivideEngine::Init() { strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0"; strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1"; this->vecDataSourceConfig_ = Config::getins()->getAllDataSourceConfig(); if (this->vecDataSourceConfig_.size() <= this->engineId_) { LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit"; return APP_ERR_OK; } this->dataSourceConfig_ = this->vecDataSourceConfig_.at(engineId_); this->baseConfig_ = Config::getins()->getBaseConfig(); this->identifyConfig_ = Config::getins()->getIdentifyConfig(); this->initParam(); LogInfo << "DetectDivideEngine Init ok"; return APP_ERR_OK; } APP_ERROR ContainerDivideEngine::DeInit() { LogInfo << "DetectDivideEngine DeInit ok"; return APP_ERR_OK; } /** * 初始化参数信息 * inParam : N/A * outParam: N/A * return : N/A */ void ContainerDivideEngine::initParam() { this->bLastIsEnd = true; this->iContainerIndex = 0; this->pVStep2OutputDataPre_ = nullptr; this->vecContainerFail_.clear(); } void ContainerDivideEngine::sendBestData(const VSelectBestData& selectBestData) { // 直接发送第一个箱子的箱号 std::shared_ptr pVSelectBestData = std::make_shared(); *pVSelectBestData = selectBestData; outputQueMap_[strPort0_]->push(std::static_pointer_cast(pVSelectBestData), true); LogInfo << " 帧:" << selectBestData.iFrameId << " 数据源:" << selectBestData.iDataSource << " 节:" << selectBestData.iContainerIndex << " 发送识别结果 " << selectBestData.strNumResult; this->stdContainerResult_ = selectBestData.strNumResult; this->iContainerIndex++; } void ContainerDivideEngine::makeResult(const std::shared_ptr& pVStep2OutputData, VSelectBestData & selectBestData) const { selectBestData.strDetectDate = pVStep2OutputData->strDetectDate; selectBestData.strDetectTime = pVStep2OutputData->strDetectTime; selectBestData.iDataSource = pVStep2OutputData->iDataSource; selectBestData.iFrameId = pVStep2OutputData->iFrameId; selectBestData.strNumResult = pVStep2OutputData->step2ResultData.transInfo.strTmpResult; selectBestData.bIsChkFlag = pVStep2OutputData->step2ResultData.transInfo.bIsChkFlag; selectBestData.fSumScore = pVStep2OutputData->step2ResultData.fSubScoreSum; selectBestData.iContainerIndex = this->iContainerIndex; selectBestData.bHaveTwoContainer = pVStep2OutputData->vecCornerResultData.size() > 2; } /** * 依据像素进行集装箱切分 * @param pVStep2OutputData */ void ContainerDivideEngine::divideByPixelInfo(std::shared_ptr pVStep2OutputData) { auto vCompare = [](std::string a, std::string b) { if (a.size() != b.size()) return 999; int count = 0; for (int i = 0; i < a.size(); ++i) { if (a[i] != b[i]) { ++count; } } return count; }; if (!this->pVStep2OutputDataPre_) { if (pVStep2OutputData->step2ResultData.fScore == 0.0f) return; this->pVStep2OutputDataPre_ = pVStep2OutputData; } Step2ResultData step2ResultData = pVStep2OutputData->step2ResultData; Step2ResultData step2ResultData_pre = this->pVStep2OutputDataPre_->step2ResultData; float fCenterX = (step2ResultData.fRBX + step2ResultData.fLTX)/2; float fCenterY = (step2ResultData.fRBY + step2ResultData.fLTY)/2; float fCenterX_Pre = (step2ResultData_pre.fRBX + step2ResultData_pre.fLTX)/2; float fCenterY_Pre = (step2ResultData_pre.fRBY + step2ResultData_pre.fLTY)/2; LogDebug << " 帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " Y差值:" << std::abs(fCenterY - fCenterY_Pre) << " X差值:" << std::abs(fCenterX - fCenterX_Pre); if ((pVStep2OutputData->iDataSource == 0 && std::abs(fCenterY - fCenterY_Pre) > this->identifyConfig_.iTop_Y_SplitSpanPx) || (pVStep2OutputData->iDataSource != 0 && std::abs(fCenterX - fCenterX_Pre) > this->identifyConfig_.iSide_X_SplitSpanPx)) { if (step2ResultData.transInfo.bIsChkFlag) { if (stdContainerResult_ != step2ResultData.transInfo.strTmpResult) { if (stdContainerResult_.empty()) { // 筛选不满足校验的部分 取最长的发送 int iSize = 0, iIndex = 0; std::string strContainer_tmp; for (int i = 0; i < this->vecContainerFail_.size(); i++) { this->vecContainerFail_[i]; if (iSize < this->vecContainerFail_[i].strNumResult.size()) { iSize = this->vecContainerFail_[i].strNumResult.size(); iIndex = i; strContainer_tmp = this->vecContainerFail_[i].strNumResult; } } sendBestData(this->vecContainerFail_[iIndex]); } // 记录第二箱子 this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->sendBestData(selectBestData); } } else { // 筛选不满足校验的部分 取最长的发送 int iSize = 0, iIndex = 0; std::string strContainer_tmp; for (int i = 0; i < this->vecContainerFail_.size(); i++) { this->vecContainerFail_[i]; if (iSize < this->vecContainerFail_[i].strNumResult.size()) { iSize = this->vecContainerFail_[i].strNumResult.size(); iIndex = i; strContainer_tmp = this->vecContainerFail_[i].strNumResult; } } if (this->vecContainerFail_.size() > 0 && this->iContainerIndex < 1) { sendBestData(this->vecContainerFail_[iIndex]); } // 把之前的清理掉再存新箱子的 (有风险,有可能切分错) this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->vecContainerFail_.emplace_back(selectBestData); } } else {// 不满足像素差 if (!this->stdContainerResult_.empty()) { if (step2ResultData.transInfo.bIsChkFlag) { if (this->stdContainerResult_ != step2ResultData.transInfo.strTmpResult) { // if (vCompare(this->stdContainerResult_, step2ResultData.transInfo.strTmpResult) > 2) // { this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->sendBestData(selectBestData); // } } } } else { if (step2ResultData.transInfo.bIsChkFlag) { this->stdContainerResult_ = step2ResultData.transInfo.strTmpResult; this->vecContainerFail_.clear(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->sendBestData(selectBestData); } else { VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); this->vecContainerFail_.emplace_back(selectBestData); } } } } bool ContainerDivideEngine::setectBestByCorner() { if (!this->vecContainer_.empty()) { // 汇总下vecContainer中,有几种正确的识别结果,选出识别次数最多的 std::map mapContainer_count; std::map mapContainer_index; int i = 0; for (auto &container: this->vecContainer_) { mapContainer_count[container.strNumResult]++; mapContainer_index[container.strNumResult] = i; ++i; } std::string strBestContainer; int count = 0; // 取出现次数最高的 且与第一个箱子不一样的 for (auto &it_max: mapContainer_count) { if (it_max.first.empty()) continue; if (it_max.second >= count) { count = it_max.second; strBestContainer = it_max.first; } } this->sendBestData(this->vecContainer_[mapContainer_index[strBestContainer]]); this->vecContainer_.clear(); this->vecContainerFail_.clear(); } else { if (this->vecContainerFail_.empty()) return false; // 汇总下vecContainer中,有几种正确的识别结果,选出识别次数最多的 int iMaxSize = 0, iIndex = 0; std::map mapContainer_index; int i = 0; for (auto &container: this->vecContainerFail_) { if (container.strNumResult.size() > iMaxSize) { iMaxSize = container.strNumResult.size(); iIndex = i; } ++i; } this->sendBestData(this->vecContainerFail_[iIndex]); this->vecContainer_.clear(); this->vecContainerFail_.clear(); } return true; } /** * 依据箱角切分 * @param pVStep2OutputData */ void ContainerDivideEngine::divideByCornerInfo(std::shared_ptr pVStep2OutputData) { Step2ResultData step2ResultData = pVStep2OutputData->step2ResultData; if (pVStep2OutputData->vecCornerResultData.size() > 1 && this->iContainerIndex < 1) { if (pVStep2OutputData->vecCornerResultData.size() > 2) { LogWarn << "帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " ---- >>> 检测到超过2个箱角,疑似画面过于倾斜或模型训练不足!"; return; } float fDiffX = std::abs(pVStep2OutputData->vecCornerResultData[0].fLTX - pVStep2OutputData->vecCornerResultData[1].fLTX); float fDiffY = std::abs(pVStep2OutputData->vecCornerResultData[0].fLTY - pVStep2OutputData->vecCornerResultData[1].fLTY); if (fDiffX > this->identifyConfig_.iMaxContainerSpaceX || fDiffY > this->identifyConfig_.iMaxContainerSpaceY) { LogWarn << "帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " ---- >>> 检测到超过2个间隔过远的箱角,疑似画面过于倾斜看到一个箱子的多个角或模型训练不足!"; return; } LogInfo << "帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " ---- >>> 检测到多个箱角,准备切分"; if (pVStep2OutputData->iDataSource == 0 || this->dataSourceConfig_.strRunDirection == "right") { if (this->vecContainerFail_.empty()) { VSelectBestData selectBestData; selectBestData.strDetectDate = pVStep2OutputData->strDetectDate; selectBestData.strDetectTime = pVStep2OutputData->strDetectTime; selectBestData.iDataSource = pVStep2OutputData->iDataSource; selectBestData.iFrameId = 1; selectBestData.strNumResult = "invalid-?"; selectBestData.iContainerIndex = this->iContainerIndex; selectBestData.bHaveTwoContainer = true; this->vecContainerFail_.emplace_back(selectBestData); } this->setectBestByCorner(); VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); if (step2ResultData.transInfo.bIsChkFlag) { this->vecContainer_.emplace_back(selectBestData); } else { this->vecContainerFail_.emplace_back(selectBestData); } return; } if (this->dataSourceConfig_.strRunDirection == "left") { LogWarn << "向左行驶的一侧暂不支持箱角识别,请及时修改参数 divide_mode: \"pixel\""; return; } } else { VSelectBestData selectBestData; this->makeResult(pVStep2OutputData, selectBestData); if (step2ResultData.transInfo.bIsChkFlag) { this->vecContainer_.emplace_back(selectBestData); } else { this->vecContainerFail_.emplace_back(selectBestData); } } } /** * 切分车厢 * @param pVStep2OutputData */ void ContainerDivideEngine::divideInfo(std::shared_ptr pVStep2OutputData) { // if (!this->pVStep2OutputDataPre_) return; if (this->iContainerIndex > 1) return; if (this->vecDataSourceConfig_[pVStep2OutputData->iDataSource].strDivideModel == "corner") { this->divideByCornerInfo(pVStep2OutputData); } else { this->divideByPixelInfo(pVStep2OutputData); } } APP_ERROR ContainerDivideEngine::Process() { int iRet = APP_ERR_OK; while (!isStop_) { //pop端口0 std::shared_ptr pVoidData0 = nullptr; iRet = inputQueMap_[strPort0_]->pop(pVoidData0); if (nullptr == pVoidData0) { usleep(1000); continue; } std::shared_ptr pVStep2OutputData = std::static_pointer_cast(pVoidData0); if (pVStep2OutputData->bIsEnd) { if (this->bLastIsEnd == pVStep2OutputData->bIsEnd) continue; std::shared_ptr pVSelectBestData = std::make_shared(); pVSelectBestData->bIsEnd = true; pVSelectBestData->iDataSource = pVStep2OutputData->iDataSource; pVSelectBestData->strDetectDate = pVStep2OutputData->strDetectDate; pVSelectBestData->strDetectTime = pVStep2OutputData->strDetectTime; if (this->iContainerIndex > 1) { outputQueMap_[strPort0_]->push(std::static_pointer_cast(pVSelectBestData), true); this->initParam(); continue; } if (this->vecDataSourceConfig_[pVStep2OutputData->iDataSource].strDivideModel == "corner") { if (!this->vecContainerFail_.empty() || !this->vecContainer_.empty()) { this->setectBestByCorner(); } } else { if (!this->vecContainerFail_.empty()) { // 筛选不满足校验的部分 取最长的发送 int iSize = 0, iIndex = 0; std::string strContainer_tmp; for (int i = 0; i < this->vecContainerFail_.size(); i++) { this->vecContainerFail_[i]; if (iSize < this->vecContainerFail_[i].strNumResult.size()) { iSize = this->vecContainerFail_[i].strNumResult.size(); iIndex = i; strContainer_tmp = this->vecContainerFail_[i].strNumResult; } } this->vecContainerFail_[iIndex].bIsEnd = true; sendBestData(this->vecContainerFail_[iIndex]); this->initParam(); continue; } } outputQueMap_[strPort0_]->push(std::static_pointer_cast(pVSelectBestData), true); this->initParam(); continue; } this->bLastIsEnd = false; std::string strFilePath ; strFilePath = this->baseConfig_.strDebugResultPath + "/" + pVStep2OutputData->strDetectDate + "/" + StringUtil::getins()->replace_all_distinct(pVStep2OutputData->strDetectTime, ":", "-") + "/" + std::to_string(pVStep2OutputData->iFrameId) + "_" + std::to_string(pVStep2OutputData->iDataSource) + ".json"; // 先读取文本内容,追加新的信息后再写入 Json::Value jvFrameInfo; if (!FileUtil::getins()->readJsonInfo(jvFrameInfo, strFilePath)) { LogError << "read fail:" << strFilePath; } // jvFrameInfo["isEnd"] = (pVStep2OutputData->bIsEnd || jvFrameInfo["isEnd"].asBool()); // 识别结果存储 float fCenter = pVStep2OutputData->step2ResultData.fLTX + (pVStep2OutputData->step2ResultData.fRBX - pVStep2OutputData->step2ResultData.fLTX) / 2; // this->mapContainerCenterInfo_.insert(std::make_pair(pVStep2OutputData->iFrameId, fCenter)); Json::Value jvInfo; jvInfo["classid"] = pVStep2OutputData->step2ResultData.iClassId; jvInfo["score"] = pVStep2OutputData->step2ResultData.fScore; jvInfo["ltx"] = pVStep2OutputData->step2ResultData.fLTX; jvInfo["lty"] = pVStep2OutputData->step2ResultData.fLTY; jvInfo["rbx"] = pVStep2OutputData->step2ResultData.fRBX; jvInfo["rby"] = pVStep2OutputData->step2ResultData.fRBY; jvFrameInfo["step1"].append(jvInfo); for (auto &step2ResultData : pVStep2OutputData->vecCornerResultData) { float fCenter = step2ResultData.fLTX + (step2ResultData.fRBX - step2ResultData.fLTX) / 2; // this->mapContainerCenterInfo_.insert(std::make_pair(pVStep2OutputData->iFrameId, fCenter)); Json::Value jvInfo; jvInfo["classid"] = step2ResultData.iClassId; jvInfo["score"] = step2ResultData.fScore; jvInfo["ltx"] = step2ResultData.fLTX; jvInfo["lty"] = step2ResultData.fLTY; jvInfo["rbx"] = step2ResultData.fRBX; jvInfo["rby"] = step2ResultData.fRBY; jvFrameInfo["step1"].append(jvInfo); } FileUtil::getins()->writeJsonInfo(jvFrameInfo, strFilePath); if (pVStep2OutputData->step2ResultData.fScore != 0.0f) { LogDebug << " 帧:" << pVStep2OutputData->iFrameId << " 数据源:" << pVStep2OutputData->iDataSource << " - "<< pVStep2OutputData->step2ResultData.transInfo.strTmpResult; } this->divideInfo(pVStep2OutputData); } return APP_ERR_OK; } \ No newline at end of file diff --git a/engine/DetectDivideEngine/ContainerDivideEngine.h b/engine/DetectDivideEngine/ContainerDivideEngine.h index 3756177..7f6dfe9 100644 --- a/engine/DetectDivideEngine/ContainerDivideEngine.h +++ b/engine/DetectDivideEngine/ContainerDivideEngine.h @@ -51,6 +51,8 @@ private: std::vector vecContainerFail_; // 存储识别到的满足校验规则的箱号数据 std::vector vecContainer_; + + bool bLastIsEnd = true; }; ENGINE_REGIST(ContainerDivideEngine)