[Python] 纯文本查看 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.widgets import Button, RadioButtons
import tkinter as tk
from tkinter import simpledialog, colorchooser, filedialog
import os
# 初始化数据
clicked_points_dict = {} # 存储每个图像的标记点信息
color_mapping = {'Red': (1.0, 0.0, 0.0), 'Yellow': (1.0, 1.0, 0.0), 'Blue': (0.0, 0.0, 1.0), 'Green': (0.0, 1.0, 0.0)}
# 图像文件路径列表
image_paths = []
current_image_index = 0
image_rgb = None
# 创建 tkinter 窗口
root = tk.Tk()
root.title("Image Annotator")
# 创建 Figure 对象和 Axes 对象
fig, ax = plt.subplots()
ax.set_title("No Image Selected")
ax.set_xlabel("X")
ax.set_ylabel("Y")
# 将 Matplotlib 的 Figure 嵌入到 tkinter 窗口中
canvas = FigureCanvasTkAgg(fig, master=root)
canvas_widget = canvas.get_tk_widget()
canvas_widget.pack(fill=tk.BOTH, expand=True)
# 显示图片像素大小
pixel_text = ""
pixel_label = tk.Label(root, text=pixel_text)
pixel_label.pack(anchor='nw', padx=10, pady=10)
# 鼠标点击事件处理函数
def on_click(event):
if event.button == 1: # 左键点击
x = int(event.xdata)
y = int(event.ydata)
color = point_colors.value_selected
size = int(point_sizes.value_selected)
clicked_points_dict[current_image_index].append((x, y, color, size))
ax.clear()
ax.imshow(image_rgb)
for point in clicked_points_dict[current_image_index]:
x, y, point_color, point_size = point
color_rgb = color_mapping.get(point_color, (1.0, 0.0, 0.0)) # 默认红色
ax.plot(x, y, 'ro', markersize=point_size, markerfacecolor=color_rgb)
ax.annotate(f'({x}, {y})', (x, y), textcoords="offset points", xytext=(0,10), ha='center', fontsize=8, color=color_rgb)
ax.set_title("Click on the image to select points")
ax.set_xlabel("X")
ax.set_ylabel("Y")
canvas.draw()
# 载入图像
def load_image(path):
global image_rgb, width, height, pixel_text
image = cv2.imread(path)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
height, width = image.shape[:2]
pixel_text = f"Image Size: {width} x {height}"
pixel_label.config(text=pixel_text)
ax.clear()
ax.imshow(image_rgb)
ax.set_title("Click on the image to select points")
ax.set_xlabel("X")
ax.set_ylabel("Y")
canvas.draw()
# 加载当前索引的图像
def load_current_image():
global current_image_index
if current_image_index < 0 or current_image_index >= len(image_paths):
return
image_path = image_paths[current_image_index]
load_image(image_path)
# 加载上一张图像
def load_previous_image(event):
global current_image_index
current_image_index -= 1
if current_image_index < 0:
current_image_index = 0
load_current_image()
# 加载下一张图像
def load_next_image(event):
global current_image_index
current_image_index += 1
if current_image_index >= len(image_paths):
current_image_index = len(image_paths) - 1
load_current_image()
# 选择图像文件夹按钮点击事件处理函数
def choose_folder():
global image_paths, current_image_index, clicked_points_dict
folder_path = filedialog.askdirectory(title="Select Image Folder")
if folder_path:
image_paths = [os.path.join(folder_path, file) for file in sorted(os.listdir(folder_path)) if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp'))]
clicked_points_dict = {i: [] for i in range(len(image_paths))}
current_image_index = 0
load_current_image()
# 保存按钮点击事件处理函数
def save_image(event):
global clicked_points_dict
new_image = np.copy(image_rgb)
for point in clicked_points_dict[current_image_index]:
x, y, point_color, point_size = point
color_rgb = color_mapping.get(point_color, (1.0, 0.0, 0.0)) # 默认红色
cv2.circle(new_image, (x, y), point_size, (int(color_rgb[0]*255), int(color_rgb[1]*255), int(color_rgb[2]*255)), -1) # 标记点为指定颜色
save_path = filedialog.asksaveasfilename(defaultextension=".jpg", filetypes=[("JPEG files", "*.jpg")])
if save_path:
cv2.imwrite(save_path, cv2.cvtColor(new_image, cv2.COLOR_RGB2BGR))
print(f"Image with marked points saved to {save_path}")
# 窗口大小变化事件处理函数
def on_resize(event):
# 重新绘制图像以保持宽高比不变
load_current_image()
# 创建点颜色选择菜单
color_choices = ['Red', 'Yellow', 'Blue', 'Green']
ax_color = plt.axes([0.08, 0.9, 0.3, 0.1], aspect='equal')
point_colors = RadioButtons(ax_color, color_choices)
# 创建点大小选择菜单
size_choices = ['5', '10', '15', '20']
ax_size = plt.axes([0.28, 0.9, 0.1, 0.1], aspect='equal')
point_sizes = RadioButtons(ax_size, size_choices)
# 创建保存按钮
ax_save = plt.axes([0.9, 0.01, 0.05, 0.04])
btn_save = Button(ax_save, 'Save')
btn_save.on_clicked(save_image)
# 创建选择图像文件夹按钮
ax_choose_folder = plt.axes([0.65, 0.9, 0.28, 0.04])
btn_choose_folder = Button(ax_choose_folder, 'Choose Image Folder')
btn_choose_folder.on_clicked(lambda x: choose_folder())
# 创建上一张图像按钮
ax_previous_image = plt.axes([0.5, 0.01, 0.20, 0.04])
btn_previous_image = Button(ax_previous_image, 'Previous Image')
btn_previous_image.on_clicked(load_previous_image)
# 创建下一张图像按钮
ax_next_image = plt.axes([0.7, 0.01, 0.20, 0.04])
btn_next_image = Button(ax_next_image, 'Next Image')
btn_next_image.on_clicked(load_next_image)
# 绑定鼠标点击事件处理函数
fig.canvas.mpl_connect('button_press_event', on_click)
# 绑定窗口大小变化事件处理函数
fig.canvas.mpl_connect('resize_event', on_resize)
# 启动 tkinter 主循环
tk.mainloop()