哈喽大家好哇~好久不见,这里是小杨儿的第四篇博客!这节课让我们一起来学习基于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界面嘻嘻)
不足之处还请大家多多包涵,互相交流学习啦~