增加了相似菜品预览功能,更完整了,交互更方便了。
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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("✅ 数据增强完成!")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user