数字图像处理第五课:基于PyQt的GUI界面设计

哈喽大家好哇~好久不见,这里是小杨儿的第四篇博客!这节课让我们一起来学习基于PyQt的GUI界面设计,有了这个可视化界面,我们可以更加方便地在同一个界面里实现对图像的旋转、平滑、锐化等多种处理。在写这篇博客的同时我喜欢的球队——金州勇士正在打一场关键的半决赛,比赛非常非常的焦灼,那就用这篇博客来给他加油吧!!!GOoooo Warrrriors!!!!

一、各模块简介 

本次实验考察我们在源代码的基础上增加所学功能,所以我们先了解一下源代码的构成吧~ 

将整个页面分为三个部分:顶部区域:“加载图片”和“保存图像”按钮,用于导入待处理的图片并把处理后的图片存储起来。图像显示区域:左侧为原始图像区域,右侧为处理后图像区域。底部区域:为图像各处理操作的按钮,现在包括“灰度化”“去噪”和“锐化”,后续可以自己添加功能。

下面对应这三个部分看看代码的构成:

顶部区域:

# 使用 QHBoxLayout() 创建一个水平布局对象,用于放置顶部的按钮等控件
top_layout = QHBoxLayout()

# 创建按钮
load_btn = QPushButton("📂 加载图片")
save_btn = QPushButton("💾 保存图像")

# 将按钮的 clicked 信号连接到 self.XX_image 函数,当按钮被点击时,会触发 XX_image 方法
load_btn.clicked.connect(self.load_image)
save_btn.clicked.connect(self.save_image)

# 调用布局的 addWidget() 方法,把按钮作为子控件添加到 top_layout 布局里
top_layout.addWidget(load_btn)
top_layout.addWidget(save_btn)

# 在顶部区域调用 addStretch() 方法,使布局中的控件在窗口大小变化时能够合理分布
top_layout.addStretch()

# 调用主布局的 addLayout() 方法,把 top_layout 作为子布局添加到主布局里
main_layout.addLayout(top_layout)

# 调用 _h_line() 方法创建一条水平分割线,并添加到主布局中,用于分隔图像显示区域和其他界面部分
main_layout.addWidget(self._h_line())

图像显示区域: 

# 使用 QHBoxLayout() 创建一个水平布局对象,用于放置显示原始图像和处理后图像
img_layout = QHBoxLayout()

# 实例化一个 QLabel 对象,用于显示原始图像和处理后图像
self.original_label = QLabel()
self.processed_label = QLabel()

# 用for循环遍历原始图像和处理后图像,分别设置它们的固定大小和对齐方式
for label in (self.original_label, self.processed_label):
    # 调用 setFixedSize() 方法,将标签的大小固定为 400x400 像素
    label.setFixedSize(400, 400)
    # 调用 setAlignment() 方法,将标签的对齐方式设置为居中
    label.setAlignment(Qt.AlignmentFlag.AlignCenter)

# 调用 addWidget() 方法,将原始图像添加到 img_layout 布局中
img_layout.addWidget(self.original_label)

# 调用 _v_line() 方法创建一条垂直分割线,并添加到 img_layout 布局中
img_layout.addWidget(self._v_line())

# 调用 addWidget() 方法,将处理后的图像添加到 img_layout 布局中
img_layout.addWidget(self.processed_label)

# 调用 setSpacing() 方法,将 img_layout 布局中各控件之间的间距设置为 0,使控件紧密排列
img_layout.setSpacing(0)

# 调用 addLayout() 方法,将 img_layout 作为子布局添加到主布局中
main_layout.addLayout(img_layout)

# 调用 _h_line() 方法创建一条水平分割线,并添加到主布局中,用于分隔图像显示区域和其他界面部分
main_layout.addWidget(self._h_line())

底部区域:

# 使用 QHBoxLayout() 创建一个水平布局对象,用于放置底部的功能按钮
bottom_layout = QHBoxLayout()

# 通过for循环创建多个按钮,每个按钮对应一种图像处理功能
for text, func in [("⚫灰度化", "gray"), ("🔍去噪", "denoise"),
                   ("✨锐化", "sharpen")]:
    # 实例化一个 QPushButton 对象,按钮上显示对应的文本(如 "⚫灰度化")
    btn = QPushButton(text)
    
    # 使用 lambda 表达式连接按钮的 clicked 信号到 self.process 方法
    # 这里使用 f=func 的默认参数技巧,确保循环中每个按钮都能正确传递对应的功能标识(如 "gray")
    btn.clicked.connect(lambda _, f=func: self.process(f))
    
    # 调用 addWidget() 方法,把创建的按钮作为子控件添加到底部布局里
    bottom_layout.addWidget(btn)

# 调用 addStretch() 方法,使按钮在窗口大小变化时能够合理分布
bottom_layout.addStretch()

# 调用主布局的 addLayout() 方法,把 bottom_layout 作为子布局添加到主布局里
main_layout.addLayout(bottom_layout)

 二、扩展功能

下面我们对每一个功能模块进行完善。

首先我想对顶部区域完善,在“加载图片”和“保存图像”之间增加“图片复原”功能,这样可以反复对同一张图像进行不同的操作。最简单粗暴的方法就是比照这源代码更改新功能的代码。可以自己尝试把如下代码添加到“双胞胎”代码中间,并在ImageProcessor中增加该功能的代码:

        restore_btn = QPushButton("↩️ 图片复原")
        restore_btn.clicked.connect(self.restore_image)#连接复原按钮的事件
        top_layout.addWidget(restore_btn)

    def restore_image(self):
        """将处理后的图像恢复为原始图像"""
        if 'original' not in self.image_data:
            QMessageBox.warning(self, "提示", "请先加载图片")  # 提示用户加载图像
            return
        # 将处理后的图像恢复为原始图像
        self.image_data['processed'] = self.image_data['original'].copy()
        # 更新处理后图像显示
        self.show_image(self.image_data['processed'], self.processed_label)
    

下面展示功能运行结果:点击“图片复原”按钮,右侧的处理后图像恢复为原始图像。 

观察上图我们发现:代码的原有功能只能对原始图像进行处理,不能在已处理的图像上进行二次处理。我们在process图像功能代码中增加约束条件,使得如果图像已被处理过,则在处理后的图像上进行处理。

    def process(self, mode):
        """根据选择的模式处理图像"""
        if 'processed' not in self.image_data:  # 如果没有处理过的图像,默认使用原始图像进行处理
            if 'original' not in self.image_data:
                QMessageBox.warning(self, "提示", "请先加载图片")  # 提示用户加载图像
                return
            img = self.image_data['original']  # 获取原始图像
        else:
            img = self.image_data['processed']  # 获取处理后的图像作为基础图像

运行结果如下:先进行灰度处理,再进行锐化,我们发现一直点“锐化”图像会一直不断加深锐化程度。  

 

下面我们可以将所学的图像处理操作都添加到底部区域当中。

首先在“去噪”的前面增加“添噪”的功能,并能够选择添加“椒盐噪声”“高斯噪声”“均匀噪声”三种:在底部按钮中添加“添噪”按钮,并在process图像功能代码中添加添噪处理(注意一定要创建图像副本,对副本进行噪声处理),并在代码最后添加添噪的相关代码。

#“添噪”按钮
        for text, func in [("⚫灰度化", "gray"),("⚡添噪", "add_noise"), ("🔍去噪", "denoise"), ("✨锐化", "sharpen")]:
            btn = QPushButton(text)

#process中的代码
            elif mode == "add_noise":  # 添噪处理
                # 弹出对话框让用户选择噪声类型
                noise_type, ok = QInputDialog.getItem(
                    self, "选择噪声类型", "噪声类型", ["椒盐噪声", "高斯噪声", "均匀噪声"], 0, False)
                if ok and noise_type:
                    img_copy = img.copy()  # 使用副本进行操作
                    if noise_type == "椒盐噪声":
                        result = self.add_salt_pepper_noise(img_copy)
                    elif noise_type == "高斯噪声":
                        result = self.add_gaussian_noise(img_copy)
                    elif noise_type == "均匀噪声":
                        result = self.add_uniform_noise(img_copy)
                else:
                    return

#三种噪声代码
    def add_salt_pepper_noise(self, image):
        """添加椒盐噪声"""
        # 添加椒盐噪声
        salt_pepper_prob = 0.05  # 椒盐噪声的概率
        salt_prob = 0.5  # 椒盐比例
        noisy_image = image.copy()  # 创建图像副本进行操作
        # 随机生成与图像大小相同的掩码
        noise_mask = np.random.random(noisy_image.shape[:2])
        salt_mask = noise_mask < (salt_pepper_prob * salt_prob)  # 椒噪声掩码
        pepper_mask = noise_mask > (1 - salt_pepper_prob * (1 - salt_prob))  # 盐噪声掩码
        # 添加椒噪声(白色像素)
        noisy_image[salt_mask] = 255
        # 添加盐噪声(黑色像素)
        noisy_image[pepper_mask] = 0
        return noisy_image.astype(np.uint8)  # 确保返回的图像是uint8类型

    def add_gaussian_noise(self, image):
        """添加高斯噪声"""
        mean = 0  # 均值
        sigma = 25  # 标准差
        # 生成与图像大小相同的高斯噪声
        gaussian_noise = np.random.normal(mean, sigma, image.shape).astype(np.float32)
        # 将图像转换为浮点数进行运算,避免溢出
        noisy_image = cv2.add(image.astype(np.float32), gaussian_noise.astype(np.float32))
        # 将图像值裁剪到[0, 255]并转换为uint8类型
        return np.clip(noisy_image, 0, 255).astype(np.uint8)

    def add_uniform_noise(self, image):
        """添加均匀噪声"""
        low = -50  # 均匀噪声的下限
        high = 50  # 均匀噪声的上限
        # 生成与图像大小相同的均匀噪声
        uniform_noise = np.random.uniform(low, high, image.shape).astype(np.float32)
        # 将图像转换为浮点数进行运算,避免溢出
        noisy_image = cv2.add(image.astype(np.float32), uniform_noise.astype(np.float32))
        # 将图像值裁剪到[0, 255]并转换为uint8类型
        return np.clip(noisy_image, 0, 255).astype(np.uint8)

下面分别展示这三种噪声的添加情况: 

同一种噪声除了可以反复叠加外,还可以同时添加高斯噪声和椒盐噪声:

既然添加三种噪声的功能都完善了,那我们也完善一下去噪的三种滤波方法吧!同样也是在process图像功能代码中添加“中值滤波”“高斯滤波”“均值滤波”三种去噪方法供用户选择(注意一定要创建图像副本,对副本进行噪声处理),并在代码最后添加三种去噪的相关代码。

#process中的代码
            elif mode == "denoise":  # 去噪处理
                 # 弹出对话框让用户选择去噪方法
                    denoise_type, ok = QInputDialog.getItem(
                        self, "选择去噪方法", "去噪方法", ["中值滤波", "高斯滤波", "均值滤波"], 0, False)
                    if ok and denoise_type:
                        img_copy = img.copy()  # 使用副本进行操作
                        if denoise_type == "中值滤波":
                            result = self.median_filter(img_copy)
                        elif denoise_type == "高斯滤波":
                            result = self.gaussian_filter(img_copy)
                        elif denoise_type == "均值滤波":
                            result = self.mean_filter(img_copy)
                    else:
                        return

#三种滤波方法
    def median_filter(self, image):
        """中值滤波"""
        return cv2.medianBlur(image, 5)  # 使用5x5的核进行中值滤波

    def gaussian_filter(self, image):
        """高斯滤波"""
        return cv2.GaussianBlur(image, (5, 5), 0)  # 使用5x5的核进行高斯滤波

    def mean_filter(self, image):
        """均值滤波"""
        return cv2.blur(image, (5, 5))  # 使用5x5的核进行均值滤波
    def show_image(self, img, label):
        """显示图像"""
        try:
            rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 将BGR转为RGB格式
            h, w, ch = rgb.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb.data, w, h, bytes_per_line, QImage.Format.Format_RGB888)  # 转为QImage格式
            label.setPixmap(QPixmap.fromImage(q_img).scaled(400, 400, Qt.AspectRatioMode.KeepAspectRatio))  # 设置显示图像
        except Exception as e:
            QMessageBox.critical(self, "错误", f"显示图像时发生错误:{e}")  # 捕获并显示异常信息

下面针对用中值滤波处理椒盐噪声来进行功能展示:

回想图像增强一章学习的知识,图像的平滑和锐化功能我们都已经完善了,下面来完善一下“灰度变换”功能。设置“灰度变换”按钮,可以选择“增强对比度”“减弱对比度”和“灰度取反”三种功能。

#“灰度变换”按钮
        for text, func in [("⚫灰度化", "gray"),  ("📈灰度变换", "intensity_transformation"),("⚡添噪", "add_noise"), ("🔍去噪", "denoise"), ("✨锐化", "sharpen")]:

#process中的代码
            elif mode == "intensity_transformation":  # 灰度变换
                # 弹出对话框让用户选择灰度变换类型
                transform_type, ok = QInputDialog.getItem(
                    self, "选择灰度变换类型", "灰度变换类型", ["增强对比度", "减弱对比度", "灰度取反"], 0, False)
                if ok and transform_type:
                    img_copy = img.copy()  # 使用副本进行操作
                    if transform_type == "增强对比度":
                        result = self.enhance_contrast(img_copy)
                    elif transform_type == "减弱对比度":
                        result = self.reduce_contrast(img_copy)
                    elif transform_type == "灰度取反":
                        result = self.invert_grayscale(img_copy)
                else:
                    return

#实现三种灰度变换功能
    def enhance_contrast(self, image):
        """增强对比度"""
        # 将图像转换为浮点数进行运算,避免溢出
        img_float = image.astype(np.float32)
        # 增强对比度:alpha > 1
        alpha = 1.5
        beta = 0
        enhanced = np.clip(img_float * alpha + beta, 0, 255)
        return enhanced.astype(np.uint8)  # 转换回uint8类型

    def reduce_contrast(self, image):
        """减弱对比度"""
        # 将图像转换为浮点数进行运算,避免溢出
        img_float = image.astype(np.float32)
        # 减弱对比度:alpha < 1
        alpha = 0.7
        beta = 0
        reduced = np.clip(img_float * alpha + beta, 0, 255)
        return reduced.astype(np.uint8)  # 转换回uint8类型

    def invert_grayscale(self, image):
        """灰度取反"""
        # 将图像转换为灰度图
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        # 取反
        inverted = 255 - gray
        # 转回三通道
        return cv2.cvtColor(inverted, cv2.COLOR_GRAY2BGR)

运行结果展示如下:

为了获得更丰富的数据库,我们再增加一个图像旋转功能:同样是在底部按钮中添加“旋转”按钮,并在process图像功能代码中添加旋转处理(注意一定要创建图像副本,对副本进行噪声处理),并在代码最后添加添噪的相关代码。

#“旋转”按钮
for text, func in [("⚫灰度化", "gray"), ("⚡添噪", "add_noise"), ("🔍去噪", "denoise"), ("✨锐化", "sharpen"), ("🔄旋转", "rotate")]:

#process中的代码
            elif mode == "rotate":  # 旋转处理
                result = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)  # 顺时针旋转90度

展示旋转结果如下:

 

能看到这里你真是太棒啦!!!奖励你全部的源代码吧:

import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QLabel, QPushButton,
    QFileDialog, QMessageBox, QVBoxLayout, QHBoxLayout, QFrame, QInputDialog
)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import Qt


class ImageProcessor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt演示")
        self.resize(900, 600)

        # 存储原始图像和处理后的图像数据
        self.image_data = {}

        # 创建主窗口小部件并设置布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)

        # 设置主窗口的样式
        main_widget.setStyleSheet("""
            QWidget {
                background-color: #f0f4f8;
            }
            QLabel {
                border: 2px solid #aaa;
                border-radius: 10px;
                background-color: white;
                padding: 5px;
                box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
            }
            QPushButton {
                font-size: 15px;
                padding: 8px 18px;
                min-width: 100px;
            }
        """)

        # 创建顶部布局:加载按钮和保存按钮
        top_layout = QHBoxLayout()
        load_btn = QPushButton("📂 加载图片")
        restore_btn = QPushButton("↩️ 图片复原")
        save_btn = QPushButton("💾 保存图像")
        load_btn.clicked.connect(self.load_image)  # 连接加载按钮的事件
        restore_btn.clicked.connect(self.restore_image)  # 连接复原按钮的事件
        save_btn.clicked.connect(self.save_image)  # 连接保存按钮的事件
        top_layout.addWidget(load_btn)
        top_layout.addWidget(restore_btn)
        top_layout.addWidget(save_btn)
        top_layout.addStretch()
        main_layout.addLayout(top_layout)
        # 添加水平分割线
        main_layout.addWidget(self._h_line())

        # 创建用于显示原始图像和处理后图像的布局
        img_layout = QHBoxLayout()
        self.original_label = QLabel()  # 用于显示原始图像
        self.processed_label = QLabel()  # 用于显示处理后的图像

        # 设置标签的固定大小和对齐方式
        for label in (self.original_label, self.processed_label):
            label.setFixedSize(400, 400)
            label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        # 添加原始图像和处理图像标签
        img_layout.addWidget(self.original_label)
        img_layout.addWidget(self._v_line())
        img_layout.addWidget(self.processed_label)
        img_layout.setSpacing(0)
        main_layout.addLayout(img_layout)
        main_layout.addWidget(self._h_line())

        # 创建底部按钮布局:灰度化、添噪、去噪、锐化
        bottom_layout = QHBoxLayout()
        for text, func in [("⚫灰度化", "gray"),  ("📈灰度变换", "intensity_transformation"),("⚡添噪", "add_noise"), ("🔍去噪", "denoise"), ("✨锐化", "sharpen"), ("🔄旋转", "rotate")]:
            btn = QPushButton(text)
            # 绑定按钮点击事件
            btn.clicked.connect(lambda _, f=func: self.process(f))
            bottom_layout.addWidget(btn)
        bottom_layout.addStretch()
        main_layout.addLayout(bottom_layout)

    def _h_line(self):
        line = QFrame()
        line.setFrameShape(QFrame.Shape.HLine)
        line.setFrameShadow(QFrame.Shadow.Sunken)
        line.setStyleSheet("color: #ccc;")
        return line

    def _v_line(self):
        line = QFrame()
        line.setFrameShape(QFrame.Shape.VLine)
        line.setFrameShadow(QFrame.Shadow.Sunken)
        line.setStyleSheet("color: #ccc;")
        return line

    def load_image(self):
        """加载图像"""
        file, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "图片文件 (*.png *.jpg *.bmp)")
        if file:
            img = cv2.imread(file)
            if img is None:
                QMessageBox.warning(self, "错误", "无法加载图像")  # 显示错误消息
                return
            self.image_data['original'] = img  # 存储原始图像
            self.image_data['processed'] = img.copy()  # 存储处理后的图像(初始为原图)
            self.show_image(img, self.original_label)  # 显示原图像

    def restore_image(self):
        """将处理后的图像恢复为原始图像"""
        if 'original' not in self.image_data:
            QMessageBox.warning(self, "提示", "请先加载图片")  # 提示用户加载图像
            return
        # 将处理后的图像恢复为原始图像
        self.image_data['processed'] = self.image_data['original'].copy()
        # 更新处理后图像显示
        self.show_image(self.image_data['processed'], self.processed_label)

    def save_image(self):
        """保存图像"""
        if 'processed' not in self.image_data:
            QMessageBox.warning(self, "提示", "没有可保存的图像")  # 没有处理图像时提示
            return
        file, _ = QFileDialog.getSaveFileName(self, "保存图像", "", "PNG (*.png);;JPG (*.jpg)")
        if file:
            cv2.imwrite(file, self.image_data['processed'])  # 保存处理图像
            QMessageBox.information(self, "成功", f"图像已保存:{file}")  # 提示保存成功

    def process(self, mode):
        """根据选择的模式处理图像"""
        if 'processed' not in self.image_data:  # 如果没有处理过的图像,默认使用原始图像进行处理
            if 'original' not in self.image_data:
                QMessageBox.warning(self, "提示", "请先加载图片")  # 提示用户加载图像
                return
            img = self.image_data['original']  # 获取原始图像
        else:
            img = self.image_data['processed']  # 获取处理后的图像作为基础图像

        result = None  # 初始化结果变量
        try:
            if mode == "gray":  # 灰度化处理
                result = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)  # 转回三通道
            elif mode == "intensity_transformation":  # 灰度变换
                # 弹出对话框让用户选择灰度变换类型
                transform_type, ok = QInputDialog.getItem(
                    self, "选择灰度变换类型", "灰度变换类型", ["增强对比度", "减弱对比度", "灰度取反"], 0, False)
                if ok and transform_type:
                    img_copy = img.copy()  # 使用副本进行操作
                    if transform_type == "增强对比度":
                        result = self.enhance_contrast(img_copy)
                    elif transform_type == "减弱对比度":
                        result = self.reduce_contrast(img_copy)
                    elif transform_type == "灰度取反":
                        result = self.invert_grayscale(img_copy)
                else:
                    return
            elif mode == "add_noise":  # 添噪处理
                # 弹出对话框让用户选择噪声类型
                noise_type, ok = QInputDialog.getItem(
                    self, "选择噪声类型", "噪声类型", ["椒盐噪声", "高斯噪声", "均匀噪声"], 0, False)
                if ok and noise_type:
                    img_copy = img.copy()  # 使用副本进行操作
                    if noise_type == "椒盐噪声":
                        result = self.add_salt_pepper_noise(img_copy)
                    elif noise_type == "高斯噪声":
                        result = self.add_gaussian_noise(img_copy)
                    elif noise_type == "均匀噪声":
                        result = self.add_uniform_noise(img_copy)
                else:
                    return
            elif mode == "denoise":  # 去噪处理
                 # 弹出对话框让用户选择去噪方法
                    denoise_type, ok = QInputDialog.getItem(
                        self, "选择去噪方法", "去噪方法", ["中值滤波", "高斯滤波", "均值滤波"], 0, False)
                    if ok and denoise_type:
                        img_copy = img.copy()  # 使用副本进行操作
                        if denoise_type == "中值滤波":
                            result = self.median_filter(img_copy)
                        elif denoise_type == "高斯滤波":
                            result = self.gaussian_filter(img_copy)
                        elif denoise_type == "均值滤波":
                            result = self.mean_filter(img_copy)
                    else:
                        return
            elif mode == "sharpen":  # 锐化处理
                kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
                result = cv2.filter2D(img, -1, kernel)
            elif mode == "rotate":  # 旋转处理
                result = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)  # 顺时针旋转90度
            else:
                return

            if result is not None:
                self.image_data['processed'] = result  # 存储处理后的图像
                self.show_image(result, self.processed_label)  # 显示处理后的图像

        except Exception as e:
            QMessageBox.critical(self, "错误", f"处理图像时发生错误:{e}")  # 捕获并显示异常信息

    def add_salt_pepper_noise(self, image):
        """添加椒盐噪声"""
        # 添加椒盐噪声
        salt_pepper_prob = 0.05  # 椒盐噪声的概率
        salt_prob = 0.5  # 椒盐比例
        noisy_image = image.copy()  # 创建图像副本进行操作
        # 随机生成与图像大小相同的掩码
        noise_mask = np.random.random(noisy_image.shape[:2])
        salt_mask = noise_mask < (salt_pepper_prob * salt_prob)  # 椒噪声掩码
        pepper_mask = noise_mask > (1 - salt_pepper_prob * (1 - salt_prob))  # 盐噪声掩码
        # 添加椒噪声(白色像素)
        noisy_image[salt_mask] = 255
        # 添加盐噪声(黑色像素)
        noisy_image[pepper_mask] = 0
        return noisy_image.astype(np.uint8)  # 确保返回的图像是uint8类型

    def add_gaussian_noise(self, image):
        """添加高斯噪声"""
        mean = 0  # 均值
        sigma = 25  # 标准差
        # 生成与图像大小相同的高斯噪声
        gaussian_noise = np.random.normal(mean, sigma, image.shape).astype(np.float32)
        # 将图像转换为浮点数进行运算,避免溢出
        noisy_image = cv2.add(image.astype(np.float32), gaussian_noise.astype(np.float32))
        # 将图像值裁剪到[0, 255]并转换为uint8类型
        return np.clip(noisy_image, 0, 255).astype(np.uint8)

    def add_uniform_noise(self, image):
        """添加均匀噪声"""
        low = -50  # 均匀噪声的下限
        high = 50  # 均匀噪声的上限
        # 生成与图像大小相同的均匀噪声
        uniform_noise = np.random.uniform(low, high, image.shape).astype(np.float32)
        # 将图像转换为浮点数进行运算,避免溢出
        noisy_image = cv2.add(image.astype(np.float32), uniform_noise.astype(np.float32))
        # 将图像值裁剪到[0, 255]并转换为uint8类型
        return np.clip(noisy_image, 0, 255).astype(np.uint8)

    def median_filter(self, image):
        """中值滤波"""
        return cv2.medianBlur(image, 5)  # 使用5x5的核进行中值滤波

    def gaussian_filter(self, image):
        """高斯滤波"""
        return cv2.GaussianBlur(image, (5, 5), 0)  # 使用5x5的核进行高斯滤波

    def mean_filter(self, image):
        """均值滤波"""
        return cv2.blur(image, (5, 5))  # 使用5x5的核进行均值滤波

    def enhance_contrast(self, image):
        """增强对比度"""
        # 将图像转换为浮点数进行运算,避免溢出
        img_float = image.astype(np.float32)
        # 增强对比度:alpha > 1
        alpha = 1.5
        beta = 0
        enhanced = np.clip(img_float * alpha + beta, 0, 255)
        return enhanced.astype(np.uint8)  # 转换回uint8类型

    def reduce_contrast(self, image):
        """减弱对比度"""
        # 将图像转换为浮点数进行运算,避免溢出
        img_float = image.astype(np.float32)
        # 减弱对比度:alpha < 1
        alpha = 0.7
        beta = 0
        reduced = np.clip(img_float * alpha + beta, 0, 255)
        return reduced.astype(np.uint8)  # 转换回uint8类型

    def invert_grayscale(self, image):
        """灰度取反"""
        # 将图像转换为灰度图
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        # 取反
        inverted = 255 - gray
        # 转回三通道
        return cv2.cvtColor(inverted, cv2.COLOR_GRAY2BGR)

    def show_image(self, img, label):
        """显示图像"""
        try:
            rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 将BGR转为RGB格式
            h, w, ch = rgb.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb.data, w, h, bytes_per_line, QImage.Format.Format_RGB888)  # 转为QImage格式
            label.setPixmap(QPixmap.fromImage(q_img).scaled(400, 400, Qt.AspectRatioMode.KeepAspectRatio))  # 设置显示图像
        except Exception as e:
            QMessageBox.critical(self, "错误", f"显示图像时发生错误:{e}")  # 捕获并显示异常信息


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ImageProcessor()
    window.show()  # 显示主窗口
    sys.exit(app.exec())

希望你也可以把自己的所学知识添加到这个界面的功能当中。一定要有耐心的反复调试,因为其实每一个模块的代码原理都是一样的,代码的逻辑思维上并不难,只要你耐心做,便能熟能生巧的掌握界面功能的扩展~

唉,这场输了啊~~~~~~~~一定要振作起来,G4捍卫主场啊!!!!(不要辜负我如此优秀的GUI界面嘻嘻)

不足之处还请大家多多包涵,互相交流学习啦~ 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值