PyQT4 - рисование на изображении для выбора региона

Я хотел бы написать инструмент для маркировки изображений, используя 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
    • когда выбор сделан, маска объекта отображается наложенной на исходное изображение.
    • когда выделение всех объектов завершено, программа сохраняет каждую маску объекта (фон: 0, передний план: 255) как отдельное изображение png
  • пользователь должен иметь возможность увеличивать/уменьшать изображение

Я уже писал похожую программу (без увеличения/уменьшения) на c++ с помощью wxWidgets. Я новичок в PyQT4 и пытаюсь понять, как все работает. Наиболее сложной частью кажется рисование и правильное получение масок объектов, даже когда пользователь увеличивает/уменьшает масштаб.

Какие классы PyQT идеально подходят для решения этой проблемы? Как я могу правильно получить маски объектов (возможно, в виде массива numpy) и сохранить их?

Большое спасибо.


Следуя вашей рекомендации, я написал фрагмент кода для отображения изображения и рисования изображения с помощью мыши (все еще на стадии экспериментов и обучения).

Я сохраняю изображение в QGraphicsPixmapItem, добавляю его на сцену. Затем я рисую изображение, переопределяя его метод рисования. Наконец, я переопределяю события мыши, чтобы получить положение мыши и нарисовать там круг. Но когда я перемещаю мышь, старый круг удаляется и рисуется новый. То есть круг не нарисован на самом изображении. Я думаю, мне следует использовать что-то вроде следующего, чтобы рисунок был постоянным на изображении:

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

Но проблема в том, что функция рисования уже принимает в качестве аргумента рисовальщик; воссоздание нового в функции рисования не работает (очевидно)..

Вот код:

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?

Вторая проблема заключается в том, как после рисования изображения получить маску выбранной области? Что-то вроде создания нового изображения с альфа-каналом, а затем извлечения значений пикселей? Или рисовать на пустом изображении параллельно? Затем я также должен отслеживать увеличение/уменьшение масштаба.


person Mutebessim    schedule 13.07.2011    source источник


Ответы (1)


У вас есть ряд различных вариантов, которые я закажу от более высокого уровня к более низкому уровню:

  1. Используйте QGraphicsScene, QGraphicsView и QGraphicsItems. Этот набор, вероятно, является основным вариантом и лучшим вариантом для операций с интенсивной графикой. Установив QGLWidget в качестве области просмотра, вы даже получите аппаратное ускорение на многих системы.
  2. Используйте QScrollArea для поддержки увеличения изображения, что может быть простым QLabel. Изменяя просматриваемую область, вы получите эффект масштабирования. QLabel можно использовать для рисования изображения, но вам придется вручную отслеживать выбранные области и делать любые наложения выделения.
  3. Используйте один QWidget и выполняйте пользовательское рисование. Вызывая update() после различных событий, вы будете в состоянии сделать любые необходимые изменения. Ожидайте делать практически все вручную.

Я бы рекомендовал подход номер 1. Вы можете использовать QGraphicsPixmapItem для хранения изображение. Затем вы можете создать графический элемент, представляющий ваш выбор, и использовать его ограничивающий прямоугольник, чтобы найти пересекающиеся области. QGraphicsView может обрабатывать все масштабирование за вас.

person Kaleb Pederson    schedule 13.07.2011
comment
Большое спасибо за этот хороший ответ. Думаю, я буду придерживаться вашей рекомендации и попробую первый подход. Знаете ли вы какой-нибудь пример кода, программное обеспечение с открытым исходным кодом, которое может быть полезным? (кроме образца imagewiever.py, который также весьма полезен) - person Mutebessim; 14.07.2011
comment
Предполагая, что вы можете читать C++ (или найти эквивалентные версии Python), взгляните на раздел «Графический вид» демонстраций (doc.trolltech.com/4.7/demos.html) и примеры (doc.trolltech.com/4.7/examples-graphicsview.html) - person Kaleb Pederson; 14.07.2011