#include "AbandObjApp.h" #include "DetUtils.h" NAMESPACE_MAS_BEGIN NAMESPACE_ABANDOBJ_BEGIN tzc::Mutex AbandObjApp::_mutex; AbandObjApp* AbandObjApp::_instance = nullptr; AbandObjApp* AbandObjApp::Instance() { if (!_instance) { _mutex.Lock(); if (!_instance) { _instance = new AbandObjApp(); TZLogInfo("AbandObjApp Instance created~~~"); } _mutex.Unlock(); } ++(_instance->m_usecnt); return _instance; } void AbandObjApp::DestroyInstance() { tzc::ScopedLock lock(_mutex); if ((_instance->m_usecnt) < 0) { TZLogWarn("AbandObjApp Instance does not exist!!!"); _instance->m_usecnt = 0; } else if ((_instance->m_usecnt) == 0) { TZ_delete(_instance); TZLogInfo("AbandObjApp Instance destroyed~~~"); } else { TZLogWarn("AbandObjApp still in use by %d objects!", _instance->m_usecnt); TZ_delete(_instance); TZLogInfo("AbandObjApp Instance destroyed~~~"); } } TZ_INT AbandObjApp::Initialize(const std::string& initParam) { if (m_inited) { TZLogInfo("AbandObjApp has been initialized~~~"); return MEC_OK; } AbandObjBuild buildParam; AbandObjBuild::fromJson(initParam, buildParam); m_factor = buildParam.factor; m_shortTermRate = buildParam.short_term_rate; m_shortTermHistory = buildParam.short_term_history; m_longTermRate = buildParam.long_term_rate; m_longTermHistory = buildParam.long_term_history; m_varThreshold = buildParam.var_threshold; m_detectShadows = buildParam.detect_shadows; m_iouThreshold = buildParam.iou_threshold; m_areaThreshold = buildParam.area_threshold; m_perimeterThreshold = buildParam.perimeter_threshold; if (buildParam.algo == "MOG2") { m_pLongBG = cv::createBackgroundSubtractorMOG2(m_longTermHistory, m_varThreshold, m_detectShadows); m_pShortBG = cv::createBackgroundSubtractorMOG2(m_shortTermHistory, m_varThreshold, m_detectShadows); } else { TZLogError("Invalid algorithm name!!!"); return MEC_FAILED; } m_inited = TRUE; TZLogInfo("AbandObjApp Initialized~~~"); return MEC_OK; } TZ_INT AbandObjApp::SetDetectCfg(const std::string& initParam) { AbandObjCfg cfgParam; AbandObjCfg::fromJson(initParam, cfgParam); m_factor = cfgParam.factor; m_shortTermRate = cfgParam.short_term_rate; m_shortTermHistory = cfgParam.short_term_history; m_longTermRate = cfgParam.long_term_rate; m_longTermHistory = cfgParam.long_term_history; m_varThreshold = cfgParam.var_threshold; m_detectShadows = cfgParam.detect_shadows; m_iouThreshold = cfgParam.iou_threshold; m_areaThreshold = cfgParam.area_threshold; m_perimeterThreshold = cfgParam.perimeter_threshold; if (cfgParam.algo == "MOG2") { m_pLongBG = cv::createBackgroundSubtractorMOG2(m_longTermHistory, m_varThreshold, m_detectShadows); m_pShortBG = cv::createBackgroundSubtractorMOG2(m_shortTermHistory, m_varThreshold, m_detectShadows); } else { TZLogError("Invalid algorithm name!!!"); return MEC_FAILED; } return MEC_OK; } TZ_INT AbandObjApp::Dispose() { if (!m_inited) { return MEC_NOT_INITED; } _mutex.Lock(); --(m_usecnt); TZLogInfo("AbandObjApp usecnt -1, now = %d~~~", m_usecnt); TZ_BOOL empty = (m_usecnt == 0); _mutex.Unlock(); if (empty) { m_inited = FALSE; TZLogInfo("AbandObjApp Dispose~~~"); this->DestroyInstance(); } return MEC_OK; } TZ_INT AbandObjApp::DoDetect(cv::Mat& input, std::vector& propRes) { cv::Mat image; cv::resize(input, image, cv::Size(input.cols / m_factor, input.rows / m_factor)); cv::Mat ltMask, stMask, subMask; // 高斯模糊 cv::GaussianBlur(image, image, cv::Size(5, 5), 0); // 转换为灰度图 cv::cvtColor(image, image, cv::COLOR_BGR2GRAY); // 更新背景模型并获取前景掩码 m_pLongBG->apply(image, ltMask, m_longTermRate); m_pShortBG->apply(image, stMask, m_shortTermRate); // 计算长期掩码减去短期掩码,得到差分掩码 cv::subtract(ltMask, stMask, subMask); // 对差分掩码进行去噪处理 // 先腐蚀再膨胀(开操作) cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5)); cv::erode(subMask, subMask, kernel, cv::Point(-1, -1), 2, 1, 1); kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3)); cv::dilate(subMask, subMask, cv::Mat(), cv::Point(-1, -1), 2, 1, 1); // 高斯模糊进一步平滑掩码 cv::GaussianBlur(subMask, subMask, cv::Size(49, 49), 7); // 将低于阈值的像素设为0,进一步清理噪点 subMask.setTo(0, subMask <= 5); // 查找轮廓 std::vector> contours; cv::findContours(subMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // 遍历所有轮廓 for (size_t i = 0; i < contours.size(); i++) { double area = cv::contourArea(contours[i]); double perimeter = cv::arcLength(contours[i], true); // 过滤掉面积或周长过小的轮廓 if (area > m_areaThreshold && perimeter > m_perimeterThreshold) { // 绘制轮廓 cv::drawContours(image, contours, i, cv::Scalar(0, 0, 255), 2); // 获取轮廓的边界矩形 cv::Rect rect = cv::boundingRect(contours[i]); // 绘制矩形 cv::rectangle(image, rect, cv::Scalar(0, 255, 0), 2); // 创建proposal对象 Proposal p; p.x = rect.x * m_factor; // 恢复到原始尺寸 p.y = rect.y * m_factor; p.w = rect.width * m_factor; p.h = rect.height * m_factor; p.life = 10; // 初始生命周期 p.status = Active; // 初始状态 // 检查当前proposal是否与已有proposal重叠 for (size_t j = 0; j < m_proposals.size(); j++) { AreaBox boxA, boxB; boxA.LTX = p.x; boxA.LTY = p.y; boxA.RBX = p.x + p.w; boxA.RBY = p.y + p.h; boxB.LTX = m_proposals[j].x; boxB.LTY = m_proposals[j].y; boxB.RBX = m_proposals[j].x + m_proposals[j].w; boxB.RBY = m_proposals[j].y + m_proposals[j].h; if (!detutils::IsIoULeqThreshold(boxA, boxB, m_iouThreshold)) { p.status = Overlap; // 重叠,不添加新的proposal m_proposals[j].life++; // 延长已有proposal的生命周期 if (m_proposals[j].status == Accepted){ continue; // 已经进行过检测 } m_proposals[j].status = Active; // 标记为需要检测的有效proposal } } // 如果没有与任何proposal重叠,则添加为新的proposal if (p.status == Active){ m_proposals.push_back(p); } } } // 遍历所有proposal,更新其状态 for (size_t i = 0; i < m_proposals.size(); i++) { if (m_proposals[i].status == Active) { // 需要检测的proposal if (m_proposals[i].life > 60) { // 生存超过60帧 // 处理proposal区域 //Mat roi = solve_proposal(origin_frame, proposals[i]); // TODO: 对roi进行CLIP视觉特征提取和结果保存 propRes.push_back(m_proposals[i]); m_proposals[i].status = Accepted; // 标记为已检测 } } else { // 减少proposal的生命周期 m_proposals[i].life--; if (m_proposals[i].life == 0) { // 生命周期结束,删除proposal m_proposals.erase(m_proposals.begin() + i); i--; // 调整索引 } } } return MEC_OK; } AbandObjApp::AbandObjApp() : m_inited(FALSE), m_usecnt(0) {} AbandObjApp::~AbandObjApp() { this->Dispose(); } NAMESPACE_ABANDOBJ_END NAMESPACE_MAS_END