PyQT4 - 在图像上绘画以选择区域

发布于 2024-11-19 21:24:01 字数 3251 浏览 0 评论 0原文

我想使用PyQT4编写一个图像标记工具:

  • 从指定文件夹加载多个图像;对于每个图像:
    • 用户通过用鼠标绘制该对象的区域来从图像中选择对象(例如汽车)
    • 选择完成后,对象蒙版将显示为覆盖在原始图像上
    • 完成所有对象的选择后,程序会将每个对象蒙版(背景:0,前景:255)保存为单独的 png 图像
  • 用户应该能够放大/缩小图像

我已经用wxWidgets在c ++中编写了一个类似的程序(没有放大/缩小) 。 我对 PyQT4 和 PyQT4 很陌生。试图了解事情是如何运作的。 最困难的部分似乎是正确绘制和获取对象蒙版,即使用户放大/缩小也是如此。

哪些 PyQT 类最适合解决这个问题? 如何正确获取对象掩码(可能作为 numpy 数组)并保存它们?

多谢。


根据您的建议,我编写了一段代码,用于显示图像并用鼠标在图像上绘图(仍处于实验和学习阶段)。

我将图像存储在 QGraphicsPixmapItem 中,并将其添加到场景中。 然后,我通过重写其绘制方法来绘制图像。 最后,我重写鼠标事件来获取鼠标位置并在那里画一个圆圈。 但是当我移动鼠标时,旧的圆圈被删除并绘制一个新的圆圈。 也就是说,圆圈不是画在图像本身上的。 我想,我应该使用类似下面的东西,以便绘画在图像上永久存在:

painter = QPainter()
painter.begin(pixmap)
# here do the drawing
painter.end() 

但是,问题是,paint函数已经将画家作为参数;在绘制函数中重新创建一个新函数不起作用(显然)。

以下是代码:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class ImageDrawPanel(QGraphicsPixmapItem):
    def __init__(self, pixmap=None, parent=None, scene=None):
        super(ImageDrawPanel, self).__init__()
        self.x, self.y = -1, -1        
        self.radius = 10

        self.pen = QPen(Qt.SolidLine)
        self.pen.setColor(Qt.black)
        self.pen.setWidth(2)

        self.brush = QBrush(Qt.yellow)


    def paint(self, painter, option, widget=None):               
        painter.drawPixmap(0, 0, self.pixmap())                
        painter.setPen(self.pen)
        painter.setBrush(self.brush)        
        if self.x >= 0 and self.y >= 0:
            painter.drawEllipse(self.x-self.radius, self.y-self.radius, 2*self.radius, 2*self.radius)
            self.x, self.y = -1, -1

    def mousePressEvent (self, event):
        print 'mouse pressed'
        self.x=event.pos().x()
        self.y=event.pos().y()            
        self.update()

    def mouseMoveEvent (self, event):
        print 'mouse moving'
        self.x=event.pos().x()
        self.y=event.pos().y()            
        self.update()        

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.scene = QGraphicsScene()
        self.scene.setSceneRect(0, 0, 800, 600)

        pixmap=self.openImage()        
        self.imagePanel = ImageDrawPanel(scene = self.scene)
        self.imagePanel.setPixmap(pixmap)
        self.scene.addItem(self.imagePanel)

        self.view = QGraphicsView(self.scene)

        layout = QHBoxLayout()        
        layout.addWidget(self.view)

        self.widget = QWidget()
        self.widget.setLayout(layout)

        self.setCentralWidget(self.widget)
        self.setWindowTitle("Image Draw")

    def openImage(self):
        fname = QFileDialog.getOpenFileName(self, "Open image", ".", "Image Files (*.bmp *.jpg *.png *.xpm)")
        if fname.isEmpty(): return None
        return QPixmap(fname)        

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

我现在应该做什么,才能在图像上永久绘制? 我可以存储所有点并在油漆中重新绘制它们,但这似乎效率不高。 我应该在 QGraphicsScene 中进行绘图,而不是在 QGraphicsPixmapItem 本身中进行绘图吗?

第二个问题是,在图像上绘制后,如何获取所选区域的蒙版? 比如,创建一个带有 Alpha 通道的新图像,然后提取像素值? 或者,并行地在空图像上绘画?然后,我还应该跟踪放大/缩小..

I would like to write an image labeling tool using PyQT4:

  • load a number of images from a specified folder; for each image:
    • the user selects objects (e.g., car) from the images, by painting the region for that object with the mouse
    • when the selection is done, the object mask is shown overlaid on the original image
    • when selection of all objects completed, the program saves each object mask (background: 0, foreground: 255) as a separate png image
  • the user should be able to zoom in/out of the image

I already wrote a similar program (without zoom in/out) in c++ with wxWidgets.
I am quite new to PyQT4 & trying to learn how things work.
The most difficult part seems painting and getting the object masks correctly even when the user zooms in/out.

Which PyQT classes would be ideal for this problem?
How can I get the object masks correctly (maybe as numpy array) and save them?

Thanks a lot.


Following your recommendation I wrote a piece of code, to display an image and draw on the image with the mouse (still in the experimentation and learning stage).

I store the image in QGraphicsPixmapItem, add it to the scene.
Then, I paint the image by overriding its paint method.
Finally, I override the mouse events to get the mouse position and draw a circle there.
But when I move the mouse, the old circle gets deleted and a new one is painted.
That is, the circle is not painted on the image itself.
I think, I should use something like the following, so that the painting is permanent on the image:

painter = QPainter()
painter.begin(pixmap)
# here do the drawing
painter.end() 

But, the problem is, the paint function already takes a painter as an argument; re-creating a new one within the paint function does not work (obviously)..

Here is the code:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class ImageDrawPanel(QGraphicsPixmapItem):
    def __init__(self, pixmap=None, parent=None, scene=None):
        super(ImageDrawPanel, self).__init__()
        self.x, self.y = -1, -1        
        self.radius = 10

        self.pen = QPen(Qt.SolidLine)
        self.pen.setColor(Qt.black)
        self.pen.setWidth(2)

        self.brush = QBrush(Qt.yellow)


    def paint(self, painter, option, widget=None):               
        painter.drawPixmap(0, 0, self.pixmap())                
        painter.setPen(self.pen)
        painter.setBrush(self.brush)        
        if self.x >= 0 and self.y >= 0:
            painter.drawEllipse(self.x-self.radius, self.y-self.radius, 2*self.radius, 2*self.radius)
            self.x, self.y = -1, -1

    def mousePressEvent (self, event):
        print 'mouse pressed'
        self.x=event.pos().x()
        self.y=event.pos().y()            
        self.update()

    def mouseMoveEvent (self, event):
        print 'mouse moving'
        self.x=event.pos().x()
        self.y=event.pos().y()            
        self.update()        

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.scene = QGraphicsScene()
        self.scene.setSceneRect(0, 0, 800, 600)

        pixmap=self.openImage()        
        self.imagePanel = ImageDrawPanel(scene = self.scene)
        self.imagePanel.setPixmap(pixmap)
        self.scene.addItem(self.imagePanel)

        self.view = QGraphicsView(self.scene)

        layout = QHBoxLayout()        
        layout.addWidget(self.view)

        self.widget = QWidget()
        self.widget.setLayout(layout)

        self.setCentralWidget(self.widget)
        self.setWindowTitle("Image Draw")

    def openImage(self):
        fname = QFileDialog.getOpenFileName(self, "Open image", ".", "Image Files (*.bmp *.jpg *.png *.xpm)")
        if fname.isEmpty(): return None
        return QPixmap(fname)        

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

What should I do now, to permanently draw on the image?
I can store all the points and re-draw them in paint, but this does not seem efficient.
Should I do the drawing in QGraphicsScene, rather than in the QGraphicsPixmapItem itself?

The second problem is, after drawing on the image, how can I get the selected region mask?
Something like, creating a new image with an alpha channel, and then extracting the pixel values?
Or, paint on an empty image in parallel? Then, I should also keep track of zoom in/out..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

假装爱人 2024-11-26 21:24:01

您有许多不同的选项,我将从较高级别到较低级别进行排序:

  1. 使用 QGraphicsSceneQGraphicsViewQGraphicsItems。该设置可能构成图形密集型操作的主要选项和最佳选项。通过将 QGLWidget 设置为视口,您甚至可以在许多设备上获得硬件加速系统。
  2. 使用 QScrollArea 来支持放大图像,这可以是一个简单的 < a href="https://doc.qt.io/qt-4.8/qlabel.html" rel="nofollow noreferrer">QLabel。通过更改查看区域,您将获得有效的缩放。 QLabel 可用于绘制图像,但您必须手动跟踪选定区域并进行任何选择叠加。
  3. 使用单个 QWidget 并进行自定义绘制。通过在各种事件发生后调用 update()能够进行任何必要的更改。期望几乎所有事情都手动完成。

我推荐方法1。您可以使用 QGraphicsPixmapItem 来保存您的图像。然后,您可以创建一个代表您的选择的图形项,并使用其边界矩形来查找 相交区域。 QGraphicsView 可以为您处理所有缩放

You have a number of different options that I'll order from higher level to lower level:

  1. Use QGraphicsScene, QGraphicsView, and QGraphicsItems. This sets probably forms the main option and best option for graphics intensive operations. By setting a QGLWidget as the viewport you'll even have hardware acceleration on many systems.
  2. Use QScrollArea to support zooming in on your image, which can be a simple QLabel. By changing the viewed region you'll have effectual zoom. QLabel could be used to draw the image but you'll have to manually keep track of selected regions and do any selection overlays.
  3. Use a single QWidget and do custom painting. By calling update() after various events you'll be able to draw any necessary changes. Expect to do virtually everything manually.

I'd recommend approach number 1. You can use a QGraphicsPixmapItem to hold your image. You can then create a graphics item that represents your selection and use its bounding rectangle to find the intersecting areas. QGraphicsView can handle all the zooming for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文