| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- #include "CAIxuexiPageUI.h"
- #include "../control/ControlEx.h"
- #include "../wnd/CMainWnd.h"
- #include "../worker/CVideoCaptureWorker.h"
- #include "../tool/CSqlite3.h"
- #include "../wnd/CMainWnd.h"
- #include "../ai/YoloFeatureManager.h"
- #include "../ai/ImageProcessor.h"
- #include "../ai/SQLiteVecManager.h"
- #include "../control/CFoodXuexiImageItemUI.h"
- CAIxuexiPageUI::CAIxuexiPageUI()
- {
- m_hBitmap = NULL;
- m_hMemDC = NULL;
- }
- CAIxuexiPageUI::~CAIxuexiPageUI()
- {
- if (m_hBitmap)
- {
- DeleteObject(m_hBitmap);
- m_hBitmap = NULL;
- }
- if (m_hMemDC)
- {
- DeleteDC(m_hMemDC);
- m_hMemDC = NULL;
- }
- }
- void CAIxuexiPageUI::InitShow()
- {
- if (m_isFirstInit)
- {
- m_isFirstInit = false;
-
- std::thread(&CAIxuexiPageUI::HandleUpdateVideo, this).detach();
- }
- //重新进来的时候,重置当前选中的分类和商品
- m_cur_click_food_item = NULL;
- CLabelUI* pLabel = static_cast<CLabelUI*>(this->FindSubControl(_T("aixuexi_page_xuanzhong_food_name")));
- std::wstring wsShow = L"当前选中商品:无";
- pLabel->SetText(wsShow.c_str());
- //先把之前的显示清空
- CTileLayoutUI* pXuexiScrollLayout = static_cast<CTileLayoutUI*>(this->FindSubControl(_T("aixuexi_page_food_xuexi_image_layout")));
- pXuexiScrollLayout->RemoveAll();
- //先初始化商品分类
- this->InitFoodtypeShow();
- //再初始化商品显示
- this->InitFoodShow();
- }
- void CAIxuexiPageUI::UpdatCurFoodXuexiShow()
- {
- //把当前选中商品的已学习显示更新一下
- std::string sFoodId = m_cur_click_food_item->GetFoodInfo().id;
- std::vector<std::pair<std::string, std::string>> food_vector = SQLiteVecManager::GetInstance()->GetFoodXueXiImageInfoByFoodId(sFoodId);
- //先把之前的显示清空
- CTileLayoutUI* pXuexiScrollLayout = static_cast<CTileLayoutUI*>(this->FindSubControl(_T("aixuexi_page_food_xuexi_image_layout")));
- pXuexiScrollLayout->RemoveAll();
- SIZE itemsize;
- itemsize.cx = 120;
- itemsize.cy = 90;
- pXuexiScrollLayout->SetItemSize(itemsize);
- //再把新的显示添加上
- for (std::vector<std::pair<std::string, std::string>>::iterator it = food_vector.begin(); it != food_vector.end(); it++)
- {
- std::pair<std::string, std::string> item = *it;
- std::string imageName = item.first;
- std::string imagePath = item.second;
- CDialogBuilder builder;
- CDialogBuilderCallbackEx cb;
- CFoodXuexiImageItemUI* pItem = static_cast<CFoodXuexiImageItemUI*>(builder.Create(_T("aixuexi_page_food_xuexi_image.xml"), (UINT)0, &cb, m_pManager));
- pItem->SetImageName(imageName);
- pItem->SetImagePath(imagePath);
- pXuexiScrollLayout->Add(pItem);
- }
- }
- 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("aixuexi_page_fooditem.xml");
- }
- else
- {
- SIZE itemsize;
- itemsize.cx = 130;
- itemsize.cy = 90;
- m_foodLayout->SetItemSize(itemsize);
- xml_name = _T("aixuexi_page_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;
- CAIXuexiPageFoodItemUI* pItem = static_cast<CAIXuexiPageFoodItemUI*>(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;
- CAIXuexiPageFoodItemUI* pItem = static_cast<CAIXuexiPageFoodItemUI*>(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;
- CAIXuexiPageFoodItemUI* pItem = static_cast<CAIXuexiPageFoodItemUI*>(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"aixuexi_page_fooditem")
- {
- CAIXuexiPageFoodItemUI* fooditemUI = static_cast<CAIXuexiPageFoodItemUI*>(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();
- }
- else if (name == L"aixuexi_page_food_image_delete")
- {
- CFoodXuexiImageItemUI* m_pImageItem = static_cast<CFoodXuexiImageItemUI*>(msg.pSender->GetParent());
- //先删除图片控件
- CTileLayoutUI* pXuexiScrollLayout = static_cast<CTileLayoutUI*>(this->FindSubControl(_T("aixuexi_page_food_xuexi_image_layout")));
- pXuexiScrollLayout->Remove(m_pImageItem);
- //然后从数据库中删除这个图片对应的向量
- std::string imageName = m_pImageItem->GetImageName();
- SQLiteVecManager::GetInstance()->DeleteFeatureVectorByImageName(imageName);
- }
- else if (name == L"aixuexi_page_return")
- {
- m_pMainWnd->SwitchPage(CMainWnd::DIANDAN);
- }
- }
- 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)
- {
- }
- 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);
- }
- }
- }
- void CAIxuexiPageUI::HandleTextCapture(std::string content)
- {
- }
- bool CAIxuexiPageUI::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- if (uMsg == WM_AI_XUEXI_UPDATE)
- {
- UpdateVideoShow();
- }
- return false;
- }
- void CAIxuexiPageUI::HandleUpdateVideo()
- {
- while (true)
- {
- if (m_pMainWnd->GetCurPageName() != CMainWnd::AIXUEXI)
- {
- Sleep(30);
- continue;
- }
- cv::Mat img;
- CVideoCaptureWorker::GetInstance()->GetFrame(img);
- if (img.empty())
- {
- Sleep(30);
- continue;
- }
- if (img.type() != CV_8UC3)
- {
- // 仅支持 3 通道彩色图像
- cvtColor(img, img, cv::COLOR_GRAY2BGR);
- }
- //缩放尺寸,适合在界面上展示,过大过小都不好看,这个尺寸是根据界面上image控件的大小来定的,可以根据实际情况调整
- cv::resize(img, img, cv::Size(400, 300), 0, 0, cv::INTER_LINEAR);
- // 创建或更新位图
- HDC hDC = GetDC(NULL);
- if (m_hBitmap)
- {
- DeleteObject(m_hBitmap);
- }
- BITMAPINFO bmi = { 0 };
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = img.cols;
- bmi.bmiHeader.biHeight = -img.rows; // 翻转 Y 轴(OpenCV 与 GDI 坐标方向相反)
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 24;
- bmi.bmiHeader.biCompression = BI_RGB;
- m_hBitmap = CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT, img.data, &bmi, DIB_RGB_COLORS);
- ReleaseDC(NULL, hDC);
- ::PostMessage(m_pMainWnd->GetHWND(), WM_AI_XUEXI_UPDATE, 0, 0);
- // 控制帧率,避免过度占用 CPU,30 FPS 大约是每 33 毫秒更新一次
- Sleep(1000 / 30);
- }
- }
- void CAIxuexiPageUI::UpdateVideoShow()
- {
- HDC hDC = m_pManager->GetPaintDC();
- if (!m_hMemDC)
- {
- m_hMemDC = CreateCompatibleDC(hDC);
- }
- HBITMAP hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap);
- BITMAP bm;
- GetObject(m_hBitmap, sizeof(BITMAP), &bm);
- CControlUI* pImage = static_cast<CControlUI*>(this->FindSubControl(_T("image")));
- if (!pImage)
- {
- return;
- }
- RECT imageRect = pImage->GetPos();
- StretchBlt(hDC, imageRect.left, imageRect.top, imageRect.right - imageRect.left,
- imageRect.bottom - imageRect.top, m_hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
- 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());
- //加载这个商品当前已经学习过的图片,并显示出来
- this->UpdatCurFoodXuexiShow();
- }
- //开始搜索某个商品名字
- 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()
- {
- if (m_cur_click_food_item == NULL)
- {
- m_pMainWnd->ShowToast(L"请先选择一个商品");
- return;
- }
- //第一步先把当前帧保存为图片
- 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::generateRandomStr(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::cout << "开始提取图库图片特征..." << std::endl;
- std::vector<float> features = YoloFeatureManager::GetInstance()->extractFeatures(s_file_save_path);
- if (!features.empty())
- {
- std::string sName = m_cur_click_food_item->GetFoodInfo().name;
- std::string sFoodId = m_cur_click_food_item->GetFoodInfo().id;
- SQLiteVecManager::GetInstance()->addFeatureVector(features, sFoodId, sName, filename, s_file_save_path);
- }
- //第三步把刚拍摄的图片显示出来
- CTileLayoutUI* pXuexiScrollLayout = static_cast<CTileLayoutUI*>(this->FindSubControl(_T("aixuexi_page_food_xuexi_image_layout")));
- std::string imageName = filename;
- std::string imagePath = s_file_save_path;
- CDialogBuilder builder;
- CDialogBuilderCallbackEx cb;
- CFoodXuexiImageItemUI* pItem = static_cast<CFoodXuexiImageItemUI*>(builder.Create(_T("aixuexi_page_food_xuexi_image.xml"), (UINT)0, &cb, m_pManager));
- pItem->SetImageName(imageName);
- pItem->SetImagePath(imagePath);
- pXuexiScrollLayout->Add(pItem);
- }
|