Camera_V6_Add_XYAngle_0.25_360To20


import cv2
import tkinter as tk
from tkinter import ttk, simpledialog
from tkinter import messagebox
from PIL import Image, ImageTk
import numpy as np
import os
import datetime

class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Camera_Match")
        self.geometry("800x600")

        # 初始化全局变量
        self.ix, self.iy = -1, -1
        self.roi_set = False
        self.roi = None
        self.drawing = False
        self.template_loaded = False
        self.match_done = False
        self.template = None
        self.method = tk.StringVar(value="cv2.TM_CCOEFF_NORMED")  # 默认匹配方法
        self.results_text = ""
        self.matches = []
        self.match_status = tk.StringVar(value="等待开始")

        # 打开摄像头
        self.cap = cv2.VideoCapture(0)

        # 获取相似度阈值
        self.threshold = self.get_threshold()

        # 创建主画布
        self.canvas = tk.Canvas(self, width=640, height=480)
        self.canvas.pack(side=tk.TOP, padx=10, pady=10)

        # 创建状态标签
        self.status_label = tk.Label(self, textvariable=self.match_status, font=("Arial", 16), fg="red")
        self.status_label.pack(side=tk.TOP)

        # 创建退出按钮
        self.quit_button = tk.Button(self, text="退出", command=self.quit)
        self.quit_button.pack(side=tk.TOP, padx=10, pady=10)

        # 创建选择框
        self.method_frame = tk.Frame(self)
        self.method_frame.pack(side=tk.TOP, padx=10, pady=10)

        self.method_label = tk.Label(self.method_frame, text="匹配方法:")
        self.method_label.pack(side=tk.LEFT, padx=5)

        methods = [
            ("模板匹配", "cv2.TM_CCOEFF_NORMED"),
            ("其他", "cv2.normalize"),  # 这里只是为了示例,实际上 cv2.normalize 不是一个匹配方法
        ]

        for text, mode in methods:
            b = tk.Radiobutton(self.method_frame, text=text, variable=self.method, value=mode, command=self.select_method)
            b.pack(side=tk.LEFT)

        # 创建用于显示操作记录和匹配结果的区域
        self.results_frame = tk.Frame(self)
        self.results_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)

        self.results_label = tk.Label(self.results_frame, text="操作记录与匹配结果:")
        self.results_label.pack(side=tk.TOP, padx=5, pady=5)

        self.results_textbox = tk.Text(self.results_frame, wrap='word', height=10, width=40)
        self.results_textbox.pack(side=tk.TOP, padx=5, pady=5)

        # 绑定鼠标事件
        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<B1-Motion>", self.on_mouse_motion)
        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)

        # 启动摄像头读取和显示循环
        self.update_frame()

    def select_method(self):
        self.results_text += f"选择了匹配方法:{self.method.get()}\n"
        self.update_results_text()

    def on_button_press(self, event):
        self.drawing = True
        self.ix, self.iy = event.x, event.y

    def on_mouse_motion(self, event):
        if self.drawing:
            self.canvas.delete("rect")
            self.canvas.create_rectangle(min(self.ix, event.x), min(self.iy, event.y),
                                         max(self.ix, event.x), max(self.iy, event.y),
                                         outline="green", tag="rect")

    def on_button_release(self, event):
        if self.drawing:
            self.drawing = False
            self.canvas.delete("rect")
            self.ix, self.iy = min(self.ix, event.x), min(self.iy, event.y)
            self.roi = self.frame[min(self.iy, event.y):max(self.iy, event.y),
                                  min(self.ix, event.x):max(self.ix, event.x)]
            self.roi_set = True
            self.template = self.roi
            self.results_text += f"选择了区域:({min(self.ix, event.x)}, {min(self.iy, event.y)}) -> ({max(self.ix, event.x)}, {max(self.iy, event.y)})\n"
            self.update_results_text()
            self.match_status.set("匹配中")

    def update_results_text(self):
        self.results_textbox.delete(1.0, tk.END)
        self.results_textbox.insert(tk.END, self.results_text)

    def update_frame(self):
        ret, frame = self.cap.read()
        if ret:
            frame = cv2.flip(frame, 1)  # 镜像翻转图像

            # 确保 frame 是一个有效的 numpy 数组
            self.frame = frame.copy()  # 使用 .copy() 来避免引用问题

            # 如果已经设置了 ROI,则进行模板匹配
            if self.roi_set and self.template is not None and self.template.size > 0:
                method = eval(self.method.get())
                if method == cv2.TM_CCOEFF_NORMED:
                    best_match = None
                    best_match_degree = 0.0
                    best_match_angle = 0
                    angles = range(0, 20, 1)  # 每隔1度旋转一次模板,最大360

                    for angle in angles:
                        # 旋转模板
                        (h, w) = self.template.shape[:2]
                        center = (w // 2, h // 2)
                        M = cv2.getRotationMatrix2D(center, angle, 1.0)
                        rotated_template = cv2.warpAffine(self.template, M, (w, h))

                        # 更细致的图像预处理
                        gray_rotated_template = cv2.cvtColor(rotated_template, cv2.COLOR_BGR2GRAY)
                        gray_frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
                        gray_rotated_template = cv2.GaussianBlur(gray_rotated_template, (5, 5), 0)
                        gray_frame = cv2.GaussianBlur(gray_frame, (5, 5), 0)
                        edges_rotated_template = cv2.Canny(gray_rotated_template, 50, 150)
                        edges_frame = cv2.Canny(gray_frame, 50, 150)

                        # 模板匹配
                        res = cv2.matchTemplate(edges_frame, edges_rotated_template, method)
                        loc = np.where(res >= self.threshold)
                        matches = list(zip(*loc[::-1]))

                        if len(matches) > 0:
                            # 寻找最大匹配度的位置
                            for pt in matches:
                                match_degree = res[pt[1], pt[0]]
                                if match_degree > best_match_degree:
                                    best_match_degree = match_degree
                                    best_match = pt
                                    best_match_angle = angle

                    if best_match is not None:
                        # 绘制最佳匹配的矩形框
                        template_h, template_w = self.template.shape[:2]
                        cv2.rectangle(self.frame, best_match, (best_match[0] + template_w, best_match[1] + template_h), (0, 0, 255), 2)
                        
                        # 计算并显示匹配度百分比及位置和角度
                        match_degree_percent = best_match_degree * 100
                        match_info = f"{match_degree_percent:.2f}% X:{best_match[0]}, Y:{best_match[1]}, Angle:{best_match_angle}"
                        cv2.putText(self.frame, match_info, (best_match[0], best_match[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
                        
                        self.results_text += f"最佳匹配位置: {best_match}, 匹配度: {match_degree_percent:.2f}% 角度: {best_match_angle}\n"
                        self.matches.append(True)
                        self.match_status.set("匹配完成")
                        self.log_best_match(best_match, match_degree_percent, best_match_angle)
                    else:
                        self.match_status.set("匹配失败")
                    self.update_results_text()

            # 将OpenCV BGR格式转换为Tkinter PhotoImage格式
            frame_tk = self.cv_to_tk(self.frame)
            self.canvas.create_image(0, 0, anchor=tk.NW, image=frame_tk)
            self.canvas.image = frame_tk

        # 更新并重复调用
        self.after(30, self.update_frame)

    def get_threshold(self):
        try:
            threshold = simpledialog.askfloat("相似度阈值", "请输入一个介于 0.01 和 0.99 之间的数值(默认 0.75):", initialvalue=0.75, minvalue=0.01, maxvalue=0.99)
            if threshold is not None:
                return threshold
            else:
                return 0.75
        except Exception as e:
            messagebox.showerror("错误", f"输入错误: {e}")
            return 0.75

    def log_best_match(self, match_position, match_degree, match_angle):
        log_dir = "D:\\dd"
        log_file_path = os.path.join(log_dir, "log.txt")
        
        # 检查目录是否存在,如果不存在则创建
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        with open(log_file_path, "a") as file:
            file.write(f"{current_time} 最佳匹配位置: {match_position}, 匹配度: {match_degree:.2f}% 角度: {match_angle}\n")

    @staticmethod
    def cv_to_tk(image_cv):
        image_cv_rgb = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB)
        image_pil = Image.fromarray(image_cv_rgb)
        return ImageTk.PhotoImage(image_pil)

if __name__ == "__main__":
    app = Application()
    app.mainloop()

    # 释放资源
    app.cap.release()
 



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值