1.说明
考完试查看正确答案(错题解析)截图, 进行ocr提取文字用于再次考试快速检索题目
GUI版说明 : 运行后会打开窗口界面, 在窗口界面搜索关键字即可
需要先提取图片中的文字, 参考: python(59) : 多线程调用大模型ocr提取图片文本-优快云博客
功能特性
- 🔍 智能搜索:基于 OCR 识别的文本内容进行图片搜索
- 🎯 高级搜索:支持
&(与)和|(或)操作符进行组合搜索 - 🖼️ 图片预览:实时显示搜索结果中的图片
- 📊 结果导航:支持上一张/下一张浏览搜索结果
- ⚡ 实时搜索:输入关键字时自动搜索,无需点击按钮
- 💻 桌面应用:基于 tkinter 的图形界面,操作简单直观
环境要求
- Python 3.7+
- tkinter(通常随 Python 安装)
- Pillow (PIL)
安装步骤
-
克隆或下载项目到本地
-
安装依赖包:
pip install Pillow
目录结构
确保你的项目目录结构如下:
考试助手/
├── answer_search_gui.py
├── resource/
│ ├── 文件夹1/
│ │ ├── imgs/ # 存放图片文件
│ │ │ ├── image1.png
│ │ │ ├── image2.png
│ │ │ └── ...
│ │ └── texts/ # OCR 识别的文本文件(由 llm_ocr.py 生成)
│ │ ├── image1.txt
│ │ ├── image2.txt
│ │ └── ...
│ └── 文件夹2/
│ ├── imgs/
│ └── texts/
└── README.md
⚠️ 注意:使用本工具前,需要先使用
llm_ocr.py对图片进行 OCR 识别,生成对应的文本文件。
使用方法
运行程序
python answer_search_gui.py
搜索功能
-
普通搜索:在搜索框中输入关键字,程序会自动搜索包含该关键字的图片
- 例如:输入
选择题会查找所有包含"选择题"的图片
- 例如:输入
-
与操作(&):使用
&连接多个关键字,要求同时包含所有关键字- 例如:
选择题&答案会查找同时包含"选择题"和"答案"的图片
- 例如:
-
或操作(|):使用
|连接多个关键字,包含任意一个关键字即可- 例如:
选择题|判断题会查找包含"选择题"或"判断题"的图片
- 例如:
-
浏览结果:
- 使用"上一张"和"下一张"按钮浏览搜索结果
- 界面会显示当前是第几张,共找到多少张图片
-
重置搜索:点击"重置"按钮清空搜索框并重新搜索
界面说明
- 搜索框:输入搜索关键字
- 搜索按钮:手动触发搜索(也可直接按 Enter 键)
- 重置按钮:清空搜索框
- 上一张/下一张:浏览搜索结果
- 图片显示区:显示当前选中的图片
- 结果信息:显示搜索结果统计和当前图片位置
打包成可执行文件
安装 PyInstaller
pip install pyinstaller
打包命令
pyinstaller -F -i maomi.ico answer_search_gui.py --clean --noconfirm
参数说明:
-F:打包成单个可执行文件-i maomi.ico:指定图标文件(可选,如果不需要图标可删除此参数)--clean:清理临时文件--noconfirm:覆盖输出目录而不询问
打包后的文件位置
打包完成后,可执行文件位于 dist/ 目录下。
工作原理
- 数据加载:程序启动时,自动扫描
resource目录下所有子文件夹 - 文本读取:读取每个文件夹中
texts目录下的文本文件(OCR 识别结果) - 建立索引:将图片路径和对应的文本内容建立映射关系
- 搜索匹配:根据用户输入的关键字,在文本内容中查找匹配的图片
- 图片显示:使用 Pillow 库加载并显示匹配的图片
注意事项
- 前置条件:使用本工具前,必须先运行
llm_ocr.py对图片进行 OCR 识别 - 文件格式:程序默认查找
.png格式的图片文件 - 文本编码:文本文件必须使用 UTF-8 编码
- 实时搜索:输入关键字时会自动触发搜索,可能对性能有一定影响
- 图片路径:确保图片文件路径正确,如果图片不存在会显示灰色占位图
常见问题
Q: 提示"资源目录不存在"?
A: 请确保在项目根目录下存在 resource 文件夹,并且其中包含 imgs 和 texts 子文件夹。
Q: 搜索不到结果?
A: 请检查:
- 是否已运行
llm_ocr.py生成文本文件 - 文本文件是否包含搜索关键字
- 关键字是否正确(注意大小写和空格)
Q: 图片显示不出来?
A: 请检查:
- 图片文件是否存在
- 图片路径是否正确
- 图片格式是否支持(建议使用 PNG 格式)
Q: 如何修改搜索逻辑?
A: 可以修改 find_keys_by_value 函数来实现自定义的搜索逻辑。
与 llm_ocr.py 的配合使用
-
第一步:运行
llm_ocr.py对图片进行 OCR 识别python llm_ocr.py -
第二步:运行
answer_search_gui.py进行搜索python answer_search_gui.py
技术栈
- GUI 框架:tkinter
- 图像处理:Pillow (PIL)
- 打包工具:PyInstaller
许可证
本项目仅供学习和个人使用。
2.python代码(answer_search_gui.py)
import os
import tkinter as tk
# from tkinter import messagebox
from PIL import Image, ImageTk
# 全局变量存储图片文本字典
img_text_dict = {}
# pip install pyinstaller Pillow
# pyinstaller -F -i maomi.ico answer_search_gui.py --clean --noconfirm
def load_image_text_data():
"""加载图片和文本数据"""
global img_text_dict
# 目录配置
current_dir = os.getcwd()
print(current_dir)
#current_dir = r'F:\my\python\mypy\考试\瓴羊高级数据研发工程师认证2'
# 遍历资源目录
ks_dirs = []
resource_dir = os.path.join(current_dir, 'resource')
if not os.path.exists(resource_dir):
print(f"警告: 资源目录不存在 {resource_dir}")
return
for item in os.listdir(resource_dir):
item_path = os.path.join(resource_dir, item)
if os.path.isdir(item_path):
ks_dirs.append(item_path)
# 检索文件
for path in ks_dirs:
texts_path = os.path.join(path, 'texts')
imgs_path = os.path.join(path, 'imgs')
if not os.path.exists(texts_path):
continue
for fil in os.listdir(texts_path):
text_file = os.path.join(texts_path, fil)
try:
with open(text_file, 'r', encoding='utf-8') as f:
content = f.read()
img_name = fil.split('.')[0] + '.png'
img_path = os.path.join(imgs_path, img_name)
img_text_dict[img_path] = content.strip().replace(' ', '')
except Exception as e:
print(f"读取文件错误 {text_file}: {e}")
print(f"加载了 {len(img_text_dict)} 张图片数据")
def find_keys_by_value(dictionary, target_value):
"""
根据目标值在字典中查找所有匹配的键。
支持 &(与)和 |(或)操作符:
- 如果 target_value 包含 '&',则要求所有分隔的值都包含在字典值中(与)。
- 如果 target_value 包含 '|',则只要有一个分隔的值包含在字典值中即可(或)。
- 如果都不包含,则进行普通包含匹配。
Args:
dictionary (dict): 要搜索的字典,值应为字符串或可迭代的容器(如列表、集合等)
target_value (str): 要查找的目标值,支持 & 和 | 分隔
Returns:
list: 包含所有匹配键的列表
"""
# 辅助函数:检查 value 是否包含 sub
def contains(value, sub):
if isinstance(value, str):
return sub in value
elif isinstance(value, (list, tuple, set)):
return sub in value
else:
return str(sub) in str(value)
result = []
for key, value in dictionary.items():
if '&' in target_value:
# 使用 & 分割,所有部分都必须包含(与)
parts = target_value.split('&')
if all(contains(value, part.strip()) for part in parts):
result.append(key)
elif '|' in target_value:
# 使用 | 分割,任意一个部分包含即可(或)
parts = target_value.split('|')
if any(contains(value, part.strip()) for part in parts):
result.append(key)
else:
# 普通包含匹配
if contains(value, target_value):
result.append(key)
return result
class ImageSearchApp:
def __init__(self, root, img_text_dict):
# 设置主窗口
self.root = root
self.img_text_dict = img_text_dict
self.root.title("图片搜索器")
self.root.geometry("1200x700")
self.root.configure(bg="#f0f0f0")
# 存储搜索结果
self.search_results = []
self.current_index = 0
# 创建UI组件
self.create_widgets()
def create_widgets(self):
# 创建搜索框和导航按钮框架(合并为一个框架)
search_nav_frame = tk.Frame(self.root, bg="#f0f0f0", pady=20)
search_nav_frame.pack(fill=tk.X, padx=20)
# 搜索标签
tk.Label(search_nav_frame, text="搜索关键字:", bg="#f0f0f0", font=("Arial", 12)).pack(side=tk.LEFT, padx=5)
# 搜索输入框
self.search_entry = tk.Entry(search_nav_frame, font=("Arial", 12), width=40)
self.search_entry.pack(side=tk.LEFT, padx=5)
self.search_entry.bind('<Return>', lambda event: self.perform_search())
# 绑定输入事件,自动搜索
self.search_entry.bind('<KeyRelease>', lambda event: self.perform_search())
# 重置按钮
reset_btn = tk.Button(
search_nav_frame,
text="重置",
command=self.reset_search,
bg="#FF9800",
fg="white",
font=("Arial", 10, "bold"),
padx=10
)
reset_btn.pack(side=tk.LEFT, padx=5)
# 搜索按钮
search_btn = tk.Button(
search_nav_frame,
text="搜索",
command=self.perform_search,
bg="#4CAF50",
fg="white",
font=("Arial", 10, "bold"),
padx=10
)
search_btn.pack(side=tk.LEFT, padx=5)
# 搜索结果信息标签 - 新增部分
self.result_info_label = tk.Label(
search_nav_frame,
text="",
bg="#f0f0f0",
font=("Arial", 10),
fg="#666666"
)
self.result_info_label.pack(side=tk.LEFT, padx=15)
# 导航按钮
self.prev_btn = tk.Button(
search_nav_frame,
text="上一张",
command=self.prev_image,
state=tk.DISABLED,
bg="#2196F3",
fg="white",
font=("Arial", 10),
padx=15
)
self.prev_btn.pack(side=tk.LEFT, padx=5)
self.next_btn = tk.Button(
search_nav_frame,
text="下一张",
command=self.next_image,
state=tk.DISABLED,
bg="#2196F3",
fg="white",
font=("Arial", 10),
padx=15
)
self.next_btn.pack(side=tk.LEFT, padx=5)
# 搜索提示信息(放在搜索框和按钮的下一行)
hint_label = tk.Label(
self.root,
text="搜索提示: 支持使用 &(与)和 |(或)操作符。 例如:关键词1&关键词2 表示同时包含两个关键词, 关键词1|关键词2 表示包含任意一个关键词。",
bg="#f0f0f0",
font=("Arial", 9),
fg="#666666",
wraplength=800,
justify=tk.LEFT
)
hint_label.pack(anchor=tk.W, padx=20, pady=(0, 10))
# 图片展示框架
self.image_frame = tk.Frame(self.root, bg="white", bd=2, relief=tk.SUNKEN)
self.image_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
# 图片标签
self.image_label = tk.Label(self.image_frame, bg="white")
self.image_label.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# 图片信息标签
self.info_label = tk.Label(self.root, text="", bg="#f0f0f0", font=("Arial", 10))
self.info_label.pack(pady=5, anchor=tk.W, padx=20)
# 初始提示
self.image_label.config(text="请输入关键字并搜索图片")
# 搜索初始化
self.perform_search()
def reset_search(self):
"""重置搜索:清空搜索框并执行搜索"""
self.search_entry.delete(0, tk.END)
self.perform_search()
def perform_search(self):
"""执行搜索操作"""
keyword = self.search_entry.get().strip()
# if not keyword:
# messagebox.showwarning("提示", "请输入搜索关键字")
# return
# 在图片列表中搜索包含关键字的项
filelist = find_keys_by_value(self.img_text_dict, keyword)
self.search_results = [(index, filename) for index, filename in enumerate(filelist)]
# 更新UI
if self.search_results:
self.current_index = 0
self.update_image_display()
self.update_nav_buttons()
self.info_label.config(text=f"找到 {len(self.search_results)} 张包含 '{keyword}' 的图片")
else:
self.image_label.config(text=f"没有找到包含 '{keyword}' 的图片", image='') # 新增 image='' 清空当前图片
self.prev_btn.config(state=tk.DISABLED)
self.next_btn.config(state=tk.DISABLED)
self.result_info_label.config(text="")
self.info_label.config(text=f"未找到包含 '{keyword}' 的图片")
def update_image_display(self):
"""更新当前显示的图片"""
if not self.search_results:
return
index, image_name = self.search_results[self.current_index]
# 实际应用中,这里应该是您的图片路径
# 这里使用示例图片,实际使用时请替换为您的图片路径
# 为了演示,我们使用Pillow创建一个简单的图像
try:
# 尝试打开真实图片(如果存在)
print(os.path.basename(image_name))
if os.path.exists(image_name):
img = Image.open(image_name)
else:
# 如果图片不存在,创建一个带有文字的示例图片
img = Image.new('RGB', (400, 300), color='lightgray')
# 注意:实际使用时需要安装Pillow库,并取消下面的注释
# from PIL import ImageDraw, ImageFont
# draw = ImageDraw.Draw(img)
# font = ImageFont.truetype("arial.ttf", 24) # 可能需要调整字体路径
# draw.text((50, 130), image_name, font=font, fill=(0, 0, 0))
# 调整图片大小以适应窗口
img.thumbnail((900, 500))
photo = ImageTk.PhotoImage(img)
# 更新图片标签
self.image_label.config(image=photo)
self.image_label.image = photo # 保持引用
self.result_info_label.config(
text=f"共 {len(self.search_results)} 张,当前第 {self.current_index + 1} 张")
except Exception as e:
self.image_label.config(text=f"无法加载图片: {str(e)}")
def update_nav_buttons(self):
"""更新导航按钮状态"""
if len(self.search_results) <= 1:
self.prev_btn.config(state=tk.DISABLED)
self.next_btn.config(state=tk.DISABLED)
else:
self.prev_btn.config(state=tk.NORMAL if self.current_index > 0 else tk.DISABLED)
self.next_btn.config(state=tk.NORMAL if self.current_index < len(self.search_results) - 1 else tk.DISABLED)
self.result_info_label.config(text=f"共 {len(self.search_results)} 张,当前第 {self.current_index + 1} 张")
def prev_image(self):
"""显示上一张图片"""
if self.current_index > 0:
self.current_index -= 1
self.update_image_display()
self.update_nav_buttons()
def next_image(self):
"""显示下一张图片"""
if self.current_index < len(self.search_results) - 1:
self.current_index += 1
self.update_image_display()
self.update_nav_buttons()
if __name__ == "__main__":
print("正在加载图片数据...")
load_image_text_data()
root = tk.Tk()
app = ImageSearchApp(root, img_text_dict)
root.mainloop()


667

被折叠的 条评论
为什么被折叠?



