首页 > 其他分享 >使用pyqt,opencv,matplotlib开发一个图片浏览器!

使用pyqt,opencv,matplotlib开发一个图片浏览器!

时间:2024-04-06 14:32:58浏览次数:20  
标签:pixmap layout img self pyqt opencv matplotlib bar btn

使用python开发一个图片浏览器!

作用

  1. 读取图片到界面并显示,对与宽度或高度大于600像素的图片进行等比缩小,让图片适合显示到界面;
  2. 输出图片的基础信息,大小和颜色通道;
  3. 绘制图片的单颜色通道直方图;

效果

颜色直方图有记忆功能,方便做对比
在这里插入图片描述
在这里插入图片描述

代码

import copy
import cv2
import sys

from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox, QWidget, QVBoxLayout, QHBoxLayout, \
    QFrame, QToolButton, QSpacerItem, QLabel, QSizePolicy
from PyQt5.QtGui import QImage, QPixmap, QIcon, QPainter, QPen, QFont, QColor
from PyQt5.QtCore import QSize, QRect, Qt
from PyQt5 import QtWidgets, QtGui
import sys
import cv2
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 正确显示负号
class HistWidget(QWidget):
    def __init__(self, image_path = None):
        super().__init__()
        if image_path is not None:
            self.img_path = image_path
            self.initUI()

    def initUI(self):
        # 创建一个QVBoxLayout实例
        layout = QVBoxLayout(self)

        # 读取图片
        image_path = self.img_path # 替换为你的图片路径
        image = cv2.imread(image_path)

        # OpenCV读取的图像默认是BGR格式,我们需要将其转换为RGB格式
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

        # 提取R, G, B通道数据
        r, g, b = cv2.split(image)

        # 创建三个matplotlib的Figure和Axes实例,分别对应R, G, B通道
        figs = [Figure() for _ in range(3)]
        axes = [fig.add_subplot(111) for fig in figs]

        # 创建三个matplotlib的canvas,并添加到布局中
        canvases = [FigureCanvas(fig) for fig in figs]

        # 遍历每个通道,绘制直方图并添加到布局中
        for i, (canvas, ax, channel_data) in enumerate(zip(canvases, axes, [r, g, b])):
            ax.hist(channel_data.flatten(), bins=30, color=['red', 'green', 'blue'][i], alpha=0.7,
                    label=f'{["R", "G", "B"][i]}通道')
            ax.legend()
            ax.set_title(f'{["R", "G", "B"][i]} 通道直方图')

            # 将canvas添加到布局中
            layout.addWidget(canvas)

            # 设置窗口的标题和大小
        self.setWindowTitle('通道直方图')
        self.setGeometry(300, 300, 800, 600)
class ImageLabel(QLabel):
    """"
    用于显示图片的 Label
    """

    def __init__(self, parent=None):
        super().__init__(parent)
        self.x0 = 0
        self.y0 = 0
        self.x1 = 0
        self.y1 = 0
        self.flag = False  # 标记是否能够绘制矩形
        self.__isClear = False  # 标记是否是清除矩形
        self.setAlignment(Qt.AlignCenter)  # 居中对齐
        self.setFrameShape(QtWidgets.QFrame.Box)  # 设置边框
        self.setStyleSheet("border-width: 1px;border-style: solid;border-color: rgb(218, 218, 218)")
        self.setText("")
        self.__w, self.__h = 0, 0
        self.pixmap_width, self.pixmap_height = 0, 0  # pixmap 的宽度、高度
        self.pixmap_x_start, self.pixmap_y_start = 0, 0  # pixmap 在 label 中的起点位置
        self.pixmap_x_end, self.pixmap_y_end = 0, 0  # pixamp 在 label 中的终点位置
        self.img_x_start, self.img_y_start = 0, 0  # 图片中选择的矩形区域的起点位置
        self.img_x_end, self.img_y_end = 0, 0  # 图片中选择的矩形区域的终点位置
        self.autoFillBackground()

    # 鼠标点击事件
    def mousePressEvent(self, event):
        # self.flag = True
        # 鼠标点击,相当于开始绘制矩形,将 isClear 置为 False
        self.__isClear = False
        self.x0 = event.x()
        self.y0 = event.y()
        # 计算 Pixmap 在 Label 中的位置
        self.__w, self.__h = self.width(), self.height()
        self.pixmap_x_start = (self.__w - self.pixmap_width) / 2
        self.pixmap_y_start = (self.__h - self.pixmap_height) / 2
        self.pixmap_x_end = self.pixmap_x_start + self.pixmap_width
        self.pixmap_y_end = self.pixmap_y_start + self.pixmap_height

    # 鼠标释放事件
    def mouseReleaseEvent(self, event):
        # self.flag = False
        self.setCursor(Qt.ArrowCursor)  # 鼠标释放,矩形已经绘制完毕,恢复鼠标样式

    # 鼠标移动事件
    def mouseMoveEvent(self, event):
        if self.flag:
            self.x1 = event.x()
            self.y1 = event.y()
            self.update()

    def setPixmap(self, pixmap):
        super().setPixmap(pixmap)
        self.pixmap_width, self.pixmap_height = pixmap.width(), pixmap.height()

    # 绘制事件
    def paintEvent(self, event):
        super().paintEvent(event)

        # 判断是否是清除
        if self.__isClear:
            return  # 是清除,则不需要执行下面的绘制操作。即此次 paint 事件没有绘制操作,因此界面中没有绘制的图形(从而相当于清除整个界面中已有的图形)

        # 判断用户起始位置是否在图片区域,只有在图片区域才画选择的矩形图
        if (self.pixmap_x_start <= self.x0 <= self.pixmap_x_end) \
                and (self.pixmap_y_start <= self.y0 <= self.pixmap_y_end):
            # 判断结束位置是否在图片区域内,如果超过,则直接设置成图片区域的终点
            if self.x1 > self.pixmap_x_end:
                self.x1 = self.pixmap_x_end
            elif self.x1 < self.pixmap_x_start:
                self.x1 = self.pixmap_x_start

            if self.y1 > self.pixmap_y_end:
                self.y1 = self.pixmap_y_end
            elif self.y1 < self.pixmap_y_start:
                self.y1 = self.pixmap_y_start
            rect = QRect(self.x0, self.y0, self.x1 - self.x0, self.y1 - self.y0)
            painter = QPainter(self)
            painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
            painter.drawRect(rect)
            # 计算矩形区域在图片中的位置
            self.img_x_start = int(self.x0 - self.pixmap_x_start)
            self.img_x_end = int(self.x1 - self.pixmap_x_start)
            self.img_y_start = int(self.y0 - self.pixmap_y_start)
            self.img_y_end = int(self.y1 - self.pixmap_y_start)

    def clearRect(self):
        # 清除
        self.__isClear = True
        self.update()


class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initialize()

    def initialize(self):
        """
        Initialize the user interface and set initial states.
        """
        self.setup_ui()
        self.set_initial_states()

    def setup_ui(self):
        """
        Set up the main window layout and components.
        """
        self.setWindowTitle("图片信息查看器")
        self.resize(1200, 900)

        self.central_widget = QWidget(self)
        self.central_layout = QVBoxLayout(self.central_widget)
        self.setCentralWidget(self.central_widget)

        # Title bar with open, save, and undo buttons
        self.title_bar = QFrame(self.central_widget)
        self.title_bar.setFrameShape(QFrame.StyledPanel)
        self.title_bar.setFrameShadow(QFrame.Raised)
        self.title_layout = QHBoxLayout(self.title_bar)

        self.btn_open = QToolButton(self.title_bar)
        self.btn_save = QToolButton(self.title_bar)
        self.btn_undo = QToolButton(self.title_bar)

        self.title_layout.addWidget(self.btn_open)
        self.title_layout.addWidget(self.btn_save)
        self.title_layout.addWidget(self.btn_undo)

        # Control bar with confirm and cancel buttons
        self.control_bar = QFrame(self.title_bar)
        self.control_layout = QHBoxLayout(self.control_bar)
        self.btn_confirm = QToolButton(self.control_bar)
        self.btn_cancel = QToolButton(self.control_bar)

        self.control_layout.addWidget(self.btn_confirm)
        self.control_layout.addWidget(self.btn_cancel)
        self.title_layout.addWidget(self.control_bar)

        # Add a spacer item to the title layout
        self.title_layout.addItem(QSpacerItem(100, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))

        # Image frame with an ImageLabel widget to display images
        self.img_frame = QFrame(self.central_widget)
        self.img_frame.setFrameShape(QFrame.StyledPanel)
        self.img_frame.setFrameShadow(QFrame.Raised)
        self.img_layout = QVBoxLayout(self.img_frame)
        self.img_display = ImageLabel(self.img_frame)
        self.img_info = QFrame(self.img_frame)

        self.hist_win = QFrame()

        self.hist_win_layout = QHBoxLayout()
        self.hist_win.setLayout(self.hist_win_layout)
        self.img_hist_win = HistWidget()
        self.hist_win_layout.addWidget(self.img_display)
        self.hist_win_layout.addWidget(self.img_hist_win)

        self.img_info.setFixedHeight(50)
        self.img_info_layout = QHBoxLayout()
        self.img_info.setLayout(self.img_info_layout)
        self.w_label = QLabel('宽度: xxx 像素')
        self.h_label = QLabel('高度: xxx 像素')
        self.c_label = QLabel('通道数: x ')
        # 创建标签来显示信息
        labels = [
            self.w_label,
            self.h_label,
            self.c_label,
        ]

        # 设置字体样式、大小和颜色
        font = QFont()
        font.setFamily('Arial')  # 设置字体类型,例如 'Arial'
        font.setPointSize(12)  # 设置字体大小,例如 12

        # 应用字体和颜色到每个标签
        for label in labels:
            label.setFont(font)  # 设置字体
            label.setStyleSheet("color: rgb(35, 184, 80);")  # 设置颜色,也可以使用QLabel的setPalette方法

        # 添加标签到布局中
        for label in labels:
            self.img_info_layout.addWidget(label)

        self.img_layout.addWidget(self.hist_win)
        self.img_layout.addWidget(self.img_info)
        self.central_layout.addWidget(self.title_bar)
        self.central_layout.addWidget(self.img_frame)

        # Set button text, icons, styles, and layout styles
        self.set_buttons_text_icons()
        self.set_buttons_styles()
        self.set_layout_styles()

    def set_initial_states(self):
        """
        Set initial states for UI components.
        """
        self.control_bar.setVisible(False)

    def set_buttons_text_icons(self):
        """
        Set button texts, icons, and style for open and save buttons.
        """
        self.btn_open.setText("打开")
        self.btn_open.setIcon(QIcon("./icon/open.png"))
        self.btn_open.setIconSize(QSize(36, 36))
        self.btn_open.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btn_open.clicked.connect(self.open_img)

        self.btn_save.setText("保存")
        self.btn_save.setIcon(QIcon("./icon/save.png"))
        self.btn_save.setIconSize(QSize(36, 36))
        self.btn_save.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btn_save.clicked.connect(self.save_img)

    def set_buttons_styles(self):
        """
        Set styles for all buttons and the control bar.
        """
        transparent_button_style = "background: rgba(0, 0, 0, 0); color: rgb(255, 255, 255);"
        gray_button_style = "background: rgb(80, 80, 80); color: rgb(255, 255, 255);"

        buttons = [self.btn_open, self.btn_save, self.btn_undo, self.btn_confirm, self.btn_cancel]
        for btn in buttons:
            btn.setStyleSheet(transparent_button_style)

        self.control_bar.setStyleSheet(gray_button_style)

    def set_layout_styles(self):
        """
        Set styles for central layout and title bar.
        """
        self.central_layout.setContentsMargins(0, 0, 0, 0)
        self.central_layout.setSpacing(0)

        self.title_bar.setMinimumSize(QSize(0, 55))
        self.title_bar.setMaximumSize(QSize(188888, 55))

        self.control_bar.setMinimumSize(QSize(0, 45))
        self.control_bar.setMaximumSize(QSize(120, 45))

        self.img_frame.setMinimumSize(QSize(100, 0))

        font = QtGui.QFont()
        font.setPointSize(8)
        self.setFont(font)
        self.btn_open.setFont(font)
        self.btn_save.setFont(font)

        self.central_widget.setStyleSheet("background: rgb(252, 255, 255);")
        self.title_bar.setStyleSheet("background: rgb(146, 6, 213);")

    def open_img(self):
        """
        Open an image file using a file dialog and display it.
        """
        img_name, img_type = QFileDialog.getOpenFileName(
            self, "打开图片", "", "*.jpg;*.png;*.jpeg"
        )
        if img_name == "" or img_name is None:
            self.show_warning_message_box("未选择图片")
            return
        self.img_path = img_name
        img = cv2.imread(img_name)
        self.show_image(img)
        self.current_img = img
        self.last_img = self.current_img
        self.original_img = copy.deepcopy(self.current_img)
        self.original_img_path = img_name

    def show_image(self, img, is_grayscale=False):
        """
        Display an image in the ImageLabel widget.

        Args:
            img (numpy.ndarray): The image to display (in BGR or grayscale format).
            is_grayscale (bool, optional): Whether the image is grayscale. Defaults to False.
        """
        if len(img.shape) == 3:  # Color image
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # Convert BGR to RGB

        height, width, channels = img.shape
        bytes_per_line = channels * width

        if len(img.shape) == 2:  # Grayscale image
            format = QImage.Format_Grayscale8
            bytes_per_line *= 1  # Treat grayscale image as having one channel
        else:  # RGB image
            format = QImage.Format_RGB888

        qimage = QImage(img.data, width, height, bytes_per_line, format)
        pixmap = QPixmap.fromImage(qimage)
        if pixmap.width() > 600 or pixmap.height() > 600:
            pixmap = pixmap.scaled(600, 600, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self.img_display.setPixmap(pixmap)
        self.img_hist_win=HistWidget(self.img_path)
        self.hist_win_layout.addWidget(self.img_hist_win)
        self.img_display.repaint()
        height, width = img.shape[:2]
        # 获取图片的通道数
        channels = img.shape[2] if len(img.shape) > 2 else 1
        self.w_label.setText(f"宽度: {width} 像素")
        self.h_label.setText(f"高度: {height} 像素")
        self.c_label.setText(f"通道数: {channels}")

    def crop_image(self, src_img, x_start, x_end, y_start, y_end):
        """
        Crop an image.

        Args:
            src_img (numpy.ndarray): The source image to crop.
            x_start (int): Starting x-coordinate of the crop region.
            x_end (int): Ending x-coordinate of the crop region.
            y_start (int): Starting y-coordinate of the crop region.
            y_end (int): Ending y-coordinate of the crop region.

        Returns:
            numpy.ndarray: The cropped image.
        """
        return src_img[y_start:y_end, x_start:x_end]

    def show_warning_message_box(self, msg):
        """
        Show a warning message box with the given message.

        Args:
            msg (str): The message to display.
        """
        QMessageBox.warning(self, "警告", msg, QMessageBox.Ok)

    def show_info_message_box(self, msg):
        """
        Show an information message box with the given message.

        Args:
            msg (str): The message to display.
        """
        QMessageBox.information(self, "提示", msg, QMessageBox.Ok)

    def save_img(self):
        """
        Save the current image to a file using a file dialog.
        """
        if self.current_img is None:
            self.show_warning_message_box("未选择图片")
            return

        ext_name = self.original_img_path[self.original_img_path.rindex("."):]
        img_path, img_type = QFileDialog.getSaveFileName(
            self, "保存图片", self.original_img_path, f"*{ext_name}"
        )
        cv2.imwrite(img_path, self.current_img)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

标签:pixmap,layout,img,self,pyqt,opencv,matplotlib,bar,btn
From: https://blog.csdn.net/summerriver1/article/details/137427934

相关文章

  • 使用OpenCV和PyQT编写的图片显示器,实现图片选择显示和保存
    使用OpenCV和PyQT编写的图片显示器简要效果代码简要为了避免重复编写pyqt控件,先写好此代码,方便使用。实现了以下基础功能:选择图片显示图片保存图片效果选择图片:显示图片:保存图片:代码importcopyimportcv2importsysfromPyQt5.QtWidgetsimport(......
  • opencv-python库 cv2图像二值化详解
    文章目录图像二值化原理cv2.threshold()Qtsu二值化cv2.adaptiveThreshold图像二值化原理图像二值化原理是通过设定一个阈值,将图像中的像素点的灰度值与阈值进行比较,大于阈值的像素点设置为白色,小于阈值的像素点设置为黑色1。图像二值化是将彩色或灰度图像转换为只包......
  • Python_matplotlib进阶
    Python_matplotlib跳转链接之前的博客中已经展示了使用python的matplotlib库进行一些基础图像的绘制,本篇进一步展示一些matplotlib中的一些进阶图像绘制。importpandasaspdimportmatplotlib.pyplotaspltplt.rcParams['font.sans-serif']=['SimHei']Titanic=......
  • Opencv各个颜色空间、用途(颜色通道分割与合并)
    Opencv各个颜色空间、用途(颜色通道分割与合并)OpenCV中提供了多种颜色空间,每种颜色空间都有其特定的用途。以下是一些常见的颜色空间及其用途:BGR颜色空间:BGR颜色空间是一种与计算机显示器显示的颜色相同的颜色空间。它由蓝色、绿色和红色通道组成,通常用于图像处理和计算机......
  • Python 使用matplotlib创建各种静态、动态、交互式和3D图表的功能
    在Python中,你可以使用各种库来创建和显示图表。其中,最常用的库之一是matplotlib,它提供了创建各种静态、动态、交互式和3D图表的功能。另一个流行的库是seaborn,它基于matplotlib,并提供了更高级别的界面,用于绘制有吸引力的统计图形。以下是一个使用matplotlib创建并显示简单折线......
  • PyQt5 GUI编程(QMainWindow与QWidget模块结合使用)
    一.简介QWidget是所有用户界面对象的基类,而QMainWindow用于创建主应用程序窗口的类。它是QWidget的一个子类,提供了创建具有菜单栏、工具栏、状态栏等的主窗口所需的功能。上篇主要介绍了基本使用,创建窗口时都是继承单个模块,本章主要介绍下两个模块的结合使用。二.具体用法1.我......
  • OpenCV的下载与安装
    首先进入官网→Releases-OpenCV,选择自己想要的版本进行下载 这里建议右键windows按钮复制下载链接在迅雷里下载会快一些双击下载好的安装包进行安装安装到自己想要的路径下 等待安装完成就可以了 接下来进行环境变量右键“此电脑”——>点击“属性”——>点击“......
  • 数据分析-Matplotlib模块
    简介Pyplot是Matplotlib的子库,提供了和MATLAB类似的绘图API。Pyplot是常用的绘图模块,能很方便让用户绘制2D图表。Pyplot包含一系列绘图函数的相关函数,每个函数会对当前的图像进行一些修改,例如:给图像加上标记,生新的图像,在图像中产生新的绘图区域等等。pyplot子模块的......
  • 图像处理环境配置opencv-python
    下载python,配置pip使用清华源下载镜像:​pipconfigsetglobal.index-urlhttps://pypi.tuna.tsinghua.edu.cn/simple​切换到python目录下,右击cmd,执行pip升级指令:​python-mpipinstall--upgradepip​下载opencv:​pipinstall--useropencv-python​opencv核心库​p......
  • Python的opencv库的函数合集(持续更新中)
    为自己也为别人,整合opencv的函数,欢迎纠错!目录1.imread()2.cv2.imshow()1.imread()介绍:cv2.imread()是OpenCV库中的一个函数,专门用于读取图片文件并将其转换为NumPy数组。此函数对于图像处理和计算机视觉应用非常有用,因为它提供了读取图片到程序中的基本能力。格式:参数......