class QExampleLabel(QLabel): def __init__(self, parentQWidget=None): super(QExampleLabel, self).__init__(parentQWidget) self.initUI() def initUI(self): self.setPixmap(QtGui.QPixmap('input.png')) def mousePressEvent(self, eventQMouseEvent): self.originQPoint = eventQMouseEvent.pos() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry( QtCore.QRect(self.originQPoint, QtCore.QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, eventQMouseEvent): self.currentQRubberBand.setGeometry( QtCore.QRect(self.originQPoint, eventQMouseEvent.pos()).normalized()) def mouseReleaseEvent(self, eventQMouseEvent): self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() cropQPixmap = self.pixmap().copy(currentQRect) cropQPixmap.save('output.png')
class QImageEdit(QLabel): def __init__(self, parentQWidget=None): super(QImageEdit, self).__init__(parentQWidget) self.rubberBand = None self.move_rubberBand = False self.rubberBand_offset = None self.originPoint = None def setImage(self, image: QPixmap): self.setPixmap(image) def getImage(self) -> QPixmap: if self.rubberBand is not None: currentRect = self.rubberBand.geometry() return self.pixmap().copy(currentRect) else: return self.pixmap() def clear(self): super(QImageEdit, self).clear() if self.rubberBand is not None: self.rubberBand.deleteLater() self.rubberBand = None self.move_rubberBand = False self.rubberBand_offset = None self.originPoint = None def mousePressEvent(self, event): self.originPoint = event.pos() if self.rubberBand is None: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(self.originPoint, QSize())) self.rubberBand.show() else: if self.rubberBand.geometry().contains(self.originPoint): self.rubberBand_offset = \ self.originPoint - self.rubberBand.pos() self.move_rubberBand = True else: self.rubberBand.hide() self.rubberBand.deleteLater() self.rubberBand = None self.move_rubberBand = False self.rubberBand_offset = None self.mousePressEvent(event) def mouseMoveEvent(self, event): newPoint = event.pos() if self.move_rubberBand: self.rubberBand.move(newPoint - self.rubberBand_offset) else: self.rubberBand.setGeometry( QRect(self.originPoint, newPoint).normalized()) def mouseReleaseEvent(self, event): self.move_rubberBand = False
class QMainLabel(QLabel): def __init__(self, parentQWidget): super(QMainLabel, self).__init__(parentQWidget) self.original_image_pixmap = None self.original_mask_pixmap = None self.target_size = self.parent().main_label_size def set_size(self, size): self.target_size = size def load_pixmap(self, image, mask=None): self.original_image_pixmap = QPixmap(image) if mask: self.original_mask_pixmap = QPixmap(mask) combo = self.merge_image_mask(self.original_image_pixmap, self.original_mask_pixmap) self.setPixmap(combo) return self.setPixmap(self.original_image_pixmap) def merge_image_mask(self, image, mask): print(type(image), type(mask)) if type(image) == QPixmap and type(mask) == QPixmap: pil_image = ImageQt.fromqpixmap(image) pil_mask = ImageQt.fromqpixmap(mask) else: pil_image = image pil_mask = mask np_image = np.array(pil_image) np_mask = np.array(pil_mask) print(np_image.shape, np_mask.shape) prob = np.float32(np_mask) / 255.0 np_combo = np.uint8(np_image * (1 - prob) + np_mask * prob * np.array([[[0., 1., 0.]]])) pil_combo = Image.fromarray(np_combo) combo = ImageQt.toqpixmap(pil_combo) return combo def restore_original(self): print('Ctrl+Z') image = self.original_image_pixmap mask = self.original_mask_pixmap if image and mask: image = self.merge_image_mask(image, mask) self.setPixmap(image) def mousePressEvent(self, eventQMouseEvent): self.originQPoint = eventQMouseEvent.pos() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(QRect(self.originQPoint, QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, eventQMouseEvent): self.currentQRubberBand.setGeometry( QRect(self.originQPoint, eventQMouseEvent.pos()).normalized()) def mouseReleaseEvent(self, eventQMouseEvent): self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() cropQPixmap = self.pixmap().copy(currentQRect) # cropQPixmap.save('output.png') crop_PIL = ImageQt.fromqpixmap(cropQPixmap) crop_PIL = pad_to_square(crop_PIL, self.target_size) cropQPixmap = ImageQt.toqpixmap(crop_PIL) self.setPixmap(cropQPixmap)
class ImageViewer(QLabel): def __init__(self): super(ImageViewer, self).__init__() self.crop = Signal() #pixmap = QPixmap(640, 640) pixmap = QPixmap() self.setPixmap(pixmap) self.first_x, self.first_y = None, None self.last_x, self.last_y = None, None self.pen_color = QColor('#000000') self.scaleFactor = 0.0 self.setBackgroundRole(QPalette.Base) #self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) #self.setScaledContents(True) #self.setGeometry(QRect(110, 550, 640, 640)) self.setText("Feature") self.setAlignment(Qt.AlignTop | Qt.AlignLeft) #self.setAlignment(Qt.AlignCenter) self.setWordWrap(False) #self.setFixedSize(640,640) self.createActions() self.show() #self.showMaximized() def setImage(self, q_img): self.setPixmap(q_img) self.scaleFactor = 1.0 #self.fitToWindowAct.setEnabled(True) self.updateActions() if not self.fitToWindowAct.isChecked(): self.adjustSize() def open(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath()) if fileName: image = QImage(fileName) if image.isNull(): QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName) return self.setPixmap(QPixmap.fromImage(image)) self.scaleFactor = 1.0 self.fitToWindowAct.setEnabled(True) self.updateActions() if not self.fitToWindowAct.isChecked(): self.adjustSize() def zoomIn(self): self.scaleImage(1.25) def zoomOut(self): self.scaleImage(0.8) def normalSize(self): self.adjustSize() self.scaleFactor = 1.0 def fitToWindow(self): fitToWindow = self.fitToWindowAct.isChecked() self.scrollArea.setWidgetResizable(fitToWindow) if not fitToWindow: self.normalSize() self.updateActions() def createActions(self): self.openAct = QAction("&Open...", self, shortcut="Ctrl+O", triggered=self.open) self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++", enabled=False, triggered=self.zoomIn) self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-", enabled=False, triggered=self.zoomOut) self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S", enabled=False, triggered=self.normalSize) self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False, checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow) def updateActions(self): self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) def scaleImage(self, factor): self.scaleFactor *= factor self.resize(self.scaleFactor * self.pixmap().size()) self.zoomInAct.setEnabled(self.scaleFactor < 3.0) self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) def mousePressEvent(self, eventQMouseEvent): self.originQPoint = self.mapFrom(self, eventQMouseEvent.pos()) self.first_x = int(eventQMouseEvent.x()) self.first_y = int(eventQMouseEvent.y()) self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(QRect(self.originQPoint, QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, eventQMouseEvent): self.x = int(eventQMouseEvent.x()) self.y = int(eventQMouseEvent.y()) text1 = str(self.x) text2 = str(self.y) p = self.mapToGlobal(eventQMouseEvent.pos()) #QToolTip.showText(eventQMouseEvent.pos() , "X: "+text1+" "+"Y: "+text2,self) QToolTip.showText(p, "X: " + text1 + " " + "Y: " + text2, self) # print ('mouse QToolTip 1') 전체장에서의 위젯 시작점의 좌표가 오리진 포인트가 되어야 함. ''' if self.currentQRubberBand.isVisible(): self.currentQRubberBand.setGeometry(QRect(self.originQPoint, eventQMouseEvent.pos()).normalized() & self.pixmap().rect()) ''' pos = self.mapFromGlobal(self.mapToGlobal(eventQMouseEvent.pos())) #QToolTip.showText(eventQMouseEvent.pos(), "X: {} Y: {}".format(p.x(), p.y()), self) if self.currentQRubberBand.isVisible() and self.pixmap() is not None: self.currentQRubberBand.setGeometry( QRect(self.originQPoint, pos).normalized() & self.rect()) def mouseReleaseEvent(self, eventQMouseEvent): self.last_x = int(eventQMouseEvent.x()) self.last_y = int(eventQMouseEvent.y()) ''' self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() if self.pixmap() is not None: tr = QTransform() if self.fitToWindowAct.isChecked(): tr.scale(self.pixmap().width() / self.scrollArea.width(), self.pixmap().height() / self.scrollArea.height()) else: tr.scale(1 / self.scaleFactor, 1 / self.scaleFactor) r = tr.mapRect(currentQRect) cropQPixmap = self.pixmap().copy(r) cropQPixmap.save('output.png') ''' self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() cropQPixmap = self.pixmap().copy(currentQRect) #size = cropQPixmap.size() # 직접변환 해서 저장하는 코드... 나중에 #s = cropQPixmap.bits().asstring(size.width() * size.height() * image.depth() // 8) # format 0xffRRGGBB #arr = np.fromstring(s, dtype=np.uint8).reshape((size.height(), size.width(), cropQPixmap.depth() // 8)) #new_image = Image.fromarray(array) cropQPixmap.save('output.png') img = cv2.imread("output.png", cv2.IMREAD_COLOR) h, w = self.cut_imgSize() self.crop.cut_signal.emit(img, h, w) def cut_imgSize(self): h = abs(self.last_y - self.first_y) w = abs(self.last_x - self.first_x) return h, w
class ImageDisplay(QGraphicsScene): """ A class to control the image display in the IPView GUI. """ # used to inform mouse event methods which button type is desired (i.e. mouse move or release) CROP_IMAGE = Qt.LeftButton PIXEL_FETCH = Qt.RightButton IMAGE_REVERT = 'PASS' #################################################################################################################### def __init__(self, ui: ipview_ui.IPViewWindow): """ """ self.ui = ui super(ImageDisplay, self).__init__() # self.__scene = QGraphicsScene() self.ui.image_display.setScene(self) self.__q_graphics_view = self.ui.image_display # image currently on display self.__displayed_image = None self.__displayed_image_orig = None # data members reserved for mouse events and the sharing of data between override methods. self.__orig_pos_scene = None self.__orig_pos_screen = None self.__current_rubber_band = None self.__cropped_image_rect = None self.__button_clicked_type = None self.stream_display = StreamDisplay.StreamDisplay(ui=self.ui) #################################################################################################################### def next_image(self) -> None: """ Method for a signal from the next push button. """ image = self.ui.app_data.get_next_image() self.__displayed_image_orig = image self.__display_image(image=image) return #################################################################################################################### def previous_image(self) -> None: """ Method for a signal from the previous push button. """ image = self.ui.app_data.get_previous_image() self.__displayed_image_orig = image self.__display_image(image=image) return #################################################################################################################### def clear_display(self) -> None: """ Method to clear the image display. """ self.clear() self.ui.image_display.show() self.__displayed_image = None # reset image held in object self.__displayed_image_orig = None # reset original image held in object self.__cropped_image_rect = None self.__button_clicked_type = None return #################################################################################################################### def get_displayed_image(self) -> im.Image: """ :return: Image currently on display. """ return self.__displayed_image #################################################################################################################### def mousePressEvent(self, event: QGraphicsSceneMouseEvent): """ Override method of a mouse click event in QGraphicsView. - Left button provides the cropping feature. - Right button provides the pixel value. """ if self.__displayed_image is None: return xy = event.scenePos().toPoint() height = self.ui.image_display.sceneRect().height() width = self.ui.image_display.sceneRect().width() if event.button() == ImageDisplay.CROP_IMAGE: self.__button_clicked_type = ImageDisplay.CROP_IMAGE # cursor location self.__orig_pos_scene = xy self.__orig_pos_screen = event.screenPos() # rubber band for zoom feature self.__current_rubber_band = QRubberBand(QRubberBand.Rectangle) self.__current_rubber_band.setGeometry( QRect(self.__orig_pos_screen, self.__orig_pos_screen)) self.__current_rubber_band.show() elif event.button() == ImageDisplay.PIXEL_FETCH: self.__button_clicked_type = ImageDisplay.PIXEL_FETCH # return if click is not in image scene if xy.x() < 0 or xy.x() >= width or xy.y() < 0 or xy.y() >= height: return self.stream_display.append_row( 'Pixel: row[{0:.2f}], col[{1:.2f}]'.format(xy.y(), xy.x())) return #################################################################################################################### def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: """ Override method to move rubber band while mouse button is pressed. """ if self.__button_clicked_type == ImageDisplay.CROP_IMAGE: xy = event.screenPos() current_pos_screen = QPoint(int(xy.x()), int(xy.y())) q_rect = self.__get_q_rect_from_corners( corner_one=self.__orig_pos_screen, corner_two=current_pos_screen, scene_coordinates=False) self.__current_rubber_band.setGeometry(q_rect) return #################################################################################################################### def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: """ Override method to complete crop of image. """ if self.__button_clicked_type == ImageDisplay.CROP_IMAGE: self.__current_rubber_band.hide() self.__cropped_image_rect = self.__get_q_rect_from_corners( corner_one=self.__orig_pos_scene, corner_two=event.scenePos().toPoint(), scene_coordinates=True) self.__crop_info_to_stream() self.__current_rubber_band.deleteLater() cropped_image = self.__displayed_image.copy( self.__cropped_image_rect) self.__display_image(image=cropped_image) return #################################################################################################################### def mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent): """ Override method to display original image. """ self.__button_clicked_type = ImageDisplay.IMAGE_REVERT self.__display_image(image=self.__displayed_image_orig) self.stream_display.clear_text() self.stream_display.append_row("Image reverted to original") return #################################################################################################################### def __display_image(self, image: im.Image) -> None: """ """ if image is not None: self.clear() self.__displayed_image = image # set reference to image in object self.addPixmap(QPixmap.fromImage(self.__displayed_image)) self.ui.image_display.setSceneRect( QRectF(self.__displayed_image.rect())) # ensures scene rectangle (rect) fits in view port. self.ui.image_display.fitInView(self.ui.image_display.sceneRect(), Qt.KeepAspectRatio) self.ui.image_display.show() return #################################################################################################################### def __get_q_rect_from_corners(self, corner_one: QPoint, corner_two: QPoint, scene_coordinates: bool = False) -> QRect: """ Method to return QRect box compatible with image indexing. The need for this function is to allow capabilities of zoom to be captured from a box creation in any direction, instead of forcing an upper-left to lower-right box creation. :param corner_one: a corner of either screen or scene :param corner_two: a corner opposite of corner_one :param scene_coordinates: True if corners are in scene coordinates. NOTE: If they are, QRect returned will be within scene QRect. :return: QRect in the format (QPoint [upper-left], QPoint[lower-right]) in input coordinates. Note: coordinate system is row -> y, col -> x """ row_1 = corner_one.y() col_1 = corner_one.x() row_2 = corner_two.y() col_2 = corner_two.x() row_min = min(row_1, row_2) row_max = max(row_1, row_2) col_min = min(col_1, col_2) col_max = max(col_1, col_2) # force corners to be within image if corners are in scene coordinates. if scene_coordinates: height = self.ui.image_display.sceneRect().height() width = self.ui.image_display.sceneRect().width() if row_min < 0: row_min = 0 if col_min < 0: col_min = 0 if row_max >= height: row_max = height - 1 if col_max >= width: col_max = width - 1 upper_left = QPoint(col_min, row_min) lower_right = QPoint(col_max, row_max) return QRect(upper_left, lower_right) #################################################################################################################### def __crop_info_to_stream(self) -> None: """ Method to write image crop information to stream. """ self.stream_display.append_row('Image Cropped, New Parameters: ') self.stream_display.append_row( 'Rows: {0:.2f}, Columns: {1:.2f}'.format( self.__cropped_image_rect.height(), self.__cropped_image_rect.width()))