|
|
@@ -1,9 +1,17 @@
|
|
|
#include "CAIxuexiPageUI.h"
|
|
|
|
|
|
+#include "../control/ControlEx.h"
|
|
|
+
|
|
|
#include "../wnd/CMainWnd.h"
|
|
|
#include "../worker/CVideoCaptureWorker.h"
|
|
|
|
|
|
-#define WM_TIMER_VIDEO_UPDATE 200002
|
|
|
+#include "../tool/CSqlite3.h"
|
|
|
+
|
|
|
+#include "../wnd/CMainWnd.h"
|
|
|
+
|
|
|
+#include "../ai/YoloFeatureManager.h"
|
|
|
+#include "../ai/ImageProcessor.h"
|
|
|
+#include "../ai/SQLiteVecManager.h"
|
|
|
|
|
|
CAIxuexiPageUI::CAIxuexiPageUI()
|
|
|
{
|
|
|
@@ -25,7 +33,6 @@ CAIxuexiPageUI::~CAIxuexiPageUI()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
void CAIxuexiPageUI::InitShow()
|
|
|
{
|
|
|
if (m_isFirstInit)
|
|
|
@@ -34,18 +41,305 @@ void CAIxuexiPageUI::InitShow()
|
|
|
|
|
|
std::thread(&CAIxuexiPageUI::HandleUpdateVideo, this).detach();
|
|
|
}
|
|
|
+
|
|
|
+ //先初始化商品分类
|
|
|
+ this->InitFoodtypeShow();
|
|
|
+
|
|
|
+ //再初始化商品显示
|
|
|
+ this->InitFoodShow();
|
|
|
+}
|
|
|
+
|
|
|
+void CAIxuexiPageUI::InitFoodtypeShow()
|
|
|
+{
|
|
|
+ //初始化商品渲染相关的信息
|
|
|
+ m_foodtype_mutex.lock();
|
|
|
+
|
|
|
+ m_foodtypeLayout = static_cast<CHorizontalLayoutUI*>(this->FindSubControl(_T("aixuexi_page_fenlei_layout")));
|
|
|
+ m_foodtypeLayout->RemoveAll();
|
|
|
+
|
|
|
+ CSqlite3 sqlite;
|
|
|
+ m_types = sqlite.GetFoodtypes(); //只包含收银机显示的分类
|
|
|
+
|
|
|
+ if (m_types.size() > 0)
|
|
|
+ {
|
|
|
+ m_cur_type_id = m_types[0].id;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_curFoodtypeOption = NULL;
|
|
|
+
|
|
|
+ //接下来开始处理商品分类
|
|
|
+ for (std::vector<CFoodType>::iterator it = m_types.begin(); it != m_types.end(); it++)
|
|
|
+ {
|
|
|
+ CFoodType type = *it;
|
|
|
+
|
|
|
+ CDialogBuilder builder;
|
|
|
+ CDialogBuilderCallbackEx cb;
|
|
|
+
|
|
|
+ CFoodtypeOptionUI* pItem = static_cast<CFoodtypeOptionUI*>(builder.Create(_T("foodtype_option.xml"), (UINT)0, &cb, m_pManager));
|
|
|
+
|
|
|
+ pItem->SetName(CLewaimaiString::UTF8ToUnicode(type.name));
|
|
|
+ pItem->SetTypeId(type.id);
|
|
|
+ pItem->SetGroup(L"aixuexi_page_foodtype");
|
|
|
+
|
|
|
+ m_foodtypeLayout->Add(pItem);
|
|
|
+
|
|
|
+ if (m_cur_type_id == type.id)
|
|
|
+ {
|
|
|
+ m_curFoodtypeOption = pItem;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m_foodtype_mutex.unlock();
|
|
|
+
|
|
|
+ if (m_curFoodtypeOption != NULL)
|
|
|
+ {
|
|
|
+ m_curFoodtypeOption->SetBkColor(0xFF3CB371);
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果不是一个分类都没有,选中第一个分类
|
|
|
+ if (m_cur_type_id != "")
|
|
|
+ {
|
|
|
+ CFoodtypeOptionUI* curTypeUI = static_cast<CFoodtypeOptionUI*>(m_foodtypeLayout->GetItemAt(0));
|
|
|
+
|
|
|
+ curTypeUI->Selected(true, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ UpdateFoodtypePos();
|
|
|
+}
|
|
|
+
|
|
|
+void CAIxuexiPageUI::UpdateFoodtypePos()
|
|
|
+{
|
|
|
+ int nFoodtypeNum = m_types.size();
|
|
|
+
|
|
|
+ //添加支付方式
|
|
|
+ int nWidth = m_nPageWidth;
|
|
|
+ if (nWidth == 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //根据宽度计算每行显示的数量
|
|
|
+ int nMeihangNum = (nWidth - 321) / 140;
|
|
|
+
|
|
|
+ int num = 0;
|
|
|
+
|
|
|
+ m_foodtype_mutex.lock();
|
|
|
+
|
|
|
+ CHorizontalLayoutUI* pFenleiLayout = static_cast<CHorizontalLayoutUI*>(this->FindSubControl(_T("aixuexi_page_fenlei_layout")));
|
|
|
+ for (int i = 0; i < nFoodtypeNum; i++)
|
|
|
+ {
|
|
|
+ CButtonUI* curItem = static_cast<CButtonUI*>(pFenleiLayout->GetItemAt(i));
|
|
|
+
|
|
|
+ int curRow = num / nMeihangNum + 1;
|
|
|
+ int curCol = num % nMeihangNum + 1;
|
|
|
+
|
|
|
+ RECT rect;
|
|
|
+ rect.left = (curCol - 1) * 140 + 10;
|
|
|
+ rect.right = rect.left + 130;
|
|
|
+ rect.top = (curRow - 1) * 52 + 10;
|
|
|
+ rect.bottom = rect.top + 42;
|
|
|
+
|
|
|
+ // 强制设置固定大小和位置
|
|
|
+ SIZE size;
|
|
|
+ size.cx = rect.left;
|
|
|
+ size.cy = rect.top;
|
|
|
+ curItem->SetFixedXY(size);
|
|
|
+
|
|
|
+ curItem->SetFixedWidth(rect.right - rect.left);
|
|
|
+ curItem->SetFixedHeight(rect.bottom - rect.top);
|
|
|
+
|
|
|
+ num++;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_foodtype_mutex.unlock();
|
|
|
+
|
|
|
+ //调整区域高度
|
|
|
+ int lastRow = (num - 1) / nMeihangNum + 1;
|
|
|
+ pFenleiLayout->SetFixedHeight(lastRow * 52 + 10);
|
|
|
+
|
|
|
+ //处理滚动条问题
|
|
|
+ CVerticalLayoutUI* pFenleiScrollLayout = static_cast<CVerticalLayoutUI*>(this->FindSubControl(_T("aixuexi_page_fenlei_layout_scrolllayout")));
|
|
|
+ if (lastRow > 2)
|
|
|
+ {
|
|
|
+ lastRow = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ pFenleiScrollLayout->SetFixedHeight(lastRow * 52 + 10);
|
|
|
+
|
|
|
+ SIZE size;
|
|
|
+ size.cx = 0;
|
|
|
+ size.cy = 0;
|
|
|
+ pFenleiScrollLayout->SetScrollPos(size);
|
|
|
+}
|
|
|
+
|
|
|
+void CAIxuexiPageUI::InitFoodShow()
|
|
|
+{
|
|
|
+ bool is_youtu;
|
|
|
+ if (CSetting::GetInstance()->GetParam("setting_xianshi_is_youtu") == "1")
|
|
|
+ {
|
|
|
+ is_youtu = true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ is_youtu = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_foodLayout = static_cast<CTileLayoutUI*>(this->FindSubControl(_T("aixuexi_page_foodlist")));
|
|
|
+ m_foodLayout->RemoveAll();
|
|
|
+
|
|
|
+ std::wstring xml_name;
|
|
|
+
|
|
|
+ if (is_youtu)
|
|
|
+ {
|
|
|
+ SIZE itemsize;
|
|
|
+ itemsize.cx = 130;
|
|
|
+ itemsize.cy = 220;
|
|
|
+ m_foodLayout->SetItemSize(itemsize);
|
|
|
+
|
|
|
+ xml_name = _T("shangpin_fooditem.xml");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SIZE itemsize;
|
|
|
+ itemsize.cx = 130;
|
|
|
+ itemsize.cy = 90;
|
|
|
+ m_foodLayout->SetItemSize(itemsize);
|
|
|
+
|
|
|
+ xml_name = _T("shangpin_fooditem_wutu.xml");
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果当前一个分类都没有,那么就不处理了
|
|
|
+ if (m_cur_type_id == "")
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if (m_cur_type_id == "sousuo")
|
|
|
+ {
|
|
|
+ if (CLewaimaiString::is_only_number(m_sousuo_foodname))
|
|
|
+ {
|
|
|
+ //说明是纯数字,按商品条码来搜索
|
|
|
+ CSqlite3 sqlite;
|
|
|
+ CFood food;
|
|
|
+
|
|
|
+ bool ret = sqlite.GetFoodByBarcode(m_sousuo_foodname, food);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ {
|
|
|
+ CDialogBuilder builder;
|
|
|
+ CDialogBuilderCallbackEx cb;
|
|
|
+
|
|
|
+ CShangpinFoodItemUI* pItem = static_cast<CShangpinFoodItemUI*>(builder.Create(xml_name.c_str(), (UINT)0, &cb, m_pManager));
|
|
|
+
|
|
|
+ pItem->SetYoutuModel(is_youtu);
|
|
|
+ pItem->SetFoodInfo(food);
|
|
|
+ pItem->UpdateShow();
|
|
|
+
|
|
|
+ m_foodLayout->Add(pItem);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //当商品名字来搜索
|
|
|
+ CSqlite3 sqlite;
|
|
|
+ std::vector<CFood> foodlist = sqlite.GetFoodByFoodname(m_sousuo_foodname);
|
|
|
+
|
|
|
+ for (std::vector<CFood>::iterator it = foodlist.begin(); it != foodlist.end(); it++)
|
|
|
+ {
|
|
|
+ CFood food = *it;
|
|
|
+
|
|
|
+ CDialogBuilder builder;
|
|
|
+ CDialogBuilderCallbackEx cb;
|
|
|
+
|
|
|
+ CShangpinFoodItemUI* pItem = static_cast<CShangpinFoodItemUI*>(builder.Create(xml_name.c_str(), (UINT)0, &cb, m_pManager));
|
|
|
+
|
|
|
+ pItem->SetYoutuModel(is_youtu);
|
|
|
+ pItem->SetFoodInfo(food);
|
|
|
+ pItem->UpdateShow();
|
|
|
+
|
|
|
+ m_foodLayout->Add(pItem);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //选择的是普通商品分类
|
|
|
+ CSqlite3 sqlite;
|
|
|
+ std::vector<CFood> foodlist = sqlite.GetFoodByTypeid(m_cur_type_id);
|
|
|
+
|
|
|
+ for (std::vector<CFood>::iterator it = foodlist.begin(); it != foodlist.end(); it++)
|
|
|
+ {
|
|
|
+ CFood food = *it;
|
|
|
+
|
|
|
+ CDialogBuilder builder;
|
|
|
+ CDialogBuilderCallbackEx cb;
|
|
|
+
|
|
|
+ CShangpinFoodItemUI* pItem = static_cast<CShangpinFoodItemUI*>(builder.Create(xml_name.c_str(), (UINT)0, &cb, m_pManager));
|
|
|
+
|
|
|
+ pItem->SetYoutuModel(is_youtu);
|
|
|
+ pItem->SetFoodInfo(food);
|
|
|
+ pItem->UpdateShow();
|
|
|
+
|
|
|
+ m_foodLayout->Add(pItem);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//处理按钮点击类事件
|
|
|
void CAIxuexiPageUI::HandleClickMsg(TNotifyUI& msg)
|
|
|
{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+
|
|
|
+ if (name == L"shangpin_fooditem")
|
|
|
+ {
|
|
|
+ CShangpinFoodItemUI* fooditemUI = static_cast<CShangpinFoodItemUI*>(msg.pSender);
|
|
|
+
|
|
|
+ m_cur_click_food_item = fooditemUI;
|
|
|
|
|
|
+ this->ClickFoodAction();
|
|
|
+ }
|
|
|
+ else if (name == L"aixuexi_page_food_search_clear")
|
|
|
+ {
|
|
|
+ CEditUI* m_pEdit = static_cast<CEditUI*>(m_pManager->FindControl(_T("aixuexi_page_food_search_edit")));
|
|
|
+ m_pEdit->SetText(L"");
|
|
|
+
|
|
|
+ StopSerachFood();
|
|
|
+ }
|
|
|
+ else if (name == L"aixuexi_page_paishe_btn")
|
|
|
+ {
|
|
|
+ this->DoXuexi();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
void CAIxuexiPageUI::HandleSelectChangeMsg(TNotifyUI& msg)
|
|
|
{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+
|
|
|
+ COptionUI* curOption = static_cast<COptionUI*>(msg.pSender);
|
|
|
+ std::wstring groupname = curOption->GetGroup();
|
|
|
+
|
|
|
+ if (groupname == L"aixuexi_page_foodtype")
|
|
|
+ {
|
|
|
+ //商品分类切换
|
|
|
+ CFoodtypeOptionUI* typeUI = static_cast<CFoodtypeOptionUI*>(curOption);
|
|
|
+ std::string id = typeUI->GetTypeId();
|
|
|
+
|
|
|
+ if (m_cur_type_id != id)
|
|
|
+ {
|
|
|
+ //切换了商品分类
|
|
|
+ m_curFoodtypeOption->SetBkColor(0xFFECECEC);
|
|
|
+
|
|
|
+ msg.pSender->SetBkColor(0xFF3CB371);
|
|
|
|
|
|
+ m_curFoodtypeOption = static_cast<CControlUI*>(msg.pSender);
|
|
|
+
|
|
|
+ m_cur_type_id = id;
|
|
|
+
|
|
|
+ this->InitFoodShow();
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void CAIxuexiPageUI::HandleItemSelectMsg(TNotifyUI& msg)
|
|
|
@@ -56,7 +350,26 @@ void CAIxuexiPageUI::HandleItemSelectMsg(TNotifyUI& msg)
|
|
|
|
|
|
void CAIxuexiPageUI::HandleTextChangedMsg(TNotifyUI& msg)
|
|
|
{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+
|
|
|
+ if (name == L"aixuexi_page_food_search_edit")
|
|
|
+ {
|
|
|
+ //商品搜索框的输入事件
|
|
|
+ CEditUI* m_pEdit = static_cast<CEditUI*>(m_pManager->FindControl(_T("aixuexi_page_food_search_edit")));
|
|
|
+ std::wstring ws_Value = m_pEdit->GetText();
|
|
|
+ std::string strValue = CLewaimaiString::UnicodeToUTF8(ws_Value);
|
|
|
|
|
|
+ if (strValue.length() == 0)
|
|
|
+ {
|
|
|
+ //搜索词被清空了,退出搜索
|
|
|
+ this->StopSerachFood();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //搜索词没清空,进入搜索
|
|
|
+ this->StartSearchFood(strValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -102,7 +415,7 @@ void CAIxuexiPageUI::HandleUpdateVideo()
|
|
|
}
|
|
|
|
|
|
//缩放尺寸,适合在界面上展示,过大过小都不好看,这个尺寸是根据界面上image控件的大小来定的,可以根据实际情况调整
|
|
|
- cv::resize(img, img, cv::Size(480, 360), 0, 0, cv::INTER_LINEAR);
|
|
|
+ cv::resize(img, img, cv::Size(400, 300), 0, 0, cv::INTER_LINEAR);
|
|
|
|
|
|
// 创建或更新位图
|
|
|
HDC hDC = GetDC(NULL);
|
|
|
@@ -156,4 +469,144 @@ void CAIxuexiPageUI::UpdateVideoShow()
|
|
|
SelectObject(m_hMemDC, hOldBitmap);
|
|
|
|
|
|
return;
|
|
|
+}
|
|
|
+
|
|
|
+void CAIxuexiPageUI::ClickFoodAction()
|
|
|
+{
|
|
|
+ //点击普通商品之后的处理逻辑
|
|
|
+ CLabelUI* pLabel = static_cast<CLabelUI*>(this->FindSubControl(_T("aixuexi_page_xuanzhong_food_name")));
|
|
|
+
|
|
|
+ std::string sName = m_cur_click_food_item->GetFoodInfo().name;
|
|
|
+
|
|
|
+ std::wstring wsShow = L"当前选中商品:" + CLewaimaiString::UTF8ToUnicode(sName);
|
|
|
+
|
|
|
+ pLabel->SetText(wsShow.c_str());
|
|
|
+
|
|
|
+ //加载这个商品当前已经学习过的图片,并显示出来
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+//开始搜索某个商品名字
|
|
|
+void CAIxuexiPageUI::StartSearchFood(std::string foodname)
|
|
|
+{
|
|
|
+ //展示删除按钮
|
|
|
+ CButtonUI* pClear = static_cast<CButtonUI*>(this->FindSubControl(_T("aixuexi_page_food_search_clear")));
|
|
|
+ pClear->SetVisible(true);
|
|
|
+
|
|
|
+ //隐藏商品分类展示
|
|
|
+ CVerticalLayoutUI* pFoodtype = static_cast<CVerticalLayoutUI*>(this->FindSubControl(_T("aixuexi_page_fenlei_layout_scrolllayout")));
|
|
|
+ pFoodtype->SetVisible(false);
|
|
|
+
|
|
|
+ if (m_cur_type_id != "sousuo")
|
|
|
+ {
|
|
|
+ m_type_id_before_sousuo = m_cur_type_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_cur_type_id = "sousuo";
|
|
|
+
|
|
|
+ m_sousuo_foodname = foodname;
|
|
|
+
|
|
|
+ CLabelUI* pTishi = static_cast<CLabelUI*>(this->FindSubControl(_T("aixuexi_page_food_search_tishi")));
|
|
|
+ pTishi->SetVisible(false);
|
|
|
+
|
|
|
+ this->InitFoodShow();
|
|
|
+}
|
|
|
+
|
|
|
+//停止搜索商品
|
|
|
+void CAIxuexiPageUI::StopSerachFood()
|
|
|
+{
|
|
|
+ //隐藏删除按钮
|
|
|
+ CButtonUI* pClear = static_cast<CButtonUI*>(this->FindSubControl(_T("aixuexi_page_food_search_clear")));
|
|
|
+ pClear->SetVisible(false);
|
|
|
+
|
|
|
+ //展示商品分类展示
|
|
|
+ CVerticalLayoutUI* pFoodtype = static_cast<CVerticalLayoutUI*>(this->FindSubControl(_T("aixuexi_page_fenlei_layout_scrolllayout")));
|
|
|
+ pFoodtype->SetVisible(true);
|
|
|
+
|
|
|
+ m_cur_type_id = m_type_id_before_sousuo;
|
|
|
+
|
|
|
+ CLabelUI* pTishi = static_cast<CLabelUI*>(this->FindSubControl(_T("aixuexi_page_food_search_tishi")));
|
|
|
+ pTishi->SetVisible(true);
|
|
|
+
|
|
|
+ this->InitFoodShow();
|
|
|
+}
|
|
|
+
|
|
|
+void CAIxuexiPageUI::SetPos(RECT rc, bool bNeedInvalidate)
|
|
|
+{
|
|
|
+ m_nPageWidth = rc.right - rc.left;
|
|
|
+
|
|
|
+ //更新分类位置
|
|
|
+ UpdateFoodtypePos();
|
|
|
+
|
|
|
+ CContainerUI::SetPos(rc, bNeedInvalidate);
|
|
|
+}
|
|
|
+
|
|
|
+void CAIxuexiPageUI::DoXuexi()
|
|
|
+{
|
|
|
+ //第一步先把当前帧保存为图片
|
|
|
+ std::wstring save_path = CSystem::GetTmpDir() + L"\\ai_xuexi_image";
|
|
|
+
|
|
|
+ if (!CSystem::IsPathExist(save_path))
|
|
|
+ {
|
|
|
+ CSystem::CreateMultiLevel(CLewaimaiString::UnicodeToUTF8(save_path));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string filename = CLewaimaiString::generateRandomString(32);
|
|
|
+ std::wstring file_save_path = save_path + L"\\" + CLewaimaiString::UTF8ToUnicode(filename) + L".jpg";
|
|
|
+ std::string s_file_save_path = CLewaimaiString::UnicodeToUTF8(file_save_path);
|
|
|
+
|
|
|
+ cv::Mat img;
|
|
|
+ CVideoCaptureWorker::GetInstance()->GetFrame(img);
|
|
|
+
|
|
|
+ if (img.empty())
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (img.type() != CV_8UC3)
|
|
|
+ {
|
|
|
+ // 仅支持 3 通道彩色图像
|
|
|
+ cvtColor(img, img, cv::COLOR_GRAY2BGR);
|
|
|
+ }
|
|
|
+
|
|
|
+ cv::imwrite(CLewaimaiString::UnicodeToUTF8(file_save_path), img);
|
|
|
+
|
|
|
+ //第二步把这个图片的特征向量提取出来,保存到数据库里,关联到当前选中的商品上
|
|
|
+ std::string sName = m_cur_click_food_item->GetFoodInfo().name;
|
|
|
+ std::string sFoodId = m_cur_click_food_item->GetFoodInfo().id;
|
|
|
+
|
|
|
+ //初始化数据库
|
|
|
+ std::wstring wsExePath = CSystem::getExePath();
|
|
|
+ std::wstring wsProgramDir = CSystem::GetProgramDir();
|
|
|
+ std::filesystem::path mainDir = wsProgramDir;
|
|
|
+ std::string sMainDir = mainDir.string();
|
|
|
+
|
|
|
+ std::string databasePath = sMainDir + "/image_features.db"; // SQLite数据库路径
|
|
|
+
|
|
|
+ // 初始化SQLite-Vec数据库
|
|
|
+ std::cout << "正在初始化SQLite-Vec数据库..." << std::endl;
|
|
|
+ SQLiteVecManager vecManager(databasePath);
|
|
|
+
|
|
|
+ // 检查数据库是否已存在数据
|
|
|
+ bool databaseExists = vecManager.loadDatabase();
|
|
|
+ std::cout << "数据库加载结果:" << std::boolalpha << databaseExists << std::endl;
|
|
|
+ std::cout << "数据集数量:" << vecManager.getFeatureCount() << std::endl;
|
|
|
+ if (databaseExists && vecManager.getFeatureCount() > 0)
|
|
|
+ {
|
|
|
+ std::cout << "数据库已存在 " << vecManager.getFeatureCount() << " 条特征记录" << std::endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ //初始化向量数据库,特征向量是1280维度
|
|
|
+ vecManager.initializeDatabase(1280);
|
|
|
+
|
|
|
+ std::string modelPath = sMainDir + "/ai/best_448.onnx"; // YOLO2026模型路径
|
|
|
+ YoloFeatureManager extractor;
|
|
|
+ extractor.loadModel(modelPath);
|
|
|
+
|
|
|
+ std::cout << "开始提取图库图片特征..." << std::endl;
|
|
|
+ std::vector<float> features = extractor.extractFeatures(s_file_save_path);
|
|
|
+ if (!features.empty())
|
|
|
+ {
|
|
|
+ vecManager.addFeatureVector(features, s_file_save_path);
|
|
|
+ }
|
|
|
}
|