使用PyQt裁剪图像的角度

时间:2019-12-04 12:23:57

标签: python python-3.x pyqt pyqt5

我想制作一个python脚本,其中程序显示包含矩形或正方形的图像,然后最终用户应通过在其角度上拖动一些指针来指定该形状的四个角度。这些指针将由程序呈现。

基于这些角度,程序应从其余图像中裁剪形状。

  • 程序将显示的图像示例:

enter image description here enter image description here

  • 指针图像:

enter image description here

  • 用户应该做什么:

enter image description here

  • 程序的输出:

enter image description here

我该怎么做? 我正在使用 Python3 PyQt5

这是我到目前为止所做的,是允许用户浏览以从他的计算机中选择图像并将其上传到程序的过程

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QCursor

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.setEnabled(True)
        Dialog.resize(1050, 800)
        Dialog.setMinimumSize(QtCore.QSize(1050, 800))
        Dialog.setMaximumSize(QtCore.QSize(1050, 800))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("project pic/images LPR icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        Dialog.setWindowIcon(icon)
        Dialog.setStyleSheet("background-color: rgb(217, 217, 217);\n"
"background-color: rgb(243, 243, 243);")
        self.UserImageLbl = QtWidgets.QLabel(Dialog)
        self.UserImageLbl.setGeometry(QtCore.QRect(130, 60, 800, 600))
        self.UserImageLbl.setMinimumSize(QtCore.QSize(800, 600))
        self.UserImageLbl.setMaximumSize(QtCore.QSize(800, 600))
        self.UserImageLbl.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.UserImageLbl.setFrameShadow(QtWidgets.QFrame.Plain)
        self.UserImageLbl.setLineWidth(1)
        self.UserImageLbl.setMidLineWidth(0)
        self.UserImageLbl.setText("")
        self.UserImageLbl.setPixmap(QtGui.QPixmap("project pic/upn.png"))
        self.UserImageLbl.setScaledContents(False)
        self.UserImageLbl.setAlignment(QtCore.Qt.AlignCenter)
        self.UserImageLbl.setObjectName("UserImageLbl")
        self.BrowesImageButton = QtWidgets.QPushButton(Dialog)
        self.BrowesImageButton.setGeometry(QtCore.QRect(430, 690, 230, 60))
        self.BrowesImageButton.setMinimumSize(QtCore.QSize(230, 60))
        self.BrowesImageButton.setMaximumSize(QtCore.QSize(230, 60))
        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei UI")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.BrowesImageButton.setFont(font)
        self.BrowesImageButton.setStyleSheet("background-color: rgb(0, 214, 157);\n"
"background-color: rgb(0, 170, 127);")
        self.BrowesImageButton.setCursor(QCursor(QtCore.Qt.PointingHandCursor))
       # self.BrowesImageButton.setStyleSheet("BrowesImageButton:hover { background-color:  rgb(0, 214, 157) }" )

        self.BrowesImageButton.setCheckable(True)
        self.BrowesImageButton.setObjectName("BrowesImageButton")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

        self.BrowesImageButton.clicked.connect(self.setImage)


    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", " Cropping Shapes"))
        self.BrowesImageButton.setText(_translate("Dialog", "Browse Image"))


    def setImage(self):
        #fileName, _ = QtWidgets.QFileDialog.getOpenFileName(None, "Select Image", "", "Image Files (*.png *.jpg *jpeg *.bmp);;All Files (*)") # Ask for file
        fileName, _ = QtWidgets.QFileDialog.getOpenFileName(None, "Select Image", "", "Image Files (*.png)") # Ask for file

        if fileName: # If the user gives a file
            pixmap = QtGui.QPixmap(fileName) # Setup pixmap with the provided image
            pixmap = pixmap.scaled(self.UserImageLbl.width(), self.UserImageLbl.height(), QtCore.Qt.KeepAspectRatio) # Scale pixmap
            self.UserImageLbl.setPixmap(pixmap) # Set the pixmap onto the label
            self.UserImageLbl.setAlignment(QtCore.Qt.AlignCenter) # Align the label to center


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())

谢谢。

1 个答案:

答案 0 :(得分:2)

在这种情况下,最好使用QGraphicsView,因为它允许我们添加其他图像而无需缩放图像。在QGraphicsView中,您可以单击包含图像的项目,然后使用my previous answer使用该信息,获得以下内容:

import os
import sys

from PyQt5 import QtCore, QtGui, QtWidgets


current_dir = os.path.dirname(os.path.realpath(__file__))
point_filename = os.path.join(current_dir, "41uu2.png")


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(QtWidgets.QGraphicsScene(), parent)
        self.pixmap_item = self.scene().addPixmap(QtGui.QPixmap())
        self.pixmap_item.setShapeMode(QtWidgets.QGraphicsPixmapItem.BoundingRectShape)

        self.setAlignment(QtCore.Qt.AlignCenter)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

    def set_image(self, pixmap):
        self.pixmap_item.setPixmap(pixmap)
        self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)


class CropView(GraphicsView):
    resultChanged = QtCore.pyqtSignal(QtGui.QPixmap)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.point_items = []

    def mousePressEvent(self, event):
        if not self.pixmap_item.pixmap().isNull():
            sp = self.mapToScene(event.pos())
            lp = self.pixmap_item.mapFromScene(sp)
            if self.pixmap_item.contains(lp):
                size = QtCore.QSize(30, 30)
                height = (
                    self.mapToScene(QtCore.QRect(QtCore.QPoint(), size))
                    .boundingRect()
                    .size()
                    .height()
                )
                pixmap = QtGui.QPixmap(point_filename)
                point_item = QtWidgets.QGraphicsPixmapItem(pixmap, self.pixmap_item)
                point_item.setOffset(
                    -QtCore.QRect(QtCore.QPoint(), pixmap.size()).center()
                )
                point_item.setPos(lp)
                scale = height / point_item.boundingRect().size().height()
                point_item.setScale(scale)
                self.point_items.append(point_item)
                if len(self.point_items) == 4:
                    points = []
                    for it in self.point_items:
                        points.append(it.pos().toPoint())
                    self.crop(points)
                elif len(self.point_items) == 5:
                    for it in self.point_items[:-1]:
                        self.scene().removeItem(it)
                    self.point_items = [self.point_items[-1]]
            else:
                print("outside")
        super().mousePressEvent(event)

    def crop(self, points):
        # https://stackoverflow.com/a/55714969/6622587
        polygon = QtGui.QPolygonF(points)
        path = QtGui.QPainterPath()
        path.addPolygon(polygon)

        source = self.pixmap_item.pixmap()

        r = path.boundingRect().toRect().intersected(source.rect())

        pixmap = QtGui.QPixmap(source.size())
        pixmap.fill(QtCore.Qt.transparent)
        painter = QtGui.QPainter(pixmap)
        painter.setClipPath(path)
        painter.drawPixmap(QtCore.QPoint(), source, source.rect())
        painter.end()
        result = pixmap.copy(r)
        self.resultChanged.emit(result)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFixedSize(1050, 600)

        self.left_view = CropView()
        self.rigth_view = GraphicsView()

        self.left_view.resultChanged.connect(self.rigth_view.set_image)

        button = QtWidgets.QPushButton(self.tr("Browse Image"))
        button.setStyleSheet("background-color: rgb(0, 214, 157);")
        button.setFixedSize(230, 60)
        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei UI")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        button.setFont(font)
        button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        button.clicked.connect(self.load_image)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QGridLayout(central_widget)
        lay.addWidget(self.left_view, 0, 0)
        lay.addWidget(self.rigth_view, 0, 1)
        lay.addWidget(button, 1, 0, 1, 2, alignment=QtCore.Qt.AlignHCenter)

    @QtCore.pyqtSlot()
    def load_image(self):
        fileName, _ = QtWidgets.QFileDialog.getOpenFileName(
            None, "Select Image", "", "Image Files (*.png)"
        )
        if fileName:
            pixmap = QtGui.QPixmap(fileName)
            self.left_view.set_image(pixmap)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

enter image description here