使用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_())
标签:裁剪,layout,鼠标,img,self,opnecv,QtWidgets,btn,frame
From: https://blog.csdn.net/summerriver1/article/details/136822718