使用opnecv裁剪鼠标所选范围的图片

使用opnecv裁剪鼠标所选范围的图片

裁剪

opencv实现裁剪很简单,确定左上角和右下角的坐标进行切片即可。

代码

import cv2

# 读取图片
image = cv2.imread('blue_2.jpg')
# 定义裁剪区域的左上角和右下角坐标
top_left = (50, 50)  # (x, y)
bottom_right = (200, 200)  # (x, y)
# 使用NumPy切片来裁剪图片
cropped_image = image[top_left[1]:bottom_right[1], top_left[0]:bottom_right[0]]
# 显示裁剪后的图片
cv2.imshow('Cropped Image', cropped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 保存裁剪后的图片
cv2.imwrite('cropped_image.jpg', cropped_image)

效果

在这里插入图片描述
在这里插入图片描述

根据鼠标选择范围进行裁剪

相对来说比较麻烦。要根据鼠标选择范围,得先读图片并显示图片,裁剪后需要进行保存。使用pyqt写了一个,有需要可以进行保存,下次使用可以直接用了~

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码

# 使用opnecv裁剪鼠标所选范围的图片
"""



"""
import copy
import cv2
import sys
from PyQt5 import QtCore, QtGui,QtWidgets
from PyQt5.QtGui import QImage, QPixmap, QIcon,QPainter, QPen
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QApplication, QMainWindow,QLabel
from PyQt5.QtCore import QSize, QRect, Qt

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(MyWindow, self).__init__()
        self.btn_cancel = None
        self.btn_confirm = None
        self.control_layout = None
        self.control = None
        self.btn_undo = None
        self.btn_save = None
        self.btn_open = None
        self.title_button_layout = None
        self.operation = None
        self.title_layout = None
        self.title = None
        self.central_widget_layout = None
        self.central_widget = None
        self.last_img = None
        self.current_operation = None
        self.original_img = None
        self.icon_path = None
        self.current_img = None
        self.setupUi()

    def setupUi(self):

        self.resize(926, 806)
        # self.central_widget:主窗口
        self.central_widget = QtWidgets.QWidget(self)
        self.central_widget_layout = QtWidgets.QVBoxLayout()
        self.central_widget.setLayout(self.central_widget_layout)
        # 主窗口布局间隙
        self.central_widget_layout.setContentsMargins(0, 0, 0, 0)
        self.central_widget_layout.setSpacing(0)
        #  self.title:横向菜单栏
        self.title = QtWidgets.QFrame(self.central_widget)
        self.title.setMinimumSize(QtCore.QSize(0, 55))
        self.title.setMaximumSize(QtCore.QSize(188888, 55))
        self.title.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.title.setFrameShadow(QtWidgets.QFrame.Raised)
        # self.title_layout:横向菜单栏布局
        self.title_layout = QtWidgets.QHBoxLayout()
        self.title.setLayout(self.title_layout)
        self.operation = QtWidgets.QFrame(self.title)
        self.operation.setMinimumSize(QtCore.QSize(250, 45))
        self.operation.setMaximumSize(QtCore.QSize(250, 45))
        self.operation.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.operation.setFrameShadow(QtWidgets.QFrame.Raised)
        # title_button_layout:title的按钮横向布局
        self.title_button_layout = QtWidgets.QHBoxLayout()
        self.operation.setLayout(self.title_button_layout)
        self.btn_open = QtWidgets.QToolButton(self.operation)
        self.title_button_layout.addWidget(self.btn_open)
        self.btn_save = QtWidgets.QToolButton(self.operation)
        self.title_button_layout.addWidget(self.btn_save)
        self.btn_undo = QtWidgets.QToolButton(self.operation)
        self.title_button_layout.addWidget(self.btn_undo)
        self.title_layout.addWidget(self.operation)
        # spacerItem弹簧
        spacerItem = QtWidgets.QSpacerItem(100, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.title_layout.addItem(spacerItem)
        # self.control:确定取消按钮控件
        self.control = QtWidgets.QFrame(self.title)
        self.control.setMinimumSize(QtCore.QSize(0, 45))
        self.control.setMaximumSize(QtCore.QSize(120, 45))
        self.control.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.control.setFrameShadow(QtWidgets.QFrame.Raised)
        self.control_layout = QtWidgets.QHBoxLayout(self.control)
        self.btn_confirm = QtWidgets.QToolButton(self.control)
        self.control_layout.addWidget(self.btn_confirm)
        self.btn_cancel = QtWidgets.QToolButton(self.control)
        self.control_layout.addWidget(self.btn_cancel)
        self.title_layout.addWidget(self.control)
        # 主窗口布局添加标题菜单控件
        self.central_widget_layout.addWidget(self.title)
        self.action_img_layout = QtWidgets.QHBoxLayout()
        self.action_img_layout.setSpacing(0)
        self.action_back_frame = QtWidgets.QFrame(self.central_widget)
        self.action_back_frame.setMinimumSize(QtCore.QSize(100, 0))
        self.action_back_frame.setMaximumSize(QtCore.QSize(100, 16777215))
        self.action_back_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.action_back_frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.action_options_frame = QtWidgets.QFrame(self.action_back_frame)
        self.action_options_frame.setGeometry(QtCore.QRect(10, 0, 56, 500))
        self.action_options_frame.setMinimumSize(QtCore.QSize(0, 500))
        self.action_options_frame.setMaximumSize(QtCore.QSize(16777215, 500))
        self.action_options_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.action_options_frame.setFrameShadow(QtWidgets.QFrame.Raised)
        # 图片操作选项竖向布局
        self.action_layout = QtWidgets.QVBoxLayout(self.action_options_frame)
        self.btn_clip = QtWidgets.QToolButton(self.action_options_frame)
        self.btn_clip.setMinimumSize(QtCore.QSize(50, 0))
        self.action_layout.addWidget(self.btn_clip)

        self.action_img_layout.addWidget(self.action_back_frame)
        self.img_frame = QtWidgets.QFrame(self.central_widget)
        self.img_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.img_frame.setFrameShadow(QtWidgets.QFrame.Raised)
        # self.img_frame布局
        self.img_frame_layout = QtWidgets.QHBoxLayout()
        self.img_frame_layout.setContentsMargins(0, 0, 0, 0)
        self.img_frame_layout.setSpacing(0)
        self.img_frame.setLayout(self.img_frame_layout)
        self.img_display = ImageLabel(self.img_frame)
        self.img_frame_layout.addWidget(self.img_display)
        self.action_img_layout.addWidget(self.img_frame)
        self.central_widget_layout.addLayout(self.action_img_layout)
        self.setCentralWidget(self.central_widget)
        self.control.hide()
        # 按钮显示文字
        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_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_undo.setText("恢复")
        self.btn_undo.setIcon(QIcon("./icon/undo.png"))
        self.btn_undo.setIconSize(QSize(36, 36))
        self.btn_undo.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btn_confirm.setText("确定")
        self.btn_cancel.setText("取消")
        self.btn_clip.setText("裁剪")
        self.btn_clip.setIcon(QIcon("./icon/clip.png"))
        self.btn_clip.setIconSize(QSize(36, 36))
        self.btn_clip.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)


        # 按钮绑定事件
        self.btn_open.clicked.connect(self.open_img)
        self.btn_save.clicked.connect(self.save_img)
        self.btn_undo.clicked.connect(self.undo_img)
        self.btn_confirm.clicked.connect(self.confirm_img)
        self.btn_cancel.clicked.connect(self.cancel_img)
        self.btn_clip.clicked.connect(self.clip_img)


        # 对象名
        self.setObjectName("MainWindow")
        self.central_widget.setObjectName("central_widget")
        self.central_widget_layout.setObjectName("central_widget_layout")
        self.title_layout.setObjectName("title_layout")
        self.title.setObjectName("title")
        self.operation.setObjectName("operation")
        self.title_button_layout.setObjectName("title_button_layout")
        self.btn_open.setObjectName("btn_open")
        self.btn_save.setObjectName("btn_save")
        self.btn_undo.setObjectName("btn_undo")
        self.control.setObjectName("control")
        self.control_layout.setObjectName("control_layout")
        self.btn_confirm.setObjectName("btn_confirm")
        self.btn_cancel.setObjectName("btn_cancel")
        self.action_img_layout.setObjectName("action_img_layout")
        self.action_back_frame.setObjectName("action_back_frame")
        self.action_options_frame.setObjectName("action_options_frame")
        self.action_layout.setObjectName("action_layout")
        self.btn_clip.setObjectName("btn_clip")

        self.img_frame.setObjectName("img_frame")
        self.img_frame_layout.setObjectName("img_frame_layout")
        self.img_display.setObjectName("img_display")

        # 字体统一定义
        font = QtGui.QFont()
        font.setPointSize(8)
        self.setFont(font)
        self.btn_open.setFont(font)
        self.btn_save.setFont(font)
        self.btn_clip.setFont(font)
        self.btn_cancel.setFont(font)
        self.btn_confirm.setFont(font)
        self.btn_undo.setFont(font)

        # 格式设置
        self.central_widget.setStyleSheet("background: rgb(252, 255, 255)")
        self.title.setStyleSheet("background: rgb(60, 60, 60)")
        self.btn_open.setStyleSheet("background: rgba(0, 0, 0, 0);\n"
                                    "color: rgb(255, 255, 255)")
        self.btn_save.setStyleSheet("background: rgba(0, 0, 0, 0);\n"
                                    "color: rgb(255, 255, 255)")
        self.btn_undo.setStyleSheet("background: rgba(0, 0, 0, 0);\n"
                                    "color: rgb(255, 255, 255)")
        self.btn_confirm.setStyleSheet("background: rgba(0, 0, 0, 0);\n"
                                       "color: rgb(255, 255, 255)")
        self.btn_cancel.setStyleSheet("background: rgba(0, 0, 0, 0);\n"
                                      "color: rgb(255, 255, 255)")
        self.action_back_frame.setStyleSheet("background: rgb(80, 80, 80)")
        self.btn_clip.setStyleSheet("background: rgba(0, 0, 0, 0);\n"
                                    "color: rgb(255, 255, 255)")

    def open_img(self):
        """
        “打开” 按钮的点击事件
        """
        img_name, img_type = QFileDialog.getOpenFileName(self, "打开图片", "", "*.jpg;*.png;*.jpeg")
        if (img_name == "") or (img_name is None):
            self.__show_warning_message_box("未选择图片")
            return

        img = cv2.imread(img_name)  # 读取图像
        self.showImage(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 showImage(self, img, is_grayscale=False):
        x = img.shape[1]  # 获取图像大小
        y = img.shape[0]
        self.zoomscale = 1  # 图片放缩尺度
        bytesPerLine = 3 * x
        if len(img.shape) == 2:  # 判断是否为灰度图,如果是灰度图,需要转换成三通道图
            img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        frame = QImage(img.data, x, y, bytesPerLine, QImage.Format_RGB888).rgbSwapped()
        pix = QPixmap.fromImage(frame)
        self.img_display.setPixmap(pix)
        self.img_display.repaint()

    def clip_img(self):
        """
        "裁剪"按钮事件
        :return:
        """
        if self.current_img is None:
            self.__show_warning_message_box("未选择图片")
            return

        self.current_operation = "clip"
        self.img_display.flag = True  # 标记 img_panel 可以绘制矩形,从而选择裁剪区域
        self.img_display.setCursor(Qt.CrossCursor)
        self.control.show()

    def crop_image(self, src_img, x_start, x_end, y_start, y_end):
        """
        :param src_img: 原始图片
        :param x_start: x 起始坐标
        :param x_end:  x 结束坐标
        :param y_start:  y 开始坐标
        :param y_end: y 结束坐标
        :return:
        """
        tmp_img = cv2.cvtColor(src_img, cv2.COLOR_BGR2RGB)
        tmp_img = tmp_img[y_start:y_end, x_start:x_end]  # 长,宽
        return cv2.cvtColor(tmp_img, cv2.COLOR_RGB2BGR)

    def __show_warning_message_box(self, msg):
        QMessageBox.warning(self, "警告", msg, QMessageBox.Ok)

    def __show_info_message_box(self, msg):
        QMessageBox.information(self, "提示", msg, QMessageBox.Ok)

    def undo_img(self):
        """
        “恢复” 按钮的点击事件,将图片恢复到最初的状态
        :return:
        """
        if self.current_img is None:
            self.__show_warning_message_box("未选择图片")
            return
        self.current_img = self.original_img
        self.last_img = self.current_img
        self.showImage(self.current_img)

    def save_img(self):
        """
         “保存” 按钮的点击事件,将图片恢复到最初的状态
        :return:
        """
        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, "*" + ext_name)
        cv2.imwrite(img_path, self.current_img)

    def confirm_img(self):
        """
        "确定"按钮时间
        :return:
        """
        self.control.hide()
        # 根据操作类型进行相应的处理
        if self.current_operation == "clip":
            x_start, x_end = self.img_display.img_x_start, self.img_display.img_x_end
            y_start, y_end = self.img_display.img_y_start, self.img_display.img_y_end
            self.current_img = self.crop_image(self.current_img, x_start, x_end, y_start, y_end)
            self.showImage(self.current_img)
            self.img_display.clearRect()
            self.img_display.flag = False  # 标记 img_display 不能绘制矩形,从而禁止选择裁剪区域
        elif self.current_operation == "base_color":
            self.last_img = self.current_img
        elif self.current_operation == "flip":
            self.last_img = self.current_img
        elif self.current_operation == "size":
            self.last_img = self.current_img
        elif self.current_operation == "correction":
            self.last_img = self.current_img
        self.last_img = self.current_img  # 将当前操作得到的图片结果保存到 last_img 中(相对于后面的操作而言,本次操作的结果就是 last 的)
        self.current_operation = None

    def cancel_img(self):
        """
        “取消”按钮事件
        :return:
        """
        self.control.hide()
        if self.current_operation in ["clip", "blur"]:
            self.img_display.clearRect()
            self.img_display.flag = False  # 标记 img_display 不能绘制矩形,从而禁止选择裁剪区域
        elif self.current_operation == "lightness":
            self.slider_lightness.setValue(int((self.lightness_max + self.lightness_min) / 2))
        self.current_img = self.last_img
        self.showImage(self.current_img)
        self.current_operation = None


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

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-23 19:18:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-23 19:18:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-23 19:18:01       82 阅读
  4. Python语言-面向对象

    2024-03-23 19:18:01       91 阅读

热门阅读

  1. 超级电容实际可用电量估算

    2024-03-23 19:18:01       162 阅读
  2. android CoordinatorLayout使用

    2024-03-23 19:18:01       44 阅读
  3. 安卓的几个核心的组件的简单介绍

    2024-03-23 19:18:01       41 阅读
  4. Linux进程间通信之消息队列

    2024-03-23 19:18:01       47 阅读
  5. 实用网站推荐 [持续更新中...]

    2024-03-23 19:18:01       48 阅读
  6. Qt拖放文件实现(Drag,Drop, MimeData )

    2024-03-23 19:18:01       40 阅读
  7. Leetcode-06-Z字形变换

    2024-03-23 19:18:01       43 阅读