#include "TrainDivideEngine.h" using namespace ai_matrix; TrainDivideEngine::TrainDivideEngine() {} TrainDivideEngine::~TrainDivideEngine() {} APP_ERROR TrainDivideEngine::Init() { strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0"; strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1"; this->baseConfig_ = Config::getins()->getBaseConfig(); this->identifyConfig_ = Config::getins()->getIdentifyConfig(); this->dataSourceConfig_ = Config::getins()->getDataSourceConfig(); InitParam(); LogInfo << "TrainDivideEngine Init ok"; return APP_ERR_OK; } APP_ERROR TrainDivideEngine::DeInit() { LogInfo << "TrainDivideEngine DeInit ok"; return APP_ERR_OK; } /** * 初始化参数信息 * inParam : N/A * outParam: N/A * return : N/A */ void TrainDivideEngine::InitParam() { iPushSpaceFrameId_ = 0; i64TimeStampFirst_ = 0; bPushIsEnd_ = false; vecParationInfo_.clear(); std::vector().swap(vecParationInfo_); bDealCenterFlag_ = false; parationInfoLast_.reset(); mapNumCenterInfo_.clear(); mapProCenterInfo_.clear(); bHaveHeadFlag_ = false; headInfo_.iFrameId = 0; headInfo_.fCenterX = 0; this->iTrainIndex = 0; } /** * 构造车厢间隔信息 * inParam : N/A * outParam: N/A * return : N/A */ void TrainDivideEngine::makeParationInfo(PartionInfo ¶tionInfo, std::shared_ptr pInferenceResultData, SingleData &singleData) { parationInfo.iDataSource = pInferenceResultData->iDataSource; parationInfo.iSpaceFrame = pInferenceResultData->iFrameId; parationInfo.iCenterX = singleData.fLTX + (singleData.fRBX - singleData.fLTX) / 2; parationInfo.strTrainDate = pInferenceResultData->strTrainDate; parationInfo.strTrainTime = pInferenceResultData->strTrainTime; parationInfo.strTrainName = pInferenceResultData->strTrainName; parationInfo.bIsEnd = pInferenceResultData->bIsEnd; parationInfoLast_ = parationInfo; } /** * 处理中心间隔信息 * inParam : N/A * outParam: N/A * return : N/A */ void TrainDivideEngine::dealCenterSpace(std::vector &vecParationInfo, std::shared_ptr pInferenceResultData) { int iVecSize = vecParationInfo.size(); if (iVecSize < 0) { return; } LogDebug << "积累的车厢切分信息数:" << iVecSize << " 当前帧:" << pInferenceResultData->iFrameId << " 第一个车厢切分帧:" << vecParationInfo.at(0).iSpaceFrame << " 最后一个车厢切分帧:" << vecParationInfo.at(iVecSize - 1).iSpaceFrame << " 最后一个车厢切分帧是否为结束:" << vecParationInfo.at(iVecSize - 1).bIsEnd; /* 因停车后再行驶未能及时判断出为行驶状态,导致更新间隔信息,出现漏切分车厢 漏切分时vecParationInfo中保存的是两辆车的间隔信息,因此针对vecParationInfo做特殊处理,从此处切分出遗漏的车厢。 */ std::vector vecSpacePos; for (int i = 1; i < iVecSize; i++) { bool bIntervalFlag = (vecParationInfo[i].iSpaceFrame - vecParationInfo[i - 1].iSpaceFrame) > this->identifyConfig_.iPartitionFrameSpan; LogDebug << "上一帧ID:" << vecParationInfo[i - 1].iSpaceFrame << " 上一帧间隔X轴中线:" << vecParationInfo[i - 1].iCenterX << " 本帧ID:" << vecParationInfo[i].iSpaceFrame << " 本帧间隔X轴中线:" << vecParationInfo[i].iCenterX << " 满足帧间隔:" << bIntervalFlag << " 累计处理计数i:" << i; if (bIntervalFlag && ( (pInferenceResultData->iDirection == DIRECTION_LEFT && vecParationInfo[i - 1].iCenterX < vecParationInfo[i].iCenterX - this->identifyConfig_.iSplitFrameSpanPx) || (pInferenceResultData->iDirection == DIRECTION_RIGHT && vecParationInfo[i - 1].iCenterX - this->identifyConfig_.iSplitFrameSpanPx > vecParationInfo[i].iCenterX) )) { vecSpacePos.push_back(i - 1); } } vecSpacePos.push_back(iVecSize - 1); /* 如果集合中最后为列车结束帧,则表示停车在车厢间隔。这时用最后一个作为车厢划分帧。 其他场景使用靠近中心的间隔帧作为车厢划分帧 */ for (int i = 0; i < vecSpacePos.size(); i++) { PartionInfo partionInfo; if (i == vecSpacePos.size() - 1 && vecParationInfo[vecSpacePos.at(i)].bIsEnd) { partionInfo = vecParationInfo[vecSpacePos.at(i)]; } else { int iPos = 0; int iImageCenter = IMAGE_WIDTH / 2; int iToCenterXMin = iImageCenter; int iBegin = (i == 0 ? 0 : vecSpacePos.at(i - 1) + 1); for (int j = iBegin; j <= vecSpacePos.at(i); j++) { if (iToCenterXMin > abs(vecParationInfo[j].iCenterX - iImageCenter)) { iToCenterXMin = abs(vecParationInfo[j].iCenterX - iImageCenter); iPos = j; } } partionInfo = vecParationInfo[iPos]; } //此处切分时,依据车号,属性判断是否有漏的车厢,如果有则依靠车号属性切分。 this->splitTrainByNumPro(partionInfo, pInferenceResultData); this->divideTrain(partionInfo); this->iPushSpaceFrameId_ = partionInfo.iSpaceFrame; this->bPushIsEnd_ = partionInfo.bIsEnd; // LogDebug // << "pushSpaceFrameId:" << this->iPushSpaceFrameId_ // << " bPushIsEnd:" << this->bPushIsEnd_; } vecParationInfo.clear(); } /** * 处理车厢间隔 * inParam : N/A * outParam: N/A * return : N/A */ void TrainDivideEngine::dealTrainSpaceInfo(std::shared_ptr pInferenceResultData, SingleData &singleData) { /* 无方向时,识别的内容都放入集合中。 有向时,按方向判断是否push车厢间隔框 向左行驶:帧间隔中心点小于画面1/3时,则处理车厢间隔集合。新车厢间隔必须大于上次车厢中心点再放入集合等待处理。 向右行驶:帧间隔中心点大于画面2/3时,则处理车厢间隔集合。新车厢间隔必须小于上次车厢中心点再放入集合等待处理。 注:最后一节不会再有帧间隔了,因此直接发送的结束帧,且坐标设置中心点。 */ // 没有识别到间隔时 return if ((singleData.iTargetType != SPACE && singleData.iTargetType != TRAINSPACE) || pInferenceResultData->iFrameId < 30) { return; } // 停车状态且有间隔时需更新最后间隔框帧号。,防止长时间停在间隔上导致集合持续增大。 if (pInferenceResultData->iTrainStatus == TRAINSTATUS_STOP && !pInferenceResultData->bIsEnd) { this->parationInfoLast_.iSpaceFrame = pInferenceResultData->iFrameId; return; } bool bIntervalFlag = (pInferenceResultData->iFrameId - this->parationInfoLast_.iSpaceFrame) > this->identifyConfig_.iPartitionFrameSpan; int iCenterCur = singleData.fLTX + (singleData.fRBX - singleData.fLTX) / 2; LogDebug << "当前帧:" << pInferenceResultData->iFrameId << " 间隔框中心线:" << iCenterCur << " 上一帧:" << this->parationInfoLast_.iSpaceFrame << " 间隔框中心线:" << this->parationInfoLast_.iCenterX << " 行车方向:" << pInferenceResultData->iDirection << " 是否满足切分帧数:" << bIntervalFlag << " bDealCenterFlag_:" << this->bDealCenterFlag_; if (pInferenceResultData->iDirection == DIRECTION_UNKNOWN || this->parationInfoLast_.iCenterX == 0) { PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); this->vecParationInfo_.push_back(parationInfo); } else if (pInferenceResultData->iDirection == DIRECTION_LEFT) { if (iCenterCur < (IMAGE_WIDTH / 3)) { PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); if (!this->bDealCenterFlag_) { this->vecParationInfo_.push_back(parationInfo); this->dealCenterSpace(this->vecParationInfo_, pInferenceResultData); this->bDealCenterFlag_ = true; } } else if ((this->parationInfoLast_.iCenterX < iCenterCur - this->identifyConfig_.iSplitFrameSpanPx) && bIntervalFlag) //该条件只会在新车间隔出来进入一次 { // 防止上节车厢间隔框所有识别都大于画面的1/3,因此当前车厢间隔出来后也需处理下上节车厢间隔 if (!this->bDealCenterFlag_ && this->vecParationInfo_.size() > 0) { this->dealCenterSpace(this->vecParationInfo_, pInferenceResultData); } PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); this->vecParationInfo_.push_back(parationInfo); this->bDealCenterFlag_ = false; } else { PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); //该条件是防止第一个XXX帧满足小于画面1/3后切割,后一帧XXX+1的中心点大于画面1/3导致的加入vec中出现的多切分现象。(向右增加30px的浮动,因为大框可能不是同一种) if (!(this->bDealCenterFlag_ && !bIntervalFlag && (iCenterCur < (IMAGE_WIDTH / 3 + 80)))) { this->vecParationInfo_.push_back(parationInfo); } } } else if (pInferenceResultData->iDirection == DIRECTION_RIGHT) { if (iCenterCur > (IMAGE_WIDTH / 3 * 2)) { PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); if (!this->bDealCenterFlag_) { this->vecParationInfo_.push_back(parationInfo); this->dealCenterSpace(this->vecParationInfo_, pInferenceResultData); this->bDealCenterFlag_ = true; } } else if ((this->parationInfoLast_.iCenterX - this->identifyConfig_.iSplitFrameSpanPx > iCenterCur) && bIntervalFlag) //该条件只会在新车间隔出来进入一次 { //防止上节车厢间隔所有识别框都小于画面的2/3,因此当前车厢间隔出来后也需处理下上节车厢间隔 if (!this->bDealCenterFlag_ && this->vecParationInfo_.size() > 0) { this->dealCenterSpace(this->vecParationInfo_, pInferenceResultData); } PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); this->vecParationInfo_.push_back(parationInfo); this->bDealCenterFlag_ = false; } else { PartionInfo parationInfo; this->makeParationInfo(parationInfo, pInferenceResultData, singleData); //该条件是防止第一个XXX帧满足大于画面2/3后切割,后一帧XXX+1的中心点小于画面2/3导致的加入vec中出现的多切分现象。(向左增加80px的浮动因为大框可能不是同一种) if (!(this->bDealCenterFlag_ && !bIntervalFlag && (iCenterCur > (IMAGE_WIDTH / 3 * 2 - 80)))) { this->vecParationInfo_.push_back(parationInfo); } } } } void TrainDivideEngine::splitTrainByNumPro(PartionInfo &partionInfo, std::shared_ptr &pInferenceResultData) { //向左行驶用车号位置,向右行驶用属性位置。 std::map *pMapCenterInfoTemp_ = nullptr; if (pInferenceResultData->iDirection == DIRECTION_LEFT) { pMapCenterInfoTemp_ = &mapNumCenterInfo_; mapProCenterInfo_.clear(); } else if (pInferenceResultData->iDirection == DIRECTION_RIGHT) { pMapCenterInfoTemp_ = &mapProCenterInfo_; mapNumCenterInfo_.clear(); } if (pMapCenterInfoTemp_ == nullptr || pMapCenterInfoTemp_->size() <= 0) { return; } auto iter = pMapCenterInfoTemp_->begin(); int iCenterXPre = iter->second; uint32_t iFrameIdPre = iter->first; bool bFlag = false; uint32_t iSplitFrameId = 0; while (++iter != pMapCenterInfoTemp_->end()) { bool bIntervalFlag = (iter->first - iFrameIdPre) > this->identifyConfig_.iPartitionFrameSpan; // LogDebug // << "iFrameIdPre:" << iFrameIdPre // << " iCenterXPre:" << iCenterXPre // << " iFrameid:" << iter->first // << " iCenter:" << iter->second // << " bIntervalFlag:" << bIntervalFlag; if (bIntervalFlag && ( (pInferenceResultData->iDirection == DIRECTION_LEFT && iCenterXPre < iter->second - this->identifyConfig_.iSplitFrameSpanPx) || (pInferenceResultData->iDirection == DIRECTION_RIGHT && iCenterXPre - this->identifyConfig_.iSplitFrameSpanPx > iter->second) )) { iSplitFrameId = iter->first; bFlag = true; } //比较完后,可更新前一帧数据 iCenterXPre = iter->second; iFrameIdPre = iter->first; if(bFlag) { if ( (pInferenceResultData->iDirection == DIRECTION_LEFT && iter->second < (IMAGE_WIDTH / 3)) || (pInferenceResultData->iDirection == DIRECTION_RIGHT && iter->second > (IMAGE_WIDTH / 3 * 2)) ) { bFlag = false; } if (!bFlag) { PartionInfo parationInfo_new; parationInfo_new.iDataSource = pInferenceResultData->iDataSource; parationInfo_new.iSpaceFrame = iSplitFrameId; parationInfo_new.strTrainDate = pInferenceResultData->strTrainDate; parationInfo_new.strTrainTime = pInferenceResultData->strTrainTime; parationInfo_new.strTrainName = pInferenceResultData->strTrainName; parationInfo_new.iEndframe = iSplitFrameId; parationInfo_new.bIsEnd = false; //**通过该函数切分的肯定不是最后一节 parationInfo_new.bSpaceDivide = false; //构造一个间隔信息写入到切分帧中 std::string strFilePath; strFilePath = this->baseConfig_.strDebugResultPath + "/" + pInferenceResultData->strTrainDate + "/" + pInferenceResultData->strTrainName + "/" + "jpg_" + std::to_string(pInferenceResultData->iDataSource) + "/" + std::to_string(iSplitFrameId) + ".json"; Json::Value jvFrameInfo; FileUtil::getins()->readJsonInfo(jvFrameInfo, strFilePath); Json::Value jvOneSpace; jvOneSpace["target_type"] = 5; jvOneSpace["classid"] = 18; jvOneSpace["ltx"] = pMapCenterInfoTemp_->at(iSplitFrameId) + (pInferenceResultData->iDirection == DIRECTION_LEFT ? -50 : 50); jvOneSpace["lty"] = 0; jvOneSpace["rbx"] = pMapCenterInfoTemp_->at(iSplitFrameId) + (pInferenceResultData->iDirection == DIRECTION_LEFT ? -50 : 50); jvOneSpace["rby"] = 0; jvFrameInfo["step1"].append(jvOneSpace); FileUtil::getins()->writeJsonInfo(jvFrameInfo, strFilePath); this->divideTrain(parationInfo_new); iPushSpaceFrameId_ = parationInfo_new.iSpaceFrame; bPushIsEnd_ = parationInfo_new.bIsEnd; LogDebug << " pushSpaceFrameId:" << iPushSpaceFrameId_ << " bPushIsEnd:" << bPushIsEnd_; while (pMapCenterInfoTemp_->size() > 0) { auto iterDel = pMapCenterInfoTemp_->begin(); if(iterDel->first > iPushSpaceFrameId_) { break; } LogDebug << "erase iFrameId:" << iterDel->first; pMapCenterInfoTemp_->erase(iterDel); } } } if (iter->first >= partionInfo.iSpaceFrame) { LogDebug << "frameid:" << iter->first << " >= pPartionInfo->iSpaceFrame:" << partionInfo.iSpaceFrame << " break"; break; } } while (pMapCenterInfoTemp_->size() > 0) { auto iterDel = pMapCenterInfoTemp_->begin(); if (iterDel->first > partionInfo.iSpaceFrame) { break; } // LogDebug << "erase iFrameId:" << iterDel->first; pMapCenterInfoTemp_->erase(iterDel); } } void TrainDivideEngine::divideTrain(PartionInfo &partionInfo) { partionInfo.iStartframe = this->vecTrainDivideInfo.empty() ? 1 : this->vecTrainDivideInfo.back().iSpaceFrame; partionInfo.iEndframe = partionInfo.iSpaceFrame; this->vecTrainDivideInfo.emplace_back(partionInfo); this->iTrainIndex++; std::string strFilePath; //检测到车厢划分信息 strFilePath = this->baseConfig_.strDebugResultPath + "/" + partionInfo.strTrainDate + "/" + partionInfo.strTrainName + "/"; if (partionInfo.iDataSource != 0) { strFilePath += "secondary_"; } strFilePath += std::to_string(this->vecTrainDivideInfo.size()) + ".json"; Json::Value jvPartionInfo; jvPartionInfo["trainIndex"] = this->vecTrainDivideInfo.size(); jvPartionInfo["startFrame"] = partionInfo.iStartframe; jvPartionInfo["endFrame"] = partionInfo.iEndframe; jvPartionInfo["spaceDivide"] = partionInfo.bSpaceDivide; partionInfo.iTrainIndex = this->vecTrainDivideInfo.size(); FileUtil::getins()->writeJsonInfo(jvPartionInfo, strFilePath); std::shared_ptr pPartionInfo = std::make_shared(); *pPartionInfo = partionInfo; LogInfo << "--------- 数据源:" << partionInfo.iDataSource << " 第" << this->vecTrainDivideInfo.size() << "节,车厢切分 --------"; LogDebug << "开始帧:" << partionInfo.iStartframe; LogDebug << "结束帧:" << partionInfo.iEndframe; outputQueMap_[strPort0_]->push(std::static_pointer_cast(pPartionInfo), true); if (partionInfo.bIsEnd) { LogInfo << " ---- >>>> 过车结束"; this->vecTrainDivideInfo.clear(); } } APP_ERROR TrainDivideEngine::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 pInferenceResultData = std::static_pointer_cast(pVoidData0); if (pInferenceResultData->bIsEnd) { if (this->vecTrainDivideInfo.empty() && !this->iTrainIndex) continue; //最后一节处理下前一节信息 if (!bDealCenterFlag_ && vecParationInfo_.size() > 0) { LogDebug << "lastFrameid:" << vecParationInfo_[0].iSpaceFrame << " frameid:" << pInferenceResultData->iFrameId; this->dealCenterSpace(vecParationInfo_, pInferenceResultData); } PartionInfo partionInfo; std::shared_ptr pPartionInfo = std::make_shared(); partionInfo.iDataSource = pInferenceResultData->iDataSource; partionInfo.iSpaceFrame = pInferenceResultData->iFrameId; partionInfo.strTrainDate = pInferenceResultData->strTrainDate; partionInfo.strTrainTime = pInferenceResultData->strTrainTime; partionInfo.strTrainName = pInferenceResultData->strTrainName; partionInfo.bIsEnd = pInferenceResultData->bIsEnd; //最后一节和倒数第二节之间的间隔未能识别时,此时也需要通过车号属性切分下。 this->splitTrainByNumPro(partionInfo, pInferenceResultData); this->divideTrain(partionInfo); iPushSpaceFrameId_ = partionInfo.iSpaceFrame; if (!bPushIsEnd_) { bPushIsEnd_ = partionInfo.bIsEnd; // LogDebug << "pushSpaceFrameId:" << iPushSpaceFrameId_ // << " bPushIsEnd:" << bPushIsEnd_; } InitParam(); } std::string strFilePath = ""; strFilePath = this->baseConfig_.strDebugResultPath + "/" + pInferenceResultData->strTrainDate + "/" + pInferenceResultData->strTrainName + "/" + "jpg_" + std::to_string(pInferenceResultData->iDataSource) + "/" + std::to_string(pInferenceResultData->iFrameId) + ".json"; // 先读取文本内容,追加新的信息后再写入 Json::Value jvFrameInfo; if (!FileUtil::getins()->readJsonInfo(jvFrameInfo, strFilePath)) { LogError << "read fail:" << strFilePath; } jvFrameInfo["isEnd"] = (pInferenceResultData->bIsEnd || jvFrameInfo["isEnd"].asBool()); jvFrameInfo["train_status"] = pInferenceResultData->iTrainStatus; jvFrameInfo["direction"] = pInferenceResultData->iDirection; SingleData singleData_sapce; // 遍历识别结果 for (int i = 0; i < pInferenceResultData->vecSingleData.size(); i++) { SingleData singleData = pInferenceResultData->vecSingleData[i]; float fCenter = singleData.fLTX + (singleData.fRBX - singleData.fLTX) / 2; if (pInferenceResultData->iTrainStatus != TRAINSTATUS_STOP) { switch (singleData.iTargetType) { case HEAD: // 车头没有属性,因此车头号也加入到属性中 // 车头只加入一次,防止一个车头2个车头号的场景。但有两个车头且没识别车头间隔则无法处理。 if (!bHaveHeadFlag_) { bool bIntervalFlag = ((pInferenceResultData->iFrameId - headInfo_.iFrameId) > this->identifyConfig_.iPartitionFrameSpan && headInfo_.iFrameId != 0); LogDebug << "车头帧:" << pInferenceResultData->iFrameId << " 中心:" << fCenter << " 上一帧:" << headInfo_.iFrameId << " 上个中心:" << headInfo_.fCenterX << " 是否满足帧跨度:" << bIntervalFlag; if ((bIntervalFlag && abs((int)(headInfo_.fCenterX - fCenter)) > this->identifyConfig_.iSplitFrameSpanPx)) { bHaveHeadFlag_ = true; } else { headInfo_.fCenterX = fCenter; headInfo_.iFrameId = pInferenceResultData->iFrameId; mapNumCenterInfo_.insert(std::make_pair(pInferenceResultData->iFrameId, fCenter)); mapProCenterInfo_.insert(std::make_pair(pInferenceResultData->iFrameId, fCenter)); } } break; case NUM: mapNumCenterInfo_.insert(std::make_pair(pInferenceResultData->iFrameId, fCenter)); break; case PRO: mapProCenterInfo_.insert(std::make_pair(pInferenceResultData->iFrameId, fCenter)); break; case SPACE: singleData_sapce = singleData; break; case TRAINSPACE: if (singleData_sapce.iTargetType != SPACE) { singleData_sapce = singleData; } break; case CONTAINER: break; default: break; } } Json::Value jvInfo; jvInfo["target_type"] = singleData.iTargetType; jvInfo["classid"] = singleData.iClassId; jvInfo["score"] = singleData.fScore; jvInfo["clear"] = singleData.fClear; jvInfo["ltx"] = singleData.fLTX; jvInfo["lty"] = singleData.fLTY; jvInfo["rbx"] = singleData.fRBX; jvInfo["rby"] = singleData.fRBY; if (singleData.iTargetType == singleData_sapce.iTargetType) { jvFrameInfo["divide_space"] = jvInfo; } jvFrameInfo["step1"].append(jvInfo); } FileUtil::getins()->writeJsonInfo(jvFrameInfo, strFilePath); if (pInferenceResultData->vecSingleData.empty()) continue; this->dealTrainSpaceInfo(pInferenceResultData, singleData_sapce); } return APP_ERR_OK; }