实验目的与背景
在本文中,我将使用 PyQt5 和 OpenCV 实现一个简单的图像处理系统。该系统提供了一个用户友好的界面,可以加载、处理和保存图像。我们将实现多种图像处理功能,包括灰度化、去噪、锐化、高斯模糊、旋转、调整亮度/对比度和边缘检测。
工具与环境
-
PyQt5
-
OpenCV
-
NumPy
项目概述
我们将创建一个基于 PyQt5 的程序,用户可以通过图形界面加载图像文件,应用不同的处理效果,并保存处理后的图像。系统的主要功能包括:
-
加载和显示图像
-
将图像转换为灰度
-
应用去噪处理
-
锐化图像
-
应用高斯模糊
-
旋转图像
-
调整图像的亮度和对比度
-
检测图像边缘
代码实现
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("图像处理系统")
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;
}
QMainWindow {
border: none;
}
QLabel#imageLabel {
border: 2px solid #4a6ea9;
border-radius: 15px;
background-color: white;
padding: 10px;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.1);
}
QLabel#originalLabel {
background-color: #f9f9f9;
border-color: #4a90e2;
}
QLabel#processedLabel {
background-color: #f0f9f0;
border-color: #52c41a;
}
""")
# 创建顶部布局:加载按钮和保存按钮
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_layout = QHBoxLayout()
self.original_label = QLabel() # 用于显示原始图像
self.original_label.setObjectName("imageLabel")
self.original_label.setProperty("class", "originalLabel")
self.processed_label = QLabel() # 用于显示处理后的图像
self.processed_label.setObjectName("imageLabel")
self.processed_label.setProperty("class", "processedLabel")
self.processed_label2 = QLabel() # 用于显示处理后的图像
self.processed_label2.setObjectName("imageLabel")
self.processed_label2.setProperty("class", "processedLabel")
# 设置标签的固定大小和对齐方式
for label in (self.original_label, self.processed_label, self.processed_label2):
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.addWidget(self._v_line())
img_layout.addWidget(self.processed_label2)
img_layout.setSpacing(0)
main_layout.addLayout(img_layout)
main_layout.addWidget(self._h_line())
# 创建底部按钮布局:灰度化、去噪、锐化、高斯模糊、旋转、调整亮度/对比度、边缘检测
bottom_layout = QHBoxLayout()
for text, func in [("⚫灰度化", "gray"), ("🔍去噪", "denoise"),
("✨锐化", "sharpen"), ("🌀高斯模糊", "blur"),
("🔄旋转", "rotate"), ("💡调整亮度/对比度", "adjust"),
("⚡边缘检测", "edge")]:
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 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 == "blur": # 高斯模糊处理
result = cv2.GaussianBlur(img, (15, 15), 0)
elif mode == "rotate": # 旋转处理
rows, cols, ch = img.shape # 获取图像尺寸
M = cv2.getRotationMatrix2D((cols/2, rows/2), 90, 1) # 旋转矩阵
result = cv2.warpAffine(img, M, (cols, rows)) # 旋转图像
elif mode == "adjust": # 调整亮度和对比度
alpha = 1.5 # 对比度控制因子 (1.0-3.0)
beta = 50 # 亮度控制因子 (0-100)
result = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
elif mode == "edge": # 边缘检测
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
result = cv2.Canny(gray, 100, 200)
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR) # 转回三通道
else:
return
self.image_data['processed'] = result # 存储处理后的图像
self.show_image(result, self.processed_label) # 显示处理后的图像到第一个标签
self.show_image(result, self.processed_label2) # 显示处理后的图像到第二个标签
def show_image(self, img, label):
"""显示图像"""
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)) # 设置显示图像
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ImageProcessor()
window.show() # 显示主窗口
sys.exit(app.exec())
功能展示
加载图像
灰度化
去噪
锐化
高斯模糊
旋转
边缘检测
总结
我们实现了一个功能丰富的图像处理系统。该系统使用 PyQt5 构建用户界面,并利用 OpenCV 进行图像处理。这个项目适合初学者学习如何结合 PyQt5 和 OpenCV 进行图形界面开发和图像处理。未来可以进一步扩展功能,如添加更多图像处理算法、支持批量处理等。希望这篇文章对你有所帮助!