PyQT4 - 在图像上绘画以选择区域
我想使用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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您有许多不同的选项,我将从较高级别到较低级别进行排序:
我推荐方法1。您可以使用 QGraphicsPixmapItem 来保存您的图像。然后,您可以创建一个代表您的选择的图形项,并使用其边界矩形来查找 相交区域。 QGraphicsView 可以为您处理所有缩放。
You have a number of different options that I'll order from higher level to lower level:
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.