增加了相似菜品预览功能,更完整了,交互更方便了。

This commit is contained in:
2025-09-29 18:03:28 +08:00
parent 1f04bb5c01
commit f3e3bd048a
4 changed files with 163 additions and 26 deletions
+142 -5
View File
@@ -61,7 +61,7 @@ class EmbeddingFoodClassifierApp:
"""加载训练好的embedding模型和FAISS索引""" """加载训练好的embedding模型和FAISS索引"""
try: try:
# 模型路径 # 模型路径
model_path = "../model/embedding_20250917_145342/best_embedding_model.pth" model_path = "../model/embedding_20250929_151102/best_embedding_model.pth"
# FAISS索引目录 # FAISS索引目录
index_dir = "../faiss_vector_db/faiss_index" index_dir = "../faiss_vector_db/faiss_index"
@@ -744,11 +744,13 @@ class EmbeddingFoodClassifierApp:
# 保存图片路径 # 保存图片路径
paths_path = os.path.join(index_dir, 'image_paths.pkl') paths_path = os.path.join(index_dir, 'image_paths.pkl')
with open(paths_path, 'wb') as f: with open(paths_path, 'wb') as f:
# 重新写入图片路径
pickle.dump(self.image_paths, f) pickle.dump(self.image_paths, f)
# 保存标签 # 保存标签
labels_path = os.path.join(index_dir, 'labels.pkl') labels_path = os.path.join(index_dir, 'labels.pkl')
with open(labels_path, 'wb') as f: with open(labels_path, 'wb') as f:
# 重新写入标签
pickle.dump(self.labels, f) pickle.dump(self.labels, f)
# 更新类别信息 # 更新类别信息
@@ -1309,18 +1311,153 @@ class EmbeddingFoodClassifierApp:
) )
similar_label.pack(fill="x", padx=5, pady=(5, 2)) similar_label.pack(fill="x", padx=5, pady=(5, 2))
# 显示前3张最相似的图片信息 # 显示前5张最相似的图片信息
for j, sim_img in enumerate(similar_images[:3]): for j, sim_img in enumerate(similar_images[:5]):
# 创建一个水平框架来放置信息和按钮
sim_frame = ctk.CTkFrame(info_frame)
sim_frame.pack(fill="x", padx=15, pady=1)
# 相似图片信息
sim_info = f" {j+1}. {sim_img['class']} (相似度: {sim_img['score']:.3f})" sim_info = f" {j+1}. {sim_img['class']} (相似度: {sim_img['score']:.3f})"
sim_info_label = ctk.CTkLabel( sim_info_label = ctk.CTkLabel(
info_frame, sim_frame,
text=sim_info, text=sim_info,
anchor="w", anchor="w",
font=("Arial", 9), font=("Arial", 9),
text_color="gray" text_color="gray"
) )
sim_info_label.pack(fill="x", padx=15, pady=1) sim_info_label.pack(side="left", fill="x", expand=True, padx=5, pady=2)
# 查看按钮(使用眼睛图标)
view_btn = ctk.CTkButton(
sim_frame,
text="👁", # 眼睛图标
width=25,
height=20,
font=("Arial", 12),
command=lambda path=sim_img['path']: self.show_similar_image(path),
fg_color="#2196F3",
hover_color="#1976D2",
corner_radius=10
)
view_btn.pack(side="right", padx=5, pady=2)
def show_similar_image(self, image_path):
"""显示相似图片的窗口"""
try:
# 创建新窗口
similar_window = ctk.CTkToplevel(self.root)
similar_window.title("相似图片查看")
similar_window.geometry("600x500")
similar_window.resizable(True, True)
# 设置窗口属性,确保在主窗口上方
similar_window.transient(self.root) # 设置为主窗口的子窗口
similar_window.grab_set() # 设置为模态窗口
similar_window.lift() # 提升到最前面
similar_window.focus_set() # 设置焦点
# 居中显示
similar_window.update_idletasks()
x = (similar_window.winfo_screenwidth() // 2) - (600 // 2)
y = (similar_window.winfo_screenheight() // 2) - (500 // 2)
similar_window.geometry(f"600x500+{x}+{y}")
# 主框架
main_frame = ctk.CTkFrame(similar_window)
main_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 标题
title_label = ctk.CTkLabel(
main_frame,
text="相似图片详情",
font=("Arial", 16, "bold")
)
title_label.pack(pady=(10, 20))
# 图片路径信息
path_label = ctk.CTkLabel(
main_frame,
text=f"图片路径: {image_path}",
font=("Arial", 10),
text_color="gray",
wraplength=550
)
path_label.pack(pady=(0, 10))
# 图片显示框架
image_frame = ctk.CTkFrame(main_frame)
image_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 加载并显示图片
try:
# 打开图片
pil_image = Image.open(image_path)
# 计算合适的显示尺寸
max_width, max_height = 500, 350
img_width, img_height = pil_image.size
# 计算缩放比例
scale = min(max_width / img_width, max_height / img_height, 1.0)
new_width = int(img_width * scale)
new_height = int(img_height * scale)
# 调整图片大小
pil_image = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
# 转换为CTk图片
ctk_image = ctk.CTkImage(
light_image=pil_image,
dark_image=pil_image,
size=(new_width, new_height)
)
# 显示图片
image_label = ctk.CTkLabel(
image_frame,
image=ctk_image,
text=""
)
image_label.pack(expand=True, pady=20)
# 图片信息
info_text = f"原始尺寸: {img_width} × {img_height}显示尺寸: {new_width} × {new_height}"
info_label = ctk.CTkLabel(
main_frame,
text=info_text,
font=("Arial", 9),
text_color="gray"
)
info_label.pack(pady=(0, 10))
except Exception as e:
error_label = ctk.CTkLabel(
image_frame,
text=f"无法加载图片: {str(e)}",
font=("Arial", 12),
text_color="red"
)
error_label.pack(expand=True)
# 关闭按钮
close_btn = ctk.CTkButton(
main_frame,
text="关闭",
width=100,
command=similar_window.destroy,
fg_color="#f44336",
hover_color="#da190b"
)
close_btn.pack(pady=(10, 10))
# 让窗口获得焦点
similar_window.focus()
similar_window.lift()
except Exception as e:
messagebox.showerror("错误", f"显示相似图片时出错: {str(e)}")
def update_stats(self): def update_stats(self):
"""更新统计信息""" """更新统计信息"""
total_images = len(self.uploaded_images) total_images = len(self.uploaded_images)
+16 -16
View File
@@ -27,24 +27,24 @@ for class_name in os.listdir(input_root):
input_dir = os.path.join(input_root, class_name) input_dir = os.path.join(input_root, class_name)
output_dir = os.path.join(output_root, class_name) output_dir = os.path.join(output_root, class_name)
os.makedirs(output_dir, exist_ok=True) os.makedirs(output_dir, exist_ok=True)
if class_name == "麻婆豆腐":
# 读取类别下所有图片路径
img_files = [f for f in os.listdir(input_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))]
img_paths = [os.path.join(input_dir, f) for f in img_files]
# 读取类别下所有图片路径 print(f"类别 {class_name} 原始图片数: {len(img_paths)}")
img_files = [f for f in os.listdir(input_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))]
img_paths = [os.path.join(input_dir, f) for f in img_files]
print(f"类别 {class_name} 原始图片数: {len(img_paths)}") count = 0
while count < target_num:
img_path = random.choice(img_paths)
img = Image.open(img_path).convert("RGB")
# 生成增强图
aug_img = transform(img)
# 保存
save_path = os.path.join(output_dir, f"aug_{count:03d}.jpg")
aug_img.save(save_path)
count += 1
count = 0 print(f"类别 {class_name} 已扩充到 {target_num} 张,保存于 {output_dir}")
while count < target_num:
img_path = random.choice(img_paths)
img = Image.open(img_path).convert("RGB")
# 生成增强图
aug_img = transform(img)
# 保存
save_path = os.path.join(output_dir, f"aug_{count:03d}.jpg")
aug_img.save(save_path)
count += 1
print(f"类别 {class_name} 已扩充到 {target_num} 张,保存于 {output_dir}")
print("✅ 数据增强完成!") print("✅ 数据增强完成!")
+1 -1
View File
@@ -477,7 +477,7 @@ class FAISSSearcher:
def main(): def main():
"""主函数""" """主函数"""
# 配置参数 # 配置参数
MODEL_PATH = "../model/embedding_20250917_145342/best_embedding_model.pth" MODEL_PATH = "../model/embedding_20250929_151102/best_embedding_model.pth"
TRAIN_DIR = "../dataset/train" TRAIN_DIR = "../dataset/train"
OUTPUT_DIR = "faiss_index" OUTPUT_DIR = "faiss_index"
BATCH_SIZE = 16 BATCH_SIZE = 16
+4 -4
View File
@@ -79,9 +79,9 @@ class TripletDataset(Dataset):
image_files = [f for f in os.listdir(class_path) image_files = [f for f in os.listdir(class_path)
if f.lower().endswith(('.png', '.jpg', '.jpeg'))] if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
# 限制每个类别的样本数 # 限制每个类别的样本数,没有必要限制
if len(image_files) > self.samples_per_class: # if len(image_files) > self.samples_per_class:
image_files = random.sample(image_files, self.samples_per_class) # image_files = random.sample(image_files, self.samples_per_class)
for img_file in image_files: for img_file in image_files:
img_path = os.path.join(class_path, img_file) img_path = os.path.join(class_path, img_file)
@@ -551,7 +551,7 @@ def main():
train_dataset = TripletDataset( train_dataset = TripletDataset(
dataset_path=settings.TRAIN_DATA_DIR, dataset_path=settings.TRAIN_DATA_DIR,
transform=transform_train, transform=transform_train,
samples_per_class=200 samples_per_class=550
) )
val_dataset = TripletDataset( val_dataset = TripletDataset(