题目: 使用 Python 和 OpenCV 实现一个简单的物体边缘检测程序。
要求通过以下四个按钮实现:
读取一张本地的彩色图片。
将彩色图片转换为灰度图片。
使用 Canny 边缘检测算法对灰度图片进行边缘检测。
显示原始彩色图片和边缘检测后的图片(在窗口的左侧)。


import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk


class ImageProcessingApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Image Processing")
        self.image = None
        self.gray = None
        self.edges = None

        # 创建左侧图像显示区域
        self.image_frame = tk.Frame(root)
        self.image_frame.pack(side=tk.LEFT, padx=10, pady=10)

        # 创建右侧按钮区域
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(side=tk.RIGHT, padx=10, pady=10)

        # 创建按钮
        self.read_button = tk.Button(self.button_frame, text="读取一张本地的彩色图片", command=self.read_image)
        self.read_button.pack(pady=5)

        self.gray_button = tk.Button(self.button_frame, text="将彩色图片转换为灰度图片", command=self.convert_to_gray,
                                     state=tk.DISABLED)
        self.gray_button.pack(pady=5)

        self.canny_button = tk.Button(self.button_frame, text="使用 Canny 边缘检测算法对灰度图片进行边缘检测",
                                      command=self.apply_canny, state=tk.DISABLED)
        self.canny_button.pack(pady=5)

        self.display_button = tk.Button(self.button_frame, text="显示原始彩色图片和边缘检测后的图片",
                                        command=self.display_images,
                                        state=tk.DISABLED)
        self.display_button.pack(pady=5)

        # 创建图像显示标签
        self.original_label = tk.Label(self.image_frame)
        self.original_label.pack(pady=5)

        self.edges_label = tk.Label(self.image_frame)
        self.edges_label.pack(pady=5)

        self.combined_label = tk.Label(self.image_frame)
        self.combined_label.pack(pady=5)

    def read_image(self):
        file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg;*.png;*.jpeg")])
        if file_path:
            self.image = cv2.imread(file_path)
            if self.image is not None:
                # 获取屏幕尺寸
                screen_width = 1920
                screen_height = 1080
                # 计算压缩后的尺寸
                target_width = int(screen_width / 4)
                target_height = int(screen_height / 4)
                # 压缩图像
                self.image = cv2.resize(self.image, (target_width, target_height))
                self.show_image(self.original_label, self.image)
                self.gray_button.config(state=tk.NORMAL)
                self.read_button.config(state=tk.DISABLED)

    def convert_to_gray(self):
        self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        self.canny_button.config(state=tk.NORMAL)
        self.gray_button.config(state=tk.DISABLED)

    def apply_canny(self):
        self.edges = cv2.Canny(self.gray, 100, 200)
        edges_color = cv2.cvtColor(self.edges, cv2.COLOR_GRAY2BGR)
        self.show_image(self.edges_label, edges_color)
        self.display_button.config(state=tk.NORMAL)
        self.canny_button.config(state=tk.DISABLED)

    def display_images(self):
        edges_color = cv2.cvtColor(self.edges, cv2.COLOR_GRAY2BGR)
        combined_image = np.hstack((self.image, edges_color))
        self.show_image(self.combined_label, combined_image)
        self.display_button.config(state=tk.DISABLED)

    def show_image(self, label, img):
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)
        img = ImageTk.PhotoImage(image=img)
        label.config(image=img)
        label.image = img


if __name__ == "__main__":
    root = tk.Tk()
    app = ImageProcessingApp(root)
    root.mainloop()

整体功能概述

这段 Python 代码借助 tkinter 库构建图形用户界面(GUI),同时使用 OpenCV 库来处理图像。GUI 界面分为左右两部分,左侧用于展示图像,右侧有四个按钮,用户点击这些按钮能依次完成图像的读取、灰度转换、边缘检测以及图像合并显示等操作。

代码详细解释

1. 导入必要的库

python

import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk

 

  • cv2:OpenCV 库,用于图像处理。
  • numpy:用于处理数组和矩阵,在图像数据处理中经常用到。
  • tkinter:Python 的标准 GUI 库,用于创建图形用户界面。
  • filedialogtkinter 的子模块,用于打开文件选择对话框。
  • Image 和 ImageTkPIL(Python Imaging Library)库的一部分,用于处理和显示图像。

2. 定义 ImageProcessingApp 类

python

class ImageProcessingApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Image Processing")
        self.image = None
        self.gray = None
        self.edges = None

 

  • __init__ 是类的构造函数,在创建类的实例时会自动调用。
  • self.root:存储 tkinter 的主窗口对象。
  • self.root.title("Image Processing"):设置主窗口的标题为 "Image Processing"。
  • self.imageself.gray 和 self.edges:分别用于存储原始彩色图像、灰度图像和边缘检测后的图像,初始值为 None

3. 创建界面布局

python

        # 创建左侧图像显示区域
        self.image_frame = tk.Frame(root)
        self.image_frame.pack(side=tk.LEFT, padx=10, pady=10)

        # 创建右侧按钮区域
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(side=tk.RIGHT, padx=10, pady=10)

 

  • tk.Frame:用于创建一个框架,可作为容器来组织其他组件。
  • pack 方法:用于将框架放置在主窗口中,side=tk.LEFT 表示将图像显示区域放在左侧,side=tk.RIGHT 表示将按钮区域放在右侧,padx 和 pady 用于设置组件的内外边距。

4. 创建按钮

python

        # 创建按钮
        self.read_button = tk.Button(self.button_frame, text="读取一张本地的彩色图片", command=self.read_image)
        self.read_button.pack(pady=5)

        self.gray_button = tk.Button(self.button_frame, text="将彩色图片转换为灰度图片", command=self.convert_to_gray,
                                     state=tk.DISABLED)
        self.gray_button.pack(pady=5)

        self.canny_button = tk.Button(self.button_frame, text="使用 Canny 边缘检测算法对灰度图片进行边缘检测",
                                      command=self.apply_canny, state=tk.DISABLED)
        self.canny_button.pack(pady=5)

        self.display_button = tk.Button(self.button_frame, text="显示原始彩色图片和边缘检测后的图片", command=self.display_images,
                                        state=tk.DISABLED)
        self.display_button.pack(pady=5)

 

  • tk.Button:用于创建按钮,text 参数设置按钮上显示的文本,command 参数指定按钮被点击时调用的函数。
  • state=tk.DISABLED:将按钮初始状态设置为禁用,只有在满足一定条件时才启用。
  • pack 方法:将按钮放置在按钮区域中,pady=5 用于设置按钮之间的垂直间距。

5. 创建图像显示标签

python

        # 创建图像显示标签
        self.original_label = tk.Label(self.image_frame)
        self.original_label.pack(pady=5)

        self.edges_label = tk.Label(self.image_frame)
        self.edges_label.pack(pady=5)

        self.combined_label = tk.Label(self.image_frame)
        self.combined_label.pack(pady=5)

 

  • tk.Label:用于创建标签,可用于显示文本或图像。
  • pack 方法:将标签放置在图像显示区域中,pady=5 用于设置标签之间的垂直间距。

6. 读取图像的函数

python

    def read_image(self):
        file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg;*.png;*.jpeg")])
        if file_path:
            self.image = cv2.imread(file_path)
            if self.image is not None:
                # 获取屏幕尺寸
                screen_width = 1920
                screen_height = 1080
                # 计算压缩后的尺寸
                target_width = int(screen_width / 4)
                target_height = int(screen_height / 4)
                # 压缩图像
                self.image = cv2.resize(self.image, (target_width, target_height))
                self.show_image(self.original_label, self.image)
                self.gray_button.config(state=tk.NORMAL)
                self.read_button.config(state=tk.DISABLED)

 

  • filedialog.askopenfilename:打开文件选择对话框,让用户选择一张本地图片,filetypes 参数指定可选择的文件类型。
  • cv2.imread:读取用户选择的图片。
  • cv2.resize:将图像压缩到屏幕尺寸的 1/4。
  • self.show_image:调用显示图像的函数,将原始图像显示在 self.original_label 标签中。
  • self.gray_button.config(state=tk.NORMAL):启用 “将彩色图片转换为灰度图片” 按钮。
  • self.read_button.config(state=tk.DISABLED):禁用 “读取一张本地的彩色图片” 按钮,防止用户重复读取图片。

7. 转换为灰度图像的函数

python

    def convert_to_gray(self):
        self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        self.canny_button.config(state=tk.NORMAL)
        self.gray_button.config(state=tk.DISABLED)

 

  • cv2.cvtColor:将彩色图像转换为灰度图像。
  • self.canny_button.config(state=tk.NORMAL):启用 “使用 Canny 边缘检测算法对灰度图片进行边缘检测” 按钮。
  • self.gray_button.config(state=tk.DISABLED):禁用 “将彩色图片转换为灰度图片” 按钮,防止用户重复转换。

8. 应用 Canny 边缘检测的函数

python

    def apply_canny(self):
        self.edges = cv2.Canny(self.gray, 100, 200)
        edges_color = cv2.cvtColor(self.edges, cv2.COLOR_GRAY2BGR)
        self.show_image(self.edges_label, edges_color)
        self.display_button.config(state=tk.NORMAL)
        self.canny_button.config(state=tk.DISABLED)

 

  • cv2.Canny:对灰度图像应用 Canny 边缘检测算法,100 和 200 是阈值参数。
  • cv2.cvtColor:将边缘检测后的灰度图像转换为彩色图像,以便在 tkinter 中显示。
  • self.show_image:将边缘检测后的图像显示在 self.edges_label 标签中。
  • self.display_button.config(state=tk.NORMAL):启用 “显示原始彩色图片和边缘检测后的图片” 按钮。
  • self.canny_button.config(state=tk.DISABLED):禁用 “使用 Canny 边缘检测算法对灰度图片进行边缘检测” 按钮,防止用户重复检测。

9. 显示合并图像的函数

python

    def display_images(self):
        edges_color = cv2.cvtColor(self.edges, cv2.COLOR_GRAY2BGR)
        combined_image = np.hstack((self.image, edges_color))
        self.show_image(self.combined_label, combined_image)
        self.display_button.config(state=tk.DISABLED)

 

  • cv2.cvtColor:将边缘检测后的灰度图像转换为彩色图像。
  • np.hstack:将原始彩色图像和边缘检测后的图像水平拼接。
  • self.show_image:将合并后的图像显示在 self.combined_label 标签中。
  • self.display_button.config(state=tk.DISABLED):禁用 “显示原始彩色图片和边缘检测后的图片” 按钮,防止用户重复显示。

10. 显示图像的辅助函数

python

    def show_image(self, label, img):
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)
        img = ImageTk.PhotoImage(image=img)
        label.config(image=img)
        label.image = img

 

  • cv2.cvtColor:将 OpenCV 的 BGR 颜色空间转换为 RGB 颜色空间,因为 tkinter 使用 RGB 颜色空间。
  • Image.fromarray:将 NumPy 数组转换为 PIL 图像对象。
  • ImageTk.PhotoImage:将 PIL 图像对象转换为 tkinter 可显示的图像对象。
  • label.config(image=img):将图像显示在指定的标签中。
  • label.image = img:保存图像对象的引用,防止图像被垃圾回收。

11. 主程序入口

python

if __name__ == "__main__":
    root = tk.Tk()
    app = ImageProcessingApp(root)
    root.mainloop()

 

  • tk.Tk():创建 tkinter 的主窗口对象。
  • ImageProcessingApp(root):创建 ImageProcessingApp 类的实例,并将主窗口对象传递给构造函数。
  • root.mainloop():启动 tkinter 的事件循环,使窗口保持显示并响应用户操作。

 

通过以上步骤,代码实现了一个简单的图像处理器,用户可以通过点击按钮逐步完成图像的读取、处理和显示操作。

 

 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐