python 中使用tkinter构建一个图片的剪切器-附源码
由于项目需要,需要构建一个间的软件,方便查看图片的剪切的位置,并对其中的图像进行分析,实现如下的功能
- 简单的UI
- 加载图片
- 剪切图片
- 显示剪切后的图片
- 针对图片的内容进行识别
- 图片质量分析
前端的具体代码如下,
有需要其他功能的,需定制化开发的,得加钱
import tkinter as tk
from tkinter import filedialog , Toplevel, Label
from PIL import Image, ImageTk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt
"""
v8.修改整体的布局
"""
def img_analysis(img_name):
img=cv2.imread(filename)
part_img=img[540:673,371:702]##y1,y2,x1,x2
img_grey=cv2.cvtColor(part_img,cv2.COLOR_RGB2GRAY)#灰度图片#COLOR_BGR2GRAY COLOR_RGB2GRAY IMREAD_GRAYSCALE
img_grey_draw=img_grey#用于画图
hist = cv2.calcHist([img_grey],[0],None,[256],[0,255]) # 应用高斯模糊,以减少图像噪声 #统计直方图数据
ret, thresh = cv2.threshold(img_grey, 80, 255, cv2.THRESH_BINARY) #80# 应用二值化
# ------------计算黑色像素的数量
black_pixels = np.count_nonzero(thresh == 0)# 计算黑色像素的数量
total_pixels = thresh.shape[0] * thresh.shape[1]# ------------计算总的像素数量
black_ratio = black_pixels / total_pixels# ------------计算黑色像素的占比print(f"黑色像素的占比: {black_ratio:.4f}")
# 数据
black = black_ratio*100
white = 100-black
# 标签
labels = ['Black', 'White']
edges = cv2.Canny(thresh, threshold1=100, threshold2=200)# 应用Canny边缘检测
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 寻找轮廓
cv2.drawContours(img_grey_draw, contours, -1, (0, 255, 0), 3)# 绘制轮廓
#new windows to show the feature of opencv
class ImageGridWindow(Toplevel):
def __init__(self, master, images):
super().__init__(master)
self.title("Image Grid")
# 返回按钮
self.return_button = tk.Button(self, text="Return", command=self.destroy)
self.return_button.pack(side=tk.BOTTOM, fill=tk.X, pady=10)
# 假设images是一个包含图片路径的列表,且长度为9
self.grid_size = 3
self.create_widgets(images)
def create_widgets(self, images):
for i, img_path in enumerate(images):
# 这里假设所有图片大小相同,或者您已经根据需要进行了调整
img = Image.open(img_path)
img = img.resize((100, 100), Image.ANTIALIAS) # 假设每个图片调整为100x100
photo = ImageTk.PhotoImage(img)
label = Label(self, image=photo)
label.image = photo # 避免垃圾回收
row, col = divmod(i, self.grid_size)
label.grid(row=row, column=col, padx=5, pady=5)
#Everybody to reset your data.
class ImageLabelerApp:
def __init__(self, root):
self.root = root
self.root.title("Image Labeler")
#------------------------ 顶部按钮
# 创建一个新的框架用于grid布局
top_frame = tk.Frame(root)
top_frame.pack(fill=tk.X, expand=False)
self.load_button = tk.Button(top_frame, text="Load Image", command=self.load_image)
self.load_button.grid(row=0, column=1, padx=10,pady=10)
# Tkinter reset button
self.reset_button = tk.Button(top_frame, text="Reset", command=self.reset)
self.reset_button.grid(row=0, column=2, padx=10)
# Tkinter button
self.button = tk.Button(top_frame, text="Show Cropped Image", command=self.show_cropped_image)
self.button.grid(row=0, column=3, padx=10)
# 添加打开图片网格的按钮
self.grid_button = tk.Button(top_frame, text="Open Image Grid", command=self.open_image_grid)
self.grid_button.grid(row=0, column=4, padx=10)
#------------------------ 中部图片
# 底部左右两个图像显示区域
bottom_images_frame = tk.Frame(root)
bottom_images_frame.pack(fill=tk.BOTH, expand=True)
# ------------right_image_label Label for cropped image
self.cropped_label = tk.Label(bottom_images_frame, bg='white') #, width=40, height=20
self.cropped_label.grid(row=0, column=1, sticky='nsew', padx=5, pady=5)
#------------left image
#left_image_label = tk.Label(bottom_images_frame, bg='white', width=40, height=20)
#left_image_label.grid(row=0, column=0, sticky='nsew', padx=5, pady=5)
self.fig, self.ax = plt.subplots(figsize=(6, 6))
self.canvas = FigureCanvasTkAgg(self.fig, master=bottom_images_frame)
self.canvas.get_tk_widget().grid(row=0, column=0, sticky='nsew', padx=5, pady=5)
# 添加文本框来显示矩形坐标
# #------------------------ 底部文字
self.coord_label = tk.Label(root, text="No rectangle selected", width=30, height=20)
self.coord_label.pack(side=tk.BOTTOM, fill=tk.X,padx=10)
self.img_path = None
self.img = None
self.rect = None
self.drawing = False
self.cropped_img = None
self.cropped_photo = None
# Connect events
self.canvas.mpl_connect('button_press_event', self.on_button_press)
self.canvas.mpl_connect('button_release_event', self.on_button_release) # 绑定事件以在鼠标释放时更新坐标
self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
def load_image(self):
path=filedialog.askopenfilename(filetypes=[("Image files", "*.jpg")])#;*.png;*.gif 09-07 only jpg,
self.img_path = path
self.img = plt.imread(path)
self.ax.imshow(self.img)
self.ax.set_title('Original Image')
self.canvas.draw()
def on_button_press(self, event):
if not self.rect and event.button == 1:
self.x0, self.y0 = event.xdata, event.ydata
self.drawing = True
def on_mouse_move(self, event):
if self.drawing:
x1, y1 = event.xdata, event.ydata
if self.rect is None:
self.rect = Rectangle((self.x0, self.y0), 0, 0, edgecolor='r', facecolor='none')
self.ax.add_patch(self.rect)
self.rect.set_width(x1 - self.x0)
self.rect.set_height(y1 - self.y0)
self.canvas.draw()
def on_button_release(self, event):
if self.drawing and event.button == 1:
self.drawing = False
# -----------------检查是否正在绘制矩形 -----------------
if self.rect is not None:
# 获取矩形的坐标(注意:这里获取的是数据坐标,不是像素坐标)
x0, y0 = self.rect.get_xy()
width, height = self.rect.get_width(), self.rect.get_height()
x1, y1 = x0 + width, y0 + height
# 格式化坐标字符串
coord_str = f"Rectangle Coordinates: ({x0:.2f}, {y0:.2f}) to ({x1:.2f}, {y1:.2f})"
# 更新文本框的文本
self.coord_label.config(text=coord_str)
def show_cropped_image(self):
if self.rect:
x0, y0 = int(self.rect.get_x()), int(self.rect.get_y())
width, height = int(self.rect.get_width()), int(self.rect.get_height())
cropped_img = self.img[y0:y0+height, x0:x0+width]
self.cropped_img = Image.fromarray(cropped_img)
self.cropped_photo = ImageTk.PhotoImage(self.cropped_img)
self.cropped_label.config(image=self.cropped_photo)
self.cropped_label.image = self.cropped_photo # Keep a reference
def reset(self):
# 清除Axes上的所有内容(包括矩形)
self.ax.clear()
self.ax.imshow(self.img) # 重新加载原始图像
self.ax.set_title('Original Image')
self.cropped_img = None
self.cropped_photo = None
self.rect = None # 如果之前绘制了矩形,则将其设置为None
self.canvas.draw()# 重新绘制画布
def open_image_grid(self):
# 这里需要一个图片列表,这里只是示例
# 假设您已经有了一个包含9个图片路径的列表
image_paths = [
'path/to/image1.jpg', 'path/to/image2.jpg', 'path/to/image3.jpg',
'path/to/image4.jpg', 'path/to/image5.jpg', 'path/to/image6.jpg',
'path/to/image7.jpg', 'path/to/image8.jpg', 'path/to/image9.jpg'
]
ImageGridWindow(self.root, image_paths)
def main():
root = tk.Tk()
app = ImageLabelerApp(root)
root.mainloop()
if __name__ == '__main__':
main()