当前位置: 首页 > news >正文

图像捕捉---Base On NCC


 

归一化互相关系数方法的工作原理

归一化的互相关系数(Normalized Cross-Correlation, NCC)是一种测量两个函数之间相似度的方法。对于图像处理而言,NCC 是通过比较模板图像和目标图像中的局部区域来评估它们之间的相似性的。

具体来说,对于每个模板可能的位置,NCC 计算如下:

\text{NCC}(x, y) = \frac{\sum_{i,j} (I(x+i, y+j) - \mu_I)(T(i, j) - \mu_T)}{\sqrt{\sum_{i,j}(I(x+i, y+j) - \mu_I)^2 \sum_{i,j}(T(i, j) - \mu_T)^2}}NCC(x,y)=∑i,j​(I(x+i,y+j)−μI​)2∑i,j​(T(i,j)−μT​)2​∑i,j​(I(x+i,y+j)−μI​)(T(i,j)−μT​)​

其中 II 是源图像,TT 是模板图像,\mu_IμI​ 和 \mu_TμT​ 分别是源图像局部区域和模板图像的均值。

----
 

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

FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"

class ImageVideoApp:
    def __init__(self, master):
        self.master = master
        master.title("图像编辑器")
        
        # 初始化变量
        self.image_path = ""
        self.template_path = ""
        self.click_count = 0
        self.points = []
        self.rect_start = None
        self.rect_end = None
        self.scan_direction = "up_to_down"
        self.selected_region = []

        # 日志文件路径
        self.log_file_path = r"D:\Picture_By\log.txt"

        # 左侧图像显示区
        self.left_frame = tk.Frame(master)
        self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.left_image_label = tk.Label(self.left_frame)
        self.left_image_label.pack()

        # 右侧图像编辑区
        self.right_frame = tk.Frame(master)
        self.right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        self.right_image_label = tk.Label(self.right_frame)
        self.right_image_label.pack()

        # 加载图像按钮
        self.load_image_button = tk.Button(master, text="加载图像", command=self.load_image)
        self.load_image_button.pack(pady=10)

        # 创建模板选择下拉菜单
        self.template_frame = tk.Frame(master)
        self.template_frame.pack(pady=10)
        self.template_var = tk.StringVar()
        self.template_var.set('请选择模板')  # 设置默认值
        self.template_menu = tk.OptionMenu(self.template_frame, self.template_var, '', command=self.load_template)
        self.template_menu.pack()

        # 文件名输入框
        self.filename_entry = tk.Entry(master)
        self.filename_entry.insert(0, "请输入模版文件名")
        self.filename_entry.pack(pady=10)

        # 编辑功能按钮
        self.save_template_button = tk.Button(master, text="保存为模板", command=self.save_template)
        self.save_template_button.pack(pady=10)

        # 添加开始检测按钮
        self.detect_button = tk.Button(master, text="开始检测", command=self.match_template)
        self.detect_button.pack(pady=10)

        # 匹配结果显示标签
        self.match_result_label = tk.Label(master)
        self.match_result_label.pack(side=tk.BOTTOM, pady=10)

        # 绑定鼠标事件
        self.left_image_label.bind("<Button-1>", self.on_click_start)
        self.left_image_label.bind("<B1-Motion>", self.on_click_drag)
        self.left_image_label.bind("<ButtonRelease-1>", self.on_click_release)

        # 初始化模板列表
        self.update_template_list()

    def update_template_list(self):
        self.template_options = []
        for filename in glob.glob(r"D:\Picture_By\*_muban.*"):
            self.template_options.append(os.path.basename(filename))
        self.template_menu['menu'].delete(0, 'end')
        for option in self.template_options:
            menu = self.template_menu['menu']
            menu.add_command(label=option, command=lambda fn=option: self.load_template(fn))
        if self.template_options:
            self.template_var.set(self.template_options[0])  # 设置第一个模板作为默认值
        else:
            self.template_var.set('请选择模板')  # 如果没有模板,则设置为默认值

    def log_event(self, message):
        """记录事件到日志文件"""
        with open(self.log_file_path, 'a') as log_file:
            log_file.write(f"{datetime.datetime.now()} - {message}\n")

    def load_image(self):
        self.image_path = filedialog.askopenfilename(title="选择图像文件", filetypes=[("图像文件", "*.jpg *.png *.bmp")])
        if self.image_path:
            self.display_image()
            self.log_event("加载图像")

    def display_image(self):
        img = cv2.imread(self.image_path)
        if img is None:
            messagebox.showerror("错误", "无法加载图像,请检查路径是否正确!")
            return

        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img_tk = ImageTk.PhotoImage(Image.fromarray(img_rgb))

        self.left_image_label.config(image=img_tk)
        self.left_image_label.image = img_tk

    def load_template(self, path):
        self.template_path = os.path.join("D:\\Picture_By", path)
        img = cv2.imread(self.template_path)
        if img is None:
            messagebox.showerror("错误", "无法加载模板,请检查路径是否正确!")
            return

        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img_tk = ImageTk.PhotoImage(Image.fromarray(img_rgb))
        self.right_image_label.config(image=img_tk)
        self.right_image_label.image = img_tk
        self.log_event(f"加载模板: {path}")

    def match_template(self):
        if self.image_path and self.template_path:
            self.log_event("开始检测")
            image = cv2.imread(self.image_path)
            template = cv2.imread(self.template_path)
            res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
            threshold = 0.8
            
            # 使用 np.argmax 找到最大相关性位置
            max_loc = np.argmax(res)
            loc = np.unravel_index(max_loc, res.shape)
            
            # 检查最大相关性是否超过阈值
            if res[loc] >= threshold:
                pt = (loc[1], loc[0])  # 注意:由于OpenCV坐标系统,这里需要交换顺序
                
                # 计算模板在图像中的右下角坐标
                bottom_right = (pt[0] + template.shape[1], pt[1] + template.shape[0])
                
                # 绘制匹配区域的矩形框
                cv2.rectangle(image, pt, bottom_right, (0, 0, 255), 2)
                
                # 获取匹配区域的四个角点
                corners = [
                    (pt[0], pt[1]),
                    (pt[0] + template.shape[1], pt[1]),
                    (pt[0] + template.shape[1], pt[1] + template.shape[0]),
                    (pt[0], pt[1] + template.shape[0])
                ]
                
                # 使用最小外接矩形计算角度
                rect = cv2.minAreaRect(np.array(corners))
                width = int(rect[1][0])
                height = int(rect[1][1])
                angle = rect[2]
                
                # 根据宽高调整角度
                if width < height:
                    angle += 90
                
                # 显示坐标和角度
                cv2.putText(image, f"({pt[0]}, {pt[1]}, Angle: {angle:.2f})", (pt[0], pt[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

                # 在右下角显示检测结果
                self.draw_detection_result(image, "Finished Detection", fontPath=FONT_PATH)

                img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                img_tk = ImageTk.PhotoImage(Image.fromarray(img_rgb))
                self.right_image_label.config(image=img_tk, bg='blue')
                self.right_image_label.image = img_tk
                self.log_event("完成检测")
            else:
                messagebox.showinfo("Info", "No result found with higher than threshold.")

    def draw_detection_result(self, img, text, fontPath=None, fontScale=1, thickness=2):
        # 使用支持中文的字体文件
        font = cv2.FONT_HERSHEY_SIMPLEX if fontPath is None else cv2.FONT_HERSHEY_SIMPLEX
        font = cv2.FONT_HERSHEY_SIMPLEX if fontPath is None else cv2.QT_FONT_NORMAL
        
        # 加载字体
        font = cv2.QT_FONT_NORMAL if fontPath is None else cv2.imread(fontPath)
        font = cv2.FONT_HERSHEY_SIMPLEX if fontPath is None else cv2.QT_FONT_NORMAL
        font = cv2.FONT_HERSHEY_SIMPLEX if fontPath is None else cv2.QT_FONT_NORMAL
        
        # 使用支持中文的字体文件
        font = cv2.FONT_HERSHEY_SIMPLEX if fontPath is None else cv2.QT_FONT_NORMAL
        font = cv2.QT_FONT_NORMAL if fontPath is None else cv2.FONT_HERSHEY_SIMPLEX
        
        # 计算文字的位置(右下角)
        (text_width, text_height), _ = cv2.getTextSize(text, font, fontScale, thickness)
        img_height, img_width = img.shape[:2]
        text_position = (img_width - text_width - 10, img_height - text_height - 10)
        
        # 使用黑色字体
        cv2.putText(img, text, text_position, font, fontScale, (0, 0, 0), thickness, lineType=cv2.LINE_AA)

    def on_click_start(self, event):
        self.rect_start = (event.x, event.y)
        self.log_event("开始绘制矩形区域")

    def on_click_drag(self, event):
        self.rect_end = (event.x, event.y)
        self.update_image_with_rectangle()
        self.log_event("拖动矩形区域")

    def on_click_release(self, event):
        self.rect_end = (event.x, event.y)
        self.update_image_with_rectangle()
        self.move_selection_to_edit_area()
        self.log_event("释放绘制矩形区域")

    def update_image_with_rectangle(self):
        if self.rect_start and self.rect_end:
            img = cv2.imread(self.image_path)
            if img is None:
                messagebox.showerror("错误", "无法加载图像,请检查路径是否正确!")
                return

            img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            img_tk = ImageTk.PhotoImage(Image.fromarray(img_rgb))

            x1, y1 = min(self.rect_start[0], self.rect_end[0]), min(self.rect_start[1], self.rect_end[1])
            x2, y2 = max(self.rect_start[0], self.rect_end[0]), max(self.rect_start[1], self.rect_end[1])

            cv2.rectangle(img_rgb, (x1, y1), (x2, y2), (255, 0, 0), 2)
            img_tk = ImageTk.PhotoImage(Image.fromarray(img_rgb))

            self.left_image_label.config(image=img_tk)
            self.left_image_label.image = img_tk

            self.selected_region = [x1, y1, x2, y2]

    def move_selection_to_edit_area(self):
        if self.selected_region:
            img = cv2.imread(self.image_path)
            if img is None:
                messagebox.showerror("错误", "无法加载图像,请检查路径是否正确!")
                return

            x1, y1, x2, y2 = self.selected_region
            cropped_img = img[y1:y2, x1:x2]
            cropped_img_rgb = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2RGB)
            cropped_img_tk = ImageTk.PhotoImage(Image.fromarray(cropped_img_rgb))

            self.right_image_label.config(image=cropped_img_tk)
            self.right_image_label.image = cropped_img_tk
            self.log_event("移动选择到编辑区域")

    def save_template(self):
        filename = self.filename_entry.get().strip()
        if not filename:
            messagebox.showerror("错误", "请输入文件名!")
            return

        if self.right_image_label.image:
            pil_image = ImageTk.getimage(self.right_image_label.image)
            cropped_img_rgb = np.array(pil_image)
            cropped_img_bgr = cv2.cvtColor(cropped_img_rgb, cv2.COLOR_RGB2BGR)
            
            template_path = os.path.join("D:\\Picture_By", f"{filename}_muban.jpg")
            try:
                cv2.imwrite(template_path, cropped_img_bgr)
                messagebox.showinfo("成功", f"模板已保存为 {template_path}!")
                self.update_template_list()
                self.log_event(f"保存模板: {filename}")
            except UnicodeEncodeError:
                messagebox.showerror("错误", "文件名包含无效字符,请检查文件名!")
                self.log_event("保存模板失败: 文件名包含无效字符")
            except Exception as e:
                messagebox.showerror("错误", f"保存文件时发生错误:{str(e)}")
                self.log_event(f"保存模板失败: {str(e)}")

    def draw_line_and_circle(self, img):
        if self.selected_region and len(self.selected_region) >= 2:
            # 这里可以添加逻辑来绘制线条和圆圈
            pass

def main():
    root = tk.Tk()
    app = ImageVideoApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()


 


http://www.mrgr.cn/news/54436.html

相关文章:

  • 第一章 计算机基础(一)
  • AI应用程序低代码构建平台Langflow
  • android 与网页交互通过网页修改宿主布局和异常处理——未来之窗行业应用跨平台架构
  • 测试教程分享
  • 提升SQL技能,掌握数据分析
  • Java中的Arrays类
  • neo4j 中日期时间 时间戳处理
  • 正确使用内部类
  • 是什么决定了我们毕业后的能力增长?
  • 【Python-AI篇】人工智能python基础-计算机组成原理
  • armbian 青龙面板
  • 超详细介绍bash脚本相关细节
  • 运算符优先级有没有通用原则?
  • 点餐小程序实战教程20广告管理
  • PCL点云库 概览
  • 大数据hive(二)
  • 批量归一化Batch Norm
  • 【PyTorch 】【CUDA】深入了解 PyTorch 中的 CUDA 和 cuDNN 版本及 GPU 信息
  • 在MySQL中建索引时需要注意哪些事项?
  • vue查缺补漏
  • 不同jdk版本中的接口规范
  • Python生成随机密码脚本
  • curl,nc和telnet的用法以及其他常用工具(nc代理与重定向)
  • 用 CSS 和 JS 打造简约又不失亮点的客户评价展示
  • 【Python】基础语法
  • 【Linux】main函数的参数列表从何而来?