DIP实验4:基于 PyQt5+OpenCV 搭建交互式图像处理工具

实验目的与背景

在本文中,我将使用 PyQt5 和 OpenCV 实现一个简单的图像处理系统。该系统提供了一个用户友好的界面,可以加载、处理和保存图像。我们将实现多种图像处理功能,包括灰度化、去噪、锐化、高斯模糊、旋转、调整亮度/对比度和边缘检测。

工具与环境

  1. PyQt5

  2. OpenCV

  3. NumPy

 项目概述

我们将创建一个基于 PyQt5 的程序,用户可以通过图形界面加载图像文件,应用不同的处理效果,并保存处理后的图像。系统的主要功能包括:

  1. 加载和显示图像

  2. 将图像转换为灰度

  3. 应用去噪处理

  4. 锐化图像

  5. 应用高斯模糊

  6. 旋转图像

  7. 调整图像的亮度和对比度

  8. 检测图像边缘

代码实现 

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 进行图形界面开发和图像处理。未来可以进一步扩展功能,如添加更多图像处理算法、支持批量处理等。希望这篇文章对你有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值