张洋 1 day ago
parent
commit
9650ee43d1

+ 6 - 4
bin/Win32/Debug/zhipuzi_pos_windows/skin/aixuexi_page.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Window>
 	<Font id="0" name="微软雅黑" size="24" />
-	<Font id="aixuexi_1" name="微软雅黑" size="26" />
+	<Font id="aixuexi_1" name="微软雅黑" size="28" />
 	<Font id="2" name="微软雅黑" size="16" />
 	<Font id="3" name="微软雅黑" size="14" />
     <Font id="4" name="微软雅黑" size="12" />
@@ -11,9 +11,9 @@
 			<Control width="1" bkcolor="#FFD1D1D1"></Control>
 			
             <VerticalLayout name="aixuexi_page_paishe_layout" width="420" padding="0,0,0,0" bordercorner="10,10" bkcolor="#FFFFFFFF" autocalcwidth="true">
-                <Label name="aixuexi_page_xuanzhong_food_name" text="当前选中商品:无" height="30" padding="10,10,10,10" font="aixuexi_1"></Label>
+                <Label name="aixuexi_page_xuanzhong_food_name" text="当前选中商品:无" height="22" padding="10,10,10,10" textcolor="#FFFF0000" font="aixuexi_1"></Label>
 
-                <Label name="aixuexi_page_xuanzhong_food_name" wordbreak="true" height="50" padding="10,0,10,10" font="3" text="智铺子AI识别无需学习默认就能识别几百种常见的水果蔬菜,只需要对未正常识别的冷门商品或非水果蔬菜类商品进行学习。"></Label>
+                <Label name="aixuexi_page_xuanzhong_food_name" wordbreak="true" height="40" padding="10,0,10,10" font="3" text="智铺子AI识别无需学习默认就能识别几百种常见的水果蔬菜,只需要对未正常识别的冷门商品或非水果蔬菜类商品进行学习。"></Label>
 
                 <Control name="image" width="400" height="300" padding="10,0,10,0"></Control>
 
@@ -23,7 +23,9 @@
                     <Control></Control>
                 </HorizontalLayout>
 
-                <Control></Control>                
+                <TileLayout name="aixuexi_page_food_xuexi_image_layout" padding="0,0,0,0" inset="10,10,0,10" childpadding="10" vscrollbar="true" hscrollbar="false">
+					
+				</TileLayout>                
             </VerticalLayout> 
 
             <Control width="1" bkcolor="#FFEBE8E8"></Control>

+ 8 - 0
bin/Win32/Debug/zhipuzi_pos_windows/skin/aixuexi_page_food_xuexi_image.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Window>	
+	<FoodXuexiImageItem name="shangpin_fooditem" bkcolor="#FFFFFFFF" padding="0,0,0,0">
+		<Control name="image" width="120" height="90" bkimage="file='food_image_default.png'" padding="0,0,0,0"></Control>
+		
+        <Button name="aixuexi_page_food_image_delete" normalimage="close_icon.png" hotimage="close_icon.png" pushedimage="close_icon.png" bkimage="close_icon.png" width="24" height="24" pos="95,0,119,24" float="true"></Button>
+	</FoodXuexiImageItem>
+</Window>

BIN
bin/Win32/Debug/zhipuzi_pos_windows/skin/close_icon.png


+ 96 - 37
zhipuzi_pos_windows/ai/SQLiteVecManager.cpp

@@ -8,35 +8,10 @@
 
 #include "../tool/debuglog.h"
 
-SQLiteVecManager::SQLiteVecManager(const std::string & databaseName) : dbName(databaseName), db(nullptr)
+SQLiteVecManager::SQLiteVecManager()
 {
-	int rc = SQLITE_OK;
-	sqlite3_stmt* stmt;
-
-	rc = sqlite3_open(dbName.c_str(), &db);
-	assert(rc == SQLITE_OK);
-	if (rc != SQLITE_OK)
-	{
-		std::string err = "Can't open database: " + std::string(sqlite3_errmsg(db));
-		throw std::runtime_error("Can't open database: " + std::string(sqlite3_errmsg(db)));
-	}
-
-	// 初始化 sqlite-vec 扩展
-	char* errMsg = 0;
-	rc = sqlite3_vec_init(db, &errMsg, 0);
-	assert(rc == SQLITE_OK);
-	if (rc != SQLITE_OK)
-	{
-		std::string err = "Can't init vec: " + std::string(sqlite3_errmsg(db));
-		throw std::runtime_error("Can't init vec: " + std::string(sqlite3_errmsg(db)));
-	}
-
-	rc = sqlite3_prepare_v2(db, "SELECT sqlite_version(), vec_version()", -1, &stmt, NULL);
-	assert(rc == SQLITE_OK);
-
-	rc = sqlite3_step(stmt);
-	DEBUG_HELPER::debug_printf("sqlite_version=%s, vec_version=%s\n", sqlite3_column_text(stmt, 0), sqlite3_column_text(stmt, 1));
-	sqlite3_finalize(stmt);
+	db = NULL;
+	dbName = "";
 }
 
 SQLiteVecManager::~SQLiteVecManager()
@@ -49,13 +24,59 @@ SQLiteVecManager::~SQLiteVecManager()
 
 bool SQLiteVecManager::initializeDatabase(int vectorDimension)
 {
-	int rc;
-	char * errMsg = 0;
+	try
+	{
+		//初始化数据库
+		std::wstring wsProgramDir = CSystem::GetProgramDir();
+		std::filesystem::path mainDir = wsProgramDir;
+		std::string sMainDir = mainDir.string();
+
+		dbName = sMainDir + "/db/image_features.db";     // SQLite数据库路径	
+
+		int rc;
+		char* errMsg = 0;
+
+		sqlite3_stmt* stmt;
+
+		rc = sqlite3_open(dbName.c_str(), &db);
+		assert(rc == SQLITE_OK);
+		if (rc != SQLITE_OK)
+		{
+			std::string err = "Can't open database: " + std::string(sqlite3_errmsg(db));
+			throw std::runtime_error("Can't open database: " + std::string(sqlite3_errmsg(db)));
+		}
+
+		// 初始化 sqlite-vec 扩展
+		rc = sqlite3_vec_init(db, &errMsg, 0);
+		assert(rc == SQLITE_OK);
+		if (rc != SQLITE_OK)
+		{
+			std::string err = "Can't init vec: " + std::string(sqlite3_errmsg(db));
+			throw std::runtime_error("Can't init vec: " + std::string(sqlite3_errmsg(db)));
+		}
+
+		rc = sqlite3_prepare_v2(db, "SELECT sqlite_version(), vec_version()", -1, &stmt, NULL);
+		assert(rc == SQLITE_OK);
+
+		rc = sqlite3_step(stmt);
+		DEBUG_HELPER::debug_printf("sqlite_version=%s, vec_version=%s\n", sqlite3_column_text(stmt, 0), sqlite3_column_text(stmt, 1));
+		sqlite3_finalize(stmt);
+
+		std::cout << "使用sqlite-vec扩展进行向量存储和搜索" << std::endl;
 
-	std::cout << "使用sqlite-vec扩展进行向量存储和搜索" << std::endl;
+		// 检查数据库是否已存在数据
+		bool databaseExists = this->loadDatabase();
+		std::cout << "数据库加载结果:" << std::boolalpha << databaseExists << std::endl;
+		std::cout << "数据集数量:" << this->getFeatureCount() << std::endl;
+		if (databaseExists && this->getFeatureCount() > 0)
+		{
+			std::cout << "数据库已存在 " << this->getFeatureCount() << " 条特征记录" << std::endl;
 
-	// 创建vec0虚拟表【这里只能用TEXT,不能用CHAR,不然创建表会报错】
-	std::string sql = R"(CREATE VIRTUAL TABLE IF NOT EXISTS image_features USING vec0(
+			return true;
+		}
+
+		// 创建vec0虚拟表【这里只能用TEXT,不能用CHAR,不然创建表会报错】
+		std::string sql = R"(CREATE VIRTUAL TABLE IF NOT EXISTS image_features USING vec0(
 		id INTEGER PRIMARY KEY AUTOINCREMENT,
 		food_id TEXT NOT NULL,
 		food_name TEXT UNIQUE NOT NULL,
@@ -63,11 +84,17 @@ bool SQLiteVecManager::initializeDatabase(int vectorDimension)
 		image_path TEXT UNIQUE NOT NULL,
 		feature_vector FLOAT[)" + std::to_string(vectorDimension) + "] distance_metric=cosine)";
 
-	rc = sqlite3_exec(db, sql.c_str(), 0, 0, &errMsg);
-	if (rc != SQLITE_OK)
+		rc = sqlite3_exec(db, sql.c_str(), 0, 0, &errMsg);
+		if (rc != SQLITE_OK)
+		{
+			std::string err = std::string("Failed to create vec0 table: ") + errMsg;
+			sqlite3_free(errMsg);
+			return false;
+		}
+	}
+	catch (const std::exception& ex)
 	{
-		std::string err = std::string("Failed to create vec0 table: ") + errMsg;
-		sqlite3_free(errMsg);
+		std::cerr << "Error initializing database: " << ex.what() << std::endl;
 		return false;
 	}
 
@@ -195,4 +222,36 @@ bool SQLiteVecManager::isEmpty() const
 float SQLiteVecManager::distanceToSimilarity(float distance)
 {
 	return 1.0f - distance;
+}
+
+std::vector<std::pair<std::string, std::string>> SQLiteVecManager::GetFoodXueXiImageInfoByFoodId(const std::string& foodId)
+{
+	std::vector<std::pair<std::string, std::string>> results;
+
+	std::string sql = "SELECT image_name, image_path FROM image_features WHERE food_id = ?;";
+
+	sqlite3_stmt* stmt;
+		
+	int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, NULL);
+	
+	if (rc != SQLITE_OK)
+	{
+		std::string err = sqlite3_errmsg(db);
+		std::cerr << "Failed to prepare statement: " << err << std::endl;
+		return results;
+	}
+
+	sqlite3_bind_text(stmt, 1, foodId.c_str(), -1, SQLITE_STATIC);
+
+	while ((rc = sqlite3_step(stmt)) == SQLITE_ROW)
+	{
+		const char* imageName = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
+		const char* imagePath = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
+
+		results.emplace_back(std::string(imageName), std::string(imagePath));
+	}
+
+	sqlite3_finalize(stmt);
+
+	return results;
 }

+ 11 - 1
zhipuzi_pos_windows/ai/SQLiteVecManager.h

@@ -12,10 +12,18 @@ private:
 	std::string dbName;
 
 public:
-	SQLiteVecManager(const std::string & databaseName);
+	SQLiteVecManager();
 	~SQLiteVecManager();
 
+	static SQLiteVecManager* GetInstance()
+	{
+		static SQLiteVecManager instance;
+
+		return &instance;
+	}
+
 	bool initializeDatabase(int vectorDimension);
+
 	bool addFeatureVector(const std::vector<float> & features, const std::string& foodId, const std::string& foodName, const std::string& imageName, const std::string & imagePath);
 	std::vector<std::pair<std::string, float>> searchSimilarVectors(const std::vector<float> & queryVector, int k = 5);
 	void saveDatabase();
@@ -23,6 +31,8 @@ public:
 	int getFeatureCount() const;
 	bool isEmpty() const;
 
+	//根据foodId获取这个商品已经学习过的图片信息,包含图片路径和图片名字
+	std::vector<std::pair<std::string, std::string>> GetFoodXueXiImageInfoByFoodId(const std::string& foodId);
 private:
 	float distanceToSimilarity(float distance);
 };

+ 21 - 11
zhipuzi_pos_windows/ai/YoloFeatureManager.cpp

@@ -18,6 +18,11 @@ YoloFeatureManager::YoloFeatureManager()
 {
 	inputWidth = 320;
 	inputHeight = 320;
+
+	CONF_THRESHOLD = 0.5f; // 可以根据需要调整置信度阈值
+	NMS_THRESHOLD = 0.4f;  // 可以根据需要调整NMS阈值
+
+	FRUIT_VEGETABLE_COUNT = sizeof(FRUIT_VEGETABLE_NAMES) / sizeof(FRUIT_VEGETABLE_NAMES[0]);
 }
 
 YoloFeatureManager::~YoloFeatureManager()
@@ -25,16 +30,18 @@ YoloFeatureManager::~YoloFeatureManager()
 	
 }
 
-void YoloFeatureManager::loadModel(const std::string & modelPath)
+void YoloFeatureManager::loadModel()
 {
 	try
 	{
-		net = cv::dnn::readNetFromONNX(modelPath);
+		std::wstring wsProgramDir = CSystem::GetProgramDir();
+		std::filesystem::path mainDir = wsProgramDir;
+		std::string sMainDir = mainDir.string();
 
-		CONF_THRESHOLD = 0.5f; // 可以根据需要调整置信度阈值
-		NMS_THRESHOLD = 0.4f;  // 可以根据需要调整NMS阈值
+		// YOLO2026模型路径
+		std::string modelPath = sMainDir + "/ai/best_2026n_320.onnx";
 
-		FRUIT_VEGETABLE_COUNT = sizeof(FRUIT_VEGETABLE_NAMES) / sizeof(FRUIT_VEGETABLE_NAMES[0]);
+		net = cv::dnn::readNetFromONNX(modelPath);
 	}
 	catch (const std::exception& e)
 	{
@@ -44,20 +51,23 @@ void YoloFeatureManager::loadModel(const std::string & modelPath)
 	}
 }
 
-void YoloFeatureManager::loadModel(const std::string& modelPath, const std::string& configPath)
+void YoloFeatureManager::loadModelForOpenVINO()
 {
 	try
 	{
+		std::wstring wsProgramDir = CSystem::GetProgramDir();
+		std::filesystem::path mainDir = wsProgramDir;
+		std::string sMainDir = mainDir.string();
+
+		// YOLO2026模型路径
+		std::string modelPath = sMainDir + "/ai/best.xml";
+		std::string configPath = sMainDir + "/ai/best.bin";
+
 		net = cv::dnn::readNetFromModelOptimizer(modelPath, configPath);
 
 		// 设置目标设备 (可选: CPU, GPU, MYRIAD等)
 		net.setPreferableBackend(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE);
 		net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);  // 或DNN_TARGET_MYRIAD等
-
-		CONF_THRESHOLD = 0.5f; // 可以根据需要调整置信度阈值
-		NMS_THRESHOLD = 0.4f;  // 可以根据需要调整NMS阈值
-
-		FRUIT_VEGETABLE_COUNT = sizeof(FRUIT_VEGETABLE_NAMES) / sizeof(FRUIT_VEGETABLE_NAMES[0]);
 	}
 	catch (const std::exception& e)
 	{

+ 9 - 2
zhipuzi_pos_windows/ai/YoloFeatureManager.h

@@ -21,10 +21,17 @@ public:
 	YoloFeatureManager();
 	~YoloFeatureManager();
 
+	static YoloFeatureManager* GetInstance()
+	{
+		static YoloFeatureManager instance;
+
+		return &instance;
+	}
+
 	// 重新加载模型
-	void loadModel(const std::string & modelPath);
+	void loadModel();
 
-	void loadModel(const std::string& modelPath, const std::string& configPath);
+	void loadModelForOpenVINO();
 
 	// 提取特征
 	std::vector<float> extractFeatures(const std::string & imagePath);

+ 2 - 2
zhipuzi_pos_windows/control/CDiandanFoodItemUI.cpp

@@ -22,13 +22,13 @@ void CDiandanFoodItemUI::DoEvent(TEventUI& event)
 	{
 		HCURSOR hcur = LoadCursor(NULL, IDC_HAND);  //加载系统自带鼠标样式
 		HWND hwnd = m_pManager->GetPaintWindow(); // 获取绘图窗口句柄
-		SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
+		//SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
 	}
 	else if (event.Type == UIEVENT_MOUSELEAVE)
 	{
 		HCURSOR hcur = LoadCursor(NULL, IDC_ARROW);  //加载系统自带鼠标样式
 		HWND hwnd = m_pManager->GetPaintWindow(); // 获取绘图窗口句柄
-		SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
+		//SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
 	}
 
 	CVerticalLayoutUI::DoEvent(event);

+ 31 - 0
zhipuzi_pos_windows/control/CFoodXuexiImageItemUI.cpp

@@ -0,0 +1,31 @@
+#include "CFoodXuexiImageItemUI.h"
+
+CFoodXuexiImageItemUI::CFoodXuexiImageItemUI()
+{
+}
+
+CFoodXuexiImageItemUI::~CFoodXuexiImageItemUI()
+{
+}
+
+void CFoodXuexiImageItemUI::SetImageName(std::string imageName)
+{
+	m_imageName = imageName;
+}
+
+void CFoodXuexiImageItemUI::SetImagePath(std::string imagePath)
+{
+	m_imagePath = imagePath;
+
+	//更新显示
+	std::wstring strImagePath = CLewaimaiString::UTF8ToUnicode(imagePath);
+
+	CControlUI* pImage = static_cast<CControlUI*>(this->FindSubControl(_T("image")));
+
+	//获取本地图片的路径
+	if (CSystem::IsFileExist(strImagePath.c_str()))
+	{
+		//图片已经存在,直接显示
+		pImage->SetBkImage(strImagePath.c_str());
+	}
+}

+ 18 - 0
zhipuzi_pos_windows/control/CFoodXuexiImageItemUI.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include "../pch/pch.h"
+
+class CFoodXuexiImageItemUI : public CVerticalLayoutUI
+{
+public:
+	CFoodXuexiImageItemUI();
+	~CFoodXuexiImageItemUI();
+
+	void SetImageName(std::string imageName);
+	void SetImagePath(std::string imagePath);
+
+private:
+	std::string m_imageName;
+	std::string m_imagePath;
+};
+

+ 2 - 2
zhipuzi_pos_windows/control/CShangpinFoodItemUI.cpp

@@ -22,13 +22,13 @@ void CShangpinFoodItemUI::DoEvent(TEventUI& event)
 	{
 		HCURSOR hcur = LoadCursor(NULL, IDC_HAND);  //加载系统自带鼠标样式
 		HWND hwnd = m_pManager->GetPaintWindow(); // 获取绘图窗口句柄
-		SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
+		//SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
 	}
 	else if (event.Type == UIEVENT_MOUSELEAVE)
 	{
 		HCURSOR hcur = LoadCursor(NULL, IDC_ARROW);  //加载系统自带鼠标样式
 		HWND hwnd = m_pManager->GetPaintWindow(); // 获取绘图窗口句柄
-		SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
+		//SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
 	}
 
 	CVerticalLayoutUI::DoEvent(event);

+ 5 - 0
zhipuzi_pos_windows/control/ControlEx.h

@@ -37,6 +37,7 @@
 #include "CZhengcanTableItemUI.h"
 #include "CZhengcanOrderItemUI.h"
 #include "CShoukuanOrderItemUI.h"
+#include "CFoodXuexiImageItemUI.h"
 
 class CDialogBuilderCallbackEx : public IDialogBuilderCallback
 {
@@ -182,6 +183,10 @@ public:
 		{
 			return new CShoukuanOrderItemUI;
 		}
+		else if (_tcscmp(pstrClass, _T("FoodXuexiImageItem")) == 0)
+		{
+			return new CFoodXuexiImageItemUI;
+		}
 
 		return NULL;
 	}

+ 6 - 7
zhipuzi_pos_windows/helper/CSpeech.h

@@ -1,6 +1,5 @@
-#pragma once
+#pragma once
 
-#define _ATL_APARTMENT_THREADED
 #include <stdio.h>
 #include <atlbase.h>
 #include <atlcom.h>
@@ -13,20 +12,20 @@ public:
     CSpeech();
     ~CSpeech();
 
-	// speakContent为LPCTSTR型的字符串,调用此函数即可将文字转为语音
+	// speakContent为LPCTSTR型的字符串,调用此函数即可将文字转为语音
 	static void MSSSpeak(LPCTSTR speakContent);
 
 	static void SpeakIntXiaoyuWan(int new_num, bool is_after_wan = false);
 
 	/*
-	 *读一个整数
+	 *读一个整数
 	 **/
 	static void SpeakInt(int num);
 
     /*
-     *把数字拆解成一个个的读音,调用wav文件播放
-     *voice_type 1:收款成功 2:退款成功
-     *num 金额,比如18.23
+     *鎶婃暟瀛楁媶瑙f垚涓€涓�釜鐨勮�闊筹紝璋冪敤wav鏂囦欢鎾�斁
+     *voice_type 1:收款成功 2:退款成功
+     *num 金额,比如18.23
      **/
 	static void SpeakNum(int voice_type, std::string num);
 };

+ 53 - 33
zhipuzi_pos_windows/page/CAIxuexiPageUI.cpp

@@ -13,6 +13,8 @@
 #include "../ai/ImageProcessor.h"
 #include "../ai/SQLiteVecManager.h"
 
+#include "../control/CFoodXuexiImageItemUI.h"
+
 CAIxuexiPageUI::CAIxuexiPageUI()
 {
 	m_hBitmap = NULL;
@@ -55,6 +57,39 @@ void CAIxuexiPageUI::InitShow()
 	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()
 {
 	//初始化商品渲染相关的信息
@@ -489,7 +524,7 @@ void CAIxuexiPageUI::ClickFoodAction()
 	pLabel->SetText(wsShow.c_str());
 
 	//加载这个商品当前已经学习过的图片,并显示出来
-
+	this->UpdatCurFoodXuexiShow();
 }
 
 //开始搜索某个商品名字
@@ -557,7 +592,6 @@ void CAIxuexiPageUI::DoXuexi()
 
 	//第一步先把当前帧保存为图片
 	std::wstring save_path = CSystem::GetTmpDir() + L"\\ai_xuexi_image";
-
 	if (!CSystem::IsPathExist(save_path))
 	{
 		CSystem::CreateMultiLevel(CLewaimaiString::UnicodeToUTF8(save_path));
@@ -584,42 +618,28 @@ void CAIxuexiPageUI::DoXuexi()
 	cv::imwrite(CLewaimaiString::UnicodeToUTF8(file_save_path), img);
 
 	//第二步把这个图片的特征向量提取出来,保存到数据库里,关联到当前选中的商品上
-
-	//初始化数据库
-	std::wstring wsExePath = CSystem::getExePath();
-	std::wstring wsProgramDir = CSystem::GetProgramDir();
-	std::filesystem::path mainDir = wsProgramDir;
-	std::string sMainDir = mainDir.string();
-
-	std::string databasePath = sMainDir + "/db/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_2026n_320.onnx";           // YOLO2026模型路径
-	YoloFeatureManager extractor;
-	extractor.loadModel(modelPath);
-
 	std::cout << "开始提取图库图片特征..." << std::endl;
-	std::vector<float> features = extractor.extractFeatures(s_file_save_path);
+	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;
 
-		vecManager.addFeatureVector(features, sFoodId, sName, filename, s_file_save_path);
+		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);
 }

+ 3 - 0
zhipuzi_pos_windows/page/CAIxuexiPageUI.h

@@ -21,6 +21,9 @@ public:
 	//初始化当前页面的展示,处理默认展示效果,在页面每次被选中加载(注意不是页面创建构造)的时候调用,如果多次选中会多次调用,这里要避免数据重复处理
 	void InitShow();
 
+	//更新当前选中商品的已学习的显示
+	void UpdatCurFoodXuexiShow();
+
 	//初始化商品分类的显示
 	void InitFoodtypeShow();
 

+ 8 - 0
zhipuzi_pos_windows/tool/CAppEnv.cpp

@@ -10,6 +10,8 @@
 #include "../worker/CDiandanAIShibieWorker.h"
 #include "CShuangpingTool.h"
 
+#include "../ai/SQLiteVecManager.h"
+
 CAppEnv::CAppEnv()
 {
 	m_worker_count = 0;
@@ -25,6 +27,12 @@ void CAppEnv::Start()
 	//初始化配置
 	CSetting::GetInstance()->Init();
 
+	//加载YOLO模型
+	YoloFeatureManager::GetInstance()->loadModel();
+
+	//初始化特征数据库
+	SQLiteVecManager::GetInstance()->initializeDatabase(1280);
+
 	//启动打印队列
 	CPosPrinterQueue::GetInstance()->StartWork();
 

+ 1 - 22
zhipuzi_pos_windows/worker/CDiandanAIShibieWorker.cpp

@@ -31,31 +31,10 @@ void CDiandanAIShibieWorker::StopWork()
 	m_is_work = false;
 }
 
-
 void CDiandanAIShibieWorker::HandleDiandanAIShibie()
 {
 	long int threadId = GetCurrentThreadId();
 
-	std::wstring wsExePath = CSystem::getExePath();
-	std::wstring wsProgramDir = CSystem::GetProgramDir();
-
-	std::filesystem::path mainDir = wsProgramDir;
-	std::string sMainDir = mainDir.string();
-
-	std::string modelPath = sMainDir + "/ai/best_2026n_320.onnx";           // YOLO2026模型路径
-
-	std::string openvino_modelPath = sMainDir + "/ai/best.xml";           // YOLO2026模型路径
-	std::string openvino_configPath = sMainDir + "/ai/best.bin";           // YOLO2026模型路径
-
-	std::string databasePath = sMainDir + "/image_features.db";     // SQLite数据库路径
-
-	std::cout << "=== 图像AI识别线程启动 ===" << std::endl;
-	std::cout << "模型路径: " << modelPath << std::endl;
-	std::cout << "数据库路径: " << databasePath << std::endl;
-	std::cout << "=========================================" << std::endl;
-
-	m_yoloFeatureManager.loadModel(modelPath);
-
 	while (m_is_work == true)
 	{
 		try
@@ -79,7 +58,7 @@ void CDiandanAIShibieWorker::HandleDiandanAIShibie()
 
 			auto time_1 = std::chrono::high_resolution_clock::now();
 
-			m_ai_shibie_foodname = m_yoloFeatureManager.ClassFromVideoCapture();
+			m_ai_shibie_foodname = YoloFeatureManager::GetInstance()->ClassFromVideoCapture();
 
 			if (m_ai_shibie_foodname != "Unknown")
 			{

+ 0 - 1
zhipuzi_pos_windows/worker/CDiandanAIShibieWorker.h

@@ -52,7 +52,6 @@ private:
 
 	bool m_is_ai_shibie = false;
 
-	YoloFeatureManager m_yoloFeatureManager;
 	std::string m_ai_shibie_foodname;
 
 	HWND m_hwnd = NULL;

+ 2 - 0
zhipuzi_pos_windows/zhipuzi_pos_windows.vcxproj

@@ -244,6 +244,7 @@ copy $(ProjectDir)conf\ $(SolutionDir)bin\$(Platform)\$(Configuration)\conf\</Co
     <ClInclude Include="ai\SQLiteVecManager.h" />
     <ClInclude Include="ai\YoloClassName.h" />
     <ClInclude Include="ai\YoloFeatureManager.h" />
+    <ClInclude Include="control\CFoodXuexiImageItemUI.h" />
     <ClInclude Include="wnd\CVirtualKeyboardWnd.h" />
     <ClInclude Include="page\CAIxuexiPageUI.h" />
     <ClInclude Include="ui\TouchWindowBase.h" />
@@ -377,6 +378,7 @@ copy $(ProjectDir)conf\ $(SolutionDir)bin\$(Platform)\$(Configuration)\conf\</Co
     <ClCompile Include="ai\test.cpp" />
     <ClCompile Include="ai\test.h" />
     <ClCompile Include="ai\YoloFeatureManager.cpp" />
+    <ClCompile Include="control\CFoodXuexiImageItemUI.cpp" />
     <ClCompile Include="wnd\CVirtualKeyboardWnd.cpp" />
     <ClCompile Include="page\CAIxuexiPageUI.cpp" />
     <ClCompile Include="ui\TouchWindowBase.cpp" />

+ 6 - 0
zhipuzi_pos_windows/zhipuzi_pos_windows.vcxproj.filters

@@ -405,6 +405,9 @@
     <ClInclude Include="wnd\CVirtualKeyboardWnd.h">
       <Filter>头文件</Filter>
     </ClInclude>
+    <ClInclude Include="control\CFoodXuexiImageItemUI.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="pch\pch.cpp">
@@ -770,6 +773,9 @@
     <ClCompile Include="wnd\CVirtualKeyboardWnd.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="control\CFoodXuexiImageItemUI.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Image Include="resource\zhipuzi.ico">