1.配置开发环境
首先我们先来介绍一下PyQt。
PyQt 是一个用于创建桌面应用程序的 Python 库,它是 Qt 应用程序框架的 Python 绑定。Qt 是一个跨平台的 C++ 框架,广泛用于开发图形用户界面(GUI)应用程序。PyQt 让开发者能够使用 Python 编写 Qt 应用程序,因此能够充分利用 Qt 的强大功能和易用性,同时享受 Python 的简洁性和灵活性。
下面是一个简单的 PyQt 应用程序,创建一个窗口并显示一个按钮。
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
def on_button_click():
print("Button clicked!")
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("PyQt Example")
window.setGeometry(100, 100, 300, 200)
button = QPushButton("Click Me", window)
button.clicked.connect(on_button_click)
button.resize(100, 50)
button.move(100, 75)
window.show()
sys.exit(app.exec_())
PyQt5 的主要优势在于其成熟的生态系统和稳定性。作为一个广泛使用的框架,PyQt5 拥有丰富的文档、教程和社区支持,适合需要高可靠性的企业级应用开发。它提供了强大的跨平台支持,允许开发者使用同一代码库创建适用于 Windows、Linux、macOS 等多个平台的应用程序。Qt5 的图形和渲染性能也非常强大,支持 2D 图形、动画、OpenGL 等,适合开发图形密集型应用。此外,PyQt5 引入的 Qt Quick 和 QML 使得开发动态、响应式用户界面变得更加容易。它还提供了多媒体处理、数据库访问、网络编程等丰富的功能模块,能满足多种应用需求。PyQt5 的兼容性良好,适用于旧版项目的迁移,且与许多第三方库兼容,是开发桌面软件、嵌入式系统等应用的可靠选择。
PyQt6 是基于 Qt 6 的 Python 绑定,相较于 PyQt5,它带来了许多显著的优势。首先,Qt 6 提供了更好的性能优化,尤其在图形渲染和多媒体支持方面,能够更高效地利用硬件加速,支持高分辨率显示(如 4K)。其次,PyQt6 的 API 经过现代化重构,更加简洁和一致,移除了许多过时的功能,提升了可维护性。PyQt6 对跨平台支持进行了强化,能够更好地在不同操作系统和硬件(如 ARM 架构、Apple M1)上运行。此外,Qt Quick 和 QML 在 PyQt6 中得到了更好的优化,便于开发动态和响应式的用户界面。总体而言,PyQt6 结合了 Qt 6 的新特性,提供了更强大的功能、更高的性能和更好的跨平台兼容性,是开发现代桌面应用的理想选择。
然后我们开始配置开发环境。
1.新建/激活 现有 conda环境 (python>3.9)
2.在当前环境内,下载实验所需要的环境。
3.如果PyQt下载存在问题,可以根据下载完成的下载whl,进行安装
2.代码实现及结果展示
以下代码基于PyQt5设计。
代码一
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget, QFileDialog
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import Qt
class ImageProcessingApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("图像处理系统") # 设置窗口标题
self.setGeometry(100, 100, 800, 600) # 设置窗口位置和大小
# 创建中心部件
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget) # 设置中心部件
# 创建布局管理器
self.layout = QVBoxLayout(self.central_widget)
# 创建标签显示原始图像
self.original_label = QLabel("原始图像", self)
self.original_label.setAlignment(Qt.AlignCenter) # 设置标签文本居中
self.layout.addWidget(self.original_label) # 将标签添加到布局中
# 创建标签显示处理后的图像
self.processed_label = QLabel("处理后的图像", self)
self.processed_label.setAlignment(Qt.AlignCenter) # 设置标签文本居中
self.layout.addWidget(self.processed_label) # 将标签添加到布局中
# 创建按钮用于加载图像
self.load_button = QPushButton("加载图像", self)
self.layout.addWidget(self.load_button) # 将按钮添加到布局中
self.load_button.clicked.connect(self.load_image) # 按钮点击事件绑定加载图像函数
# 创建按钮用于灰度化处理
self.process_button = QPushButton("灰度化处理", self)
self.layout.addWidget(self.process_button)
self.process_button.clicked.connect(self.process_image) # 按钮点击事件绑定灰度化处理函数
# 创建按钮用于高斯模糊处理
self.blur_button = QPushButton("高斯模糊", self)
self.layout.addWidget(self.blur_button)
self.blur_button.clicked.connect(self.blur_image) # 按钮点击事件绑定高斯模糊处理函数
# 创建按钮用于高斯噪声处理
self.gaussian_noise_button = QPushButton("高斯噪声", self)
self.layout.addWidget(self.gaussian_noise_button)
self.gaussian_noise_button.clicked.connect(self.gaussian_noise_image) # 按钮点击事件绑定高斯噪声处理函数
# 创建按钮用于椒盐噪声处理
self.salt_pepper_button = QPushButton("椒盐噪声", self)
self.layout.addWidget(self.salt_pepper_button)
self.salt_pepper_button.clicked.connect(self.salt_pepper_noise_image) # 按钮点击事件绑定椒盐噪声处理函数
# 初始化图像变量
self.original_image = None
self.processed_image = None
def load_image(self):
"""加载图像"""
file_path, _ = QFileDialog.getOpenFileName(self, "选择图像文件", "", "Image Files (*.png *.jpg *.bmp *.gif)")
if file_path: # 如果选择了文件
# 使用 OpenCV 加载图像(BGR格式)
self.original_image = cv2.imread(file_path)
if self.original_image is not None:
# 显示原始图像
self.display_image(self.original_label, self.original_image)
self.processed_label.clear() # 清空处理后的图像标签
self.processed_label.setText("处理后的图像") # 更新标签文字
def display_image(self, label, img):
"""将 OpenCV 图像转换并显示在 QLabel 中"""
# 将 BGR 图像转为 RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 获取图像的高度、宽度和通道数
height, width, channel = img_rgb.shape
bytes_per_line = 3 * width # 每行字节数
# 将图像数据转换为 QImage 对象
q_img = QImage(img_rgb.data, width, height, bytes_per_line, QImage.Format_RGB888)
# 将 QImage 转换为 QPixmap,并保持图像比例适应标签大小
pixmap = QPixmap.fromImage(q_img).scaled(label.width(), label.height(), Qt.KeepAspectRatio)
label.setPixmap(pixmap) # 将 QPixmap 显示在标签中
def process_image(self):
"""灰度化处理"""
if self.original_image is not None:
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY) # 转换为灰度图像
# 将单通道灰度图转换为三通道,以便显示
gray_3ch = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
self.processed_image = gray_3ch
self.display_image(self.processed_label, self.processed_image) # 显示处理后的图像
def blur_image(self):
"""高斯模糊处理"""
if self.original_image is not None:
# 使用 OpenCV 的高斯模糊函数
blurred = cv2.GaussianBlur(self.original_image, (15, 15), 0)
self.processed_image = blurred
self.display_image(self.processed_label, self.processed_image) # 显示处理后的图像
def gaussian_noise_image(self):
"""添加高斯噪声"""
if self.original_image is not None:
img = self.original_image.astype(np.float32) / 255.0 # 将图像转换为浮动点格式,归一化到 [0, 1] 范围
noise = np.random.normal(0, 0.1, img.shape) # 添加高斯噪声,均值为0,标准差为0.1
noisy_img = img + noise
noisy_img = np.clip(noisy_img, 0, 1) # 截断值,确保像素值在 [0, 1] 范围
noisy_img = (noisy_img * 255).astype(np.uint8) # 将像素值恢复到 [0, 255] 范围并转换为整数
self.processed_image = noisy_img
self.display_image(self.processed_label, self.processed_image) # 显示处理后的图像
def salt_pepper_noise_image(self):
"""添加椒盐噪声"""
if self.original_image is not None:
img = self.original_image.copy() # 复制原始图像
prob = 0.05 # 椒盐噪声的概率
rnd = np.random.rand(*img.shape[:2]) # 生成一个与图像大小相同的随机数矩阵
# 将低于概率的一部分设置为椒噪声
img[rnd < prob / 2] = 0
# 将在中间概率范围内的部分设置为盐噪声
img[(rnd >= prob / 2) & (rnd < prob)] = 255
self.processed_image = img
self.display_image(self.processed_label, self.processed_image) # 显示处理后的图像
if __name__ == "__main__":
app = QApplication(sys.argv) # 创建 QApplication 对象
window = ImageProcessingApp() # 创建主窗口对象
window.show() # 显示主窗口
sys.exit(app.exec_()) # 启动事件循环,直到窗口关闭
代码生成的操作界面如图
我们点击加载图像并选择一张图片就可以进行图像处理了。
代码二
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QLabel, QPushButton,
QFileDialog, QMessageBox, QVBoxLayout, QHBoxLayout, QFrame
)
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, 800)
# 存储原始图像和处理后的图像数据
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("📂 加载图片")
save_btn = QPushButton("💾 保存图像")
load_btn.clicked.connect(self.load_image)
save_btn.clicked.connect(self.save_image)
top_layout.addWidget(load_btn)
top_layout.addWidget(save_btn)
top_layout.addStretch()
main_layout.addLayout(top_layout)
# 添加水平分割线
main_layout.addWidget(self._h_line())
# 创建用于显示图像的两行三列布局
img_grid_layout = QVBoxLayout()
row1_layout = QHBoxLayout()
row2_layout = QHBoxLayout()
self.image_labels = []
for _ in range(6):
label = QLabel()
label.setFixedSize(280, 280)
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_labels.append(label)
for i in range(3):
row1_layout.addWidget(self.image_labels[i])
for i in range(3, 6):
row2_layout.addWidget(self.image_labels[i])
img_grid_layout.addLayout(row1_layout)
img_grid_layout.addLayout(row2_layout)
main_layout.addLayout(img_grid_layout)
main_layout.addWidget(self._h_line())
# 创建底部按钮布局:多种图像处理操作
bottom_layout = QHBoxLayout()
for text, func in [("⚫灰度化", "gray"), ("🔍去噪", "denoise"),
("✨锐化", "sharpen"), ("🔄旋转", "rotate"),
("🔀反转", "flip"), ("🌫️模糊", "blur"),
("✂️边缘检测", "edge"), ("🔆直方图均衡化", "equalize"),
("🌈色彩转换", "color_convert"), ("📏缩放", "resize")]:
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.image_labels[0])
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 'original' not in self.image_data:
QMessageBox.warning(self, "提示", "请先加载图片")
return
img = self.image_data['original']
if mode == "gray":
result = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
elif mode == "denoise":
result = cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 21)
elif mode == "sharpen":
kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
result = cv2.filter2D(img, -1, kernel)
elif mode == "rotate":
rows, cols = img.shape[:2]
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1)
result = cv2.warpAffine(img, M, (cols, rows))
elif mode == "flip":
result = cv2.flip(img, 1)
elif mode == "blur":
result = cv2.GaussianBlur(img, (5, 5), 0)
elif mode == "edge":
result = cv2.Canny(img, 100, 200)
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
elif mode == "equalize":
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)
result = cv2.cvtColor(equalized, cv2.COLOR_GRAY2BGR)
elif mode == "color_convert":
result = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
elif mode == "resize":
result = cv2.resize(img, (int(img.shape[1] * 0.5), int(img.shape[0] * 0.5)))
else:
return
self.image_data['processed'] = result
for i in range(1, len(self.image_labels)):
if not self.image_labels[i].pixmap():
self.show_image(result, self.image_labels[i])
break
def show_image(self, img, label):
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w, ch = rgb.shape
bytes_per_line = ch * w
q_img = QImage(rgb.data, w, h, bytes_per_line, QImage.Format.Format_RGB888)
label.setPixmap(QPixmap.fromImage(q_img).scaled(280, 280, Qt.AspectRatioMode.KeepAspectRatio))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ImageProcessor()
window.show()
sys.exit(app.exec())
同理,对生成的界面进行相同的操作(第一张为原图像,从左到右依次为反转,边缘检测,直方图均衡化,色彩转换,缩放)。
3.界面功能展示(以代码二为例)
1. 顶部区域(top_layout) 包含 “加载图片” 按钮和 “保存图像” 按钮。
2. 图像显示区域(img_layout) 包含两个 QLabel 标签:原始图像标签 和 处理后的图像标签。
3. 底部区域(bottom_layout) 包含三个示例功能按钮:灰度化、去噪、锐化。 用户可以选择其中一个按钮来对图像进行相应的处理,并将处理后的图像显示在右侧。
4.各模块介绍
1.水平盒子布局(Horizontal Box Layout) 意思是:接下来加的组件会横着排一排。 PyQt集成了很多像这样的组件,大家看到不懂的可以随时查一下名字,很容易理解。
2.让点击按钮时能干活 save_btn.clicked.connect(self.save_image)。按钮本身不会干事,这句的意思是: 当你点“加载图片”按钮时,去执行 load_image() 这个函数(加载图片)。
1.我们创建一个水平布局img_layout 用来容纳图像显示区域。
2.我们创建两个“图像框”(QLabel),一个专门用来放原图,一个放处理后的图。
3.将图像框放到 img_layout 之中。
1.用一个循环,依次创建三个按钮 列表中里面每一对都是:按钮上显示的文字 和 对应的处理功能的名字。 如果要增加功能,首先需要在这添加好功能按钮。
2.为按钮绑定事件响应 事件响应函数:self.process(f) 这里的f就是我们的 处理功能的名字。
5.如何扩展功能
在前面我们讲到,每个按钮点击后,都会调用这个函数:self.process(f) 这里的 f 是一个字符串,比如 "gray"、"denoise"、"sharpen",表示我们要对图像进行什么处理。
这些操作的实现都写在 process() 这个函数里:这里的 f 是一个字符串,比如 "gray"、"denoise"、"sharpen",表示我们要对图像进行什么处理。
这些操作的实现都写在 process() 这个函数里:
1.添加功能按钮。
2.在 process() 函数中添加频域滤波逻辑。
6.总结
基于PyQt5的图像处理界面设计是开发图像处理软件的重要组成部分,PyQt5作为Python的GUI开发框架,具有丰富的组件和良好的跨平台支持,能够帮助开发者快速构建功能强大的图像处理应用程序。
在设计过程中,首先需要明确图像处理界面的基本功能和需求。例如,常见的功能包括图像加载、显示、编辑和保存等。PyQt5提供了QGraphicsView
和QGraphicsScene
等组件,能够实现图像的显示和交互,开发者可以通过这些组件加载图像,并在界面上进行操作。
界面设计时,通常需要构建清晰的布局,便于用户操作。PyQt5的QVBoxLayout
、QHBoxLayout
和QGridLayout
等布局管理器,可以帮助开发者根据功能需求合理安排按钮、菜单、显示区域等控件的位置。同时,为了提升用户体验,可以加入图像缩放、旋转、滤镜等常见功能按钮,用户可以通过点击按钮轻松进行图像的处理。
此外,PyQt5还提供了QFileDialog
、QMessageBox
等控件,帮助实现文件选择、图像保存以及错误提示等功能,增加了界面的交互性和友好度。结合OpenCV等图像处理库,PyQt5可以将图像处理算法和界面展示相结合,实现复杂的图像编辑、分析任务。
总结来说,基于PyQt5的图像处理界面设计,通过良好的界面布局和交互设计,可以为用户提供高效、易用的图像处理工具,同时结合图像处理库,实现强大的功能。
编辑不易,感谢观看。