diff --git a/classifier/embedding_food_classifier_app.py b/classifier/embedding_food_classifier_app.py index dcd95f5..0c2a044 100644 --- a/classifier/embedding_food_classifier_app.py +++ b/classifier/embedding_food_classifier_app.py @@ -61,7 +61,7 @@ class EmbeddingFoodClassifierApp: """加载训练好的embedding模型和FAISS索引""" try: # 模型路径 - model_path = "../model/embedding_20250917_145342/best_embedding_model.pth" + model_path = "../model/embedding_20250929_151102/best_embedding_model.pth" # FAISS索引目录 index_dir = "../faiss_vector_db/faiss_index" @@ -744,11 +744,13 @@ class EmbeddingFoodClassifierApp: # 保存图片路径 paths_path = os.path.join(index_dir, 'image_paths.pkl') with open(paths_path, 'wb') as f: + # 重新写入图片路径 pickle.dump(self.image_paths, f) # 保存标签 labels_path = os.path.join(index_dir, 'labels.pkl') with open(labels_path, 'wb') as f: + # 重新写入标签 pickle.dump(self.labels, f) # 更新类别信息 @@ -1309,18 +1311,153 @@ class EmbeddingFoodClassifierApp: ) similar_label.pack(fill="x", padx=5, pady=(5, 2)) - # 显示前3张最相似的图片信息 - for j, sim_img in enumerate(similar_images[:3]): + # 显示前5张最相似的图片信息 + 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_label = ctk.CTkLabel( - info_frame, + sim_frame, text=sim_info, anchor="w", font=("Arial", 9), 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): """更新统计信息""" total_images = len(self.uploaded_images) diff --git a/data_expansion/data_expansion.py b/data_expansion/data_expansion.py index 7d28bc5..9b75a9a 100644 --- a/data_expansion/data_expansion.py +++ b/data_expansion/data_expansion.py @@ -27,24 +27,24 @@ for class_name in os.listdir(input_root): input_dir = os.path.join(input_root, class_name) output_dir = os.path.join(output_root, class_name) 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] - # 读取类别下所有图片路径 - 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)}") - 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 - 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(f"类别 {class_name} 已扩充到 {target_num} 张,保存于 {output_dir}") print("✅ 数据增强完成!") diff --git a/faiss_vector_db/build_faiss_index.py b/faiss_vector_db/build_faiss_index.py index 6ee1059..cd65899 100644 --- a/faiss_vector_db/build_faiss_index.py +++ b/faiss_vector_db/build_faiss_index.py @@ -477,7 +477,7 @@ class FAISSSearcher: 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" OUTPUT_DIR = "faiss_index" BATCH_SIZE = 16 diff --git a/train/train_embedding.py b/train/train_embedding.py index 9cbda59..46514d1 100644 --- a/train/train_embedding.py +++ b/train/train_embedding.py @@ -79,9 +79,9 @@ class TripletDataset(Dataset): image_files = [f for f in os.listdir(class_path) if f.lower().endswith(('.png', '.jpg', '.jpeg'))] - # 限制每个类别的样本数 - if len(image_files) > self.samples_per_class: - image_files = random.sample(image_files, self.samples_per_class) + # 限制每个类别的样本数,没有必要限制 + # if len(image_files) > self.samples_per_class: + # image_files = random.sample(image_files, self.samples_per_class) for img_file in image_files: img_path = os.path.join(class_path, img_file) @@ -551,7 +551,7 @@ def main(): train_dataset = TripletDataset( dataset_path=settings.TRAIN_DATA_DIR, transform=transform_train, - samples_per_class=200 + samples_per_class=550 ) val_dataset = TripletDataset(