import tkinter as tk
from tkinter import font
from PIL import Image, ImageTk
import cv2
import numpy as np
import time
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("Obj_Detection")
self.geometry('1200x600')
# 字体设置
self.font = font.Font(family='SimSun', size=28)
self.font2 = font.Font(family='SimSun', size=18)
self.font3 = font.Font(family='SimSun', size=14) # 缩小的字体
# 创建三个框架
self.left_frame = tk.Frame(self, width=640, height=600)
self.center_frame = tk.Frame(self, width=266, height=600)
self.right_frame = tk.Frame(self, width=266, height=600)
self.left_frame.grid(row=0, column=0, padx=1, pady=1)
self.center_frame.grid(row=0, column=1, padx=1, pady=1)
self.right_frame.grid(row=0, column=2, padx=1, pady=1)
# 左侧实时画面标签
self.label_left = tk.Label(self.left_frame, text="实时画面", font=self.font)
self.label_left.pack(pady=(10, 0))
# 实时显示检测结果
self.label2_left = tk.Label(self.left_frame, text="准备开始检测", font=self.font2)
self.label2_left.pack(pady=(10, 0))
# 中间目标图案标签
self.label_center_top = tk.Label(self.center_frame, text="目标图案", font=self.font)
self.label_center_top.pack(pady=(10, 0))
# 右侧Log标签
self.label_right_top = tk.Label(self.right_frame, text="Log 记录", font=self.font)
self.label_right_top.pack(pady=(10, 0))
self.log_text = tk.Text(self.right_frame, height=20, width=30)
self.log_text.pack(pady=(10, 0))
self.log("系统初始化完成")
# 按钮用于截图
self.button_capture = tk.Button(self.center_frame, text="截图后开启检测", command=self.start_capture)
self.button_capture.pack(pady=(10, 0))
# 设置相似度阈值
self.threshold_label = tk.Label(self.center_frame, text="全局边缘检测阈值:")
self.threshold_label.pack(pady=(10, 0))
self.threshold_slider = tk.Scale(self.center_frame, from_=0, to=255, resolution=1, orient=tk.HORIZONTAL)
self.threshold_slider.set(100)
self.threshold_slider.pack(pady=(0, 10))
# 删除所有选定区域的按钮
self.button_delete_all = tk.Button(self.center_frame, text="删除所有选定区域", command=self.delete_selected_region)
self.button_delete_all.pack(pady=(10, 0))
# 中间下方的标签用于显示缩略图
self.label_center_bottom = tk.Label(self.center_frame, text="缩略图")
self.label_center_bottom.pack(pady=(10, 0))
# 用于显示图像的Canvas
self.canvas_left = tk.Canvas(self.left_frame, width=640, height=480, scrollregion=(0, 0, 640, 480))
hbar = tk.Scrollbar(self.left_frame, orient=tk.HORIZONTAL)
hbar.pack(side=tk.BOTTOM, fill=tk.X)
hbar.config(command=self.canvas_left.xview)
vbar = tk.Scrollbar(self.left_frame, orient=tk.VERTICAL)
vbar.pack(side=tk.RIGHT, fill=tk.Y)
vbar.config(command=self.canvas_left.yview)
self.canvas_left.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
self.canvas_left.pack(pady=(10, 0))
# 图像存储
self.image_to_compare = None
self.target_regions = [] # 存储所有目标区域及其阈值
self.selection_start = None
self.selection_end = None
self.selection_tag = None
self.entries_layout = [] # 用于追踪输入框的位置
self.entry_width = 100 # 假设每个输入框的宽度
self.entry_height = 30 # 假设每个输入框的高度
self.current_x = 10 # 初始X位置
self.current_y = 10 # 初始Y位置
# 开启摄像头
self.cap = cv2.VideoCapture(0)
self.update_frame()
def update_frame(self):
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = Image.fromarray(frame)
img_tk = ImageTk.PhotoImage(image=img)
self.canvas_left.create_image(0, 0, anchor=tk.NW, image=img_tk)
self.canvas_left.img = img_tk
# 如果有目标区域,则进行边缘检测并框选
if self.target_regions:
for idx, (region, threshold) in enumerate(self.target_regions):
edges = self.detect_edges(frame, region, threshold)
self.display_results(frame, edges, region, idx+1)
self.after(15, self.update_frame)
def start_capture(self):
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.image_to_compare = Image.fromarray(frame)
self.image_to_compare.thumbnail((640, 480))
img_tk = ImageTk.PhotoImage(self.image_to_compare)
self.label_center_bottom.config(image=img_tk)
self.label_center_bottom.img = img_tk
self.log("截图成功")
self.canvas_left.bind("<Button-1>", self.on_click)
self.canvas_left.bind("<B1-Motion>", self.on_drag)
self.canvas_left.bind("<ButtonRelease-1>", self.on_release)
def on_click(self, event):
self.selection_start = (event.x, event.y)
def on_drag(self, event):
if self.selection_start:
self.canvas_left.delete(self.selection_tag)
self.selection_end = (event.x, event.y)
self.selection_tag = self.canvas_left.create_rectangle(
self.selection_start[0], self.selection_start[1],
event.x, event.y,
outline="blue", tags="selection_rect"
)
def on_release(self, event):
if self.selection_start and event.x != self.selection_start[0] and event.y != self.selection_start[1]:
new_region = (min(event.x, self.selection_start[0]), min(event.y, self.selection_start[1]),
abs(event.x - self.selection_start[0]), abs(event.y - self.selection_start[1]))
specific_threshold = tk.StringVar(value=str(self.threshold_slider.get()))
entry = tk.Entry(self.center_frame, textvariable=specific_threshold, width=10)
entry.pack(side=tk.TOP, padx=(self.current_x, 0), pady=(0, 10))
self.current_x += self.entry_width + 10 # 间距为10像素
if self.current_x > self.center_frame.winfo_width() - self.entry_width:
self.current_x = 10 # 重置X位置
self.current_y += self.entry_height + 10 # 更新Y位置
self.entries_layout.append(entry)
self.target_regions.append((new_region, specific_threshold))
self.canvas_left.delete(self.selection_tag)
self.canvas_left.create_rectangle(
new_region[0], new_region[1],
new_region[0] + new_region[2], new_region[1] + new_region[3],
outline="blue", tags="selection_rect"
)
self.log("目标区域选定")
def detect_edges(self, frame, region, threshold=None):
# 截取目标区域
target_region = frame[
region[1]:region[1] + region[3],
region[0]:region[0] + region[2]
]
# 将图像转换为灰度图像
gray = cv2.cvtColor(target_region, cv2.COLOR_RGB2GRAY)
# 使用Canny算法检测边缘
low_threshold = int(threshold.get()) if threshold else self.threshold_slider.get()
high_threshold = low_threshold * 3 # Canny通常建议高阈值是低阈值的两到三倍
edges = cv2.Canny(gray, low_threshold, high_threshold)
# 将边缘图像转换回PIL格式以便在GUI中显示
edges_img = Image.fromarray(edges)
return edges_img
def display_results(self, frame, edges_img, region, index):
# 在实时画面上框选目标区域
cv2.rectangle(frame, (region[0], region[1]),
(region[0] + region[2], region[1] + region[3]),
(255, 0, 0), 2)
# 显示编号(半透明)
cv2.putText(frame, str(index), (region[0], region[1]-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0, 128), 2)
# 在目标区域中心显示黄色十字线及坐标
center_x = region[0] + region[2] // 2
center_y = region[1] + region[3] // 2
cv2.line(frame, (center_x - 5, center_y), (center_x + 5, center_y), (0, 255, 255), 2)
cv2.line(frame, (center_x, center_y - 5), (center_x, center_y + 5), (0, 255, 255), 2)
# 显示坐标
cv2.putText(frame, f"({center_x}, {center_y})", (region[0] + region[2] - 60, region[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 255), 1)
# 在目标区域内绘制边缘
edges_array = np.array(edges_img)
edges_array = cv2.cvtColor(edges_array, cv2.COLOR_GRAY2RGB)
frame[region[1]:region[1] + region[3],
region[0]:region[0] + region[2]] = edges_array
img = Image.fromarray(frame)
img_tk = ImageTk.PhotoImage(image=img)
self.canvas_left.create_image(0, 0, anchor=tk.NW, image=img_tk)
self.canvas_left.img = img_tk
def delete_selected_region(self):
if self.target_regions:
self.canvas_left.delete("selection_rect")
for entry in self.entries_layout:
entry.destroy()
self.entries_layout.clear()
self.target_regions.clear()
self.current_x = 10 # 重置X位置
self.current_y = 10 # 重置Y位置
self.log("所有选定区域已删除")
def log(self, message):
current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
self.log_text.insert(tk.END, f"{current_time}: {message}\n")
self.log_text.see(tk.END)
if __name__ == "__main__":
app = Application()
app.mainloop()