class Canvas(QWidget): zoomRequest = pyqtSignal(int) scrollRequest = pyqtSignal(int, int) newShape = pyqtSignal() # selectionChanged = pyqtSignal(bool) selectionChanged = pyqtSignal(list) shapeMoved = pyqtSignal() drawingPolygon = pyqtSignal(bool) CREATE, EDIT = list(range(2)) _fill_drawing = False # draw shadows epsilon = 5.0 def __init__(self, *args, **kwargs): super(Canvas, self).__init__(*args, **kwargs) # Initialise local state. self.mode = self.EDIT self.shapes = [] self.shapesBackups = [] self.current = None self.selectedShapes = [] self.selectedShape = None # save the selected shape here self.selectedShapesCopy = [] self.drawingLineColor = QColor(0, 0, 255) self.drawingRectColor = QColor(0, 0, 255) self.line = Shape(line_color=self.drawingLineColor) self.prevPoint = QPointF() self.offsets = QPointF(), QPointF() self.scale = 1.0 self.pixmap = QPixmap() self.visible = {} self._hideBackround = False self.hideBackround = False self.hShape = None self.hVertex = None self._painter = QPainter() self._cursor = CURSOR_DEFAULT # Menus: self.menus = (QMenu(), QMenu()) # Set widget options. self.setMouseTracking(True) self.setFocusPolicy(Qt.WheelFocus) self.verified = False self.drawSquare = False self.fourpoint = True # ADD self.pointnum = 0 self.movingShape = False self.selectCountShape = False #initialisation for panning self.pan_initial_pos = QPoint() def setDrawingColor(self, qColor): self.drawingLineColor = qColor self.drawingRectColor = qColor def enterEvent(self, ev): self.overrideCursor(self._cursor) def leaveEvent(self, ev): self.restoreCursor() def focusOutEvent(self, ev): self.restoreCursor() def isVisible(self, shape): return self.visible.get(shape, True) def drawing(self): return self.mode == self.CREATE def editing(self): return self.mode == self.EDIT def setEditing(self, value=True): self.mode = self.EDIT if value else self.CREATE if not value: # Create self.unHighlight() self.deSelectShape() self.prevPoint = QPointF() self.repaint() def unHighlight(self): if self.hShape: self.hShape.highlightClear() self.hVertex = self.hShape = None def selectedVertex(self): return self.hVertex is not None def mouseMoveEvent(self, ev): """Update line with last point and current coordinates.""" pos = self.transformPos(ev.pos()) # Update coordinates in status bar if image is opened window = self.parent().window() if window.filePath is not None: self.parent().window().labelCoordinates.setText('X: %d; Y: %d' % (pos.x(), pos.y())) # Polygon drawing. if self.drawing(): self.overrideCursor(CURSOR_DRAW) # ? if self.current: # Display annotation width and height while drawing currentWidth = abs(self.current[0].x() - pos.x()) currentHeight = abs(self.current[0].y() - pos.y()) self.parent().window().labelCoordinates.setText( 'Width: %d, Height: %d / X: %d; Y: %d' % (currentWidth, currentHeight, pos.x(), pos.y())) color = self.drawingLineColor if self.outOfPixmap(pos): # Don't allow the user to draw outside the pixmap. # Clip the coordinates to 0 or max, # if they are outside the range [0, max] size = self.pixmap.size() clipped_x = min(max(0, pos.x()), size.width()) clipped_y = min(max(0, pos.y()), size.height()) pos = QPointF(clipped_x, clipped_y) elif len(self.current) > 1 and self.closeEnough( pos, self.current[0]): # Attract line to starting point and colorise to alert the # user: pos = self.current[0] color = self.current.line_color self.overrideCursor(CURSOR_POINT) self.current.highlightVertex(0, Shape.NEAR_VERTEX) if self.drawSquare: self.line.points = [self.current[0], pos] self.line.close() elif self.fourpoint: self.line[0] = self.current[-1] self.line[1] = pos else: self.line[1] = pos # pos is the mouse's current position self.line.line_color = color self.prevPoint = QPointF() # ? self.current.highlightClear() else: self.prevPoint = pos self.repaint() return # Polygon copy moving. if Qt.RightButton & ev.buttons(): if self.selectedShapesCopy and self.prevPoint: self.overrideCursor(CURSOR_MOVE) self.boundedMoveShape(self.selectedShapesCopy, pos) self.repaint() elif self.selectedShapes: self.selectedShapesCopy = [ s.copy() for s in self.selectedShapes ] self.repaint() return # Polygon/Vertex moving. if Qt.LeftButton & ev.buttons(): if self.selectedVertex(): self.boundedMoveVertex(pos) self.shapeMoved.emit() self.repaint() self.movingShape = True elif self.selectedShapes and self.prevPoint: self.overrideCursor(CURSOR_MOVE) self.boundedMoveShape(self.selectedShapes, pos) self.shapeMoved.emit() self.repaint() self.movingShape = True else: #pan delta_x = pos.x() - self.pan_initial_pos.x() delta_y = pos.y() - self.pan_initial_pos.y() self.scrollRequest.emit(delta_x, Qt.Horizontal) self.scrollRequest.emit(delta_y, Qt.Vertical) self.update() return # Just hovering over the canvas, 2 posibilities: # - Highlight shapes # - Highlight vertex # Update shape/vertex fill and tooltip value accordingly. self.setToolTip("Image") for shape in reversed([s for s in self.shapes if self.isVisible(s)]): # Look for a nearby vertex to highlight. If that fails, # check if we happen to be inside a shape. index = shape.nearestVertex(pos, self.epsilon) if index is not None: if self.selectedVertex(): self.hShape.highlightClear() self.hVertex, self.hShape = index, shape shape.highlightVertex(index, shape.MOVE_VERTEX) self.overrideCursor(CURSOR_POINT) self.setToolTip("Click & drag to move point") self.setStatusTip(self.toolTip()) self.update() break elif shape.containsPoint(pos): if self.selectedVertex(): self.hShape.highlightClear() self.hVertex, self.hShape = None, shape self.setToolTip("Click & drag to move shape '%s'" % shape.label) self.setStatusTip(self.toolTip()) self.overrideCursor(CURSOR_GRAB) self.update() break else: # Nothing found, clear highlights, reset state. if self.hShape: self.hShape.highlightClear() self.update() self.hVertex, self.hShape = None, None self.overrideCursor(CURSOR_DEFAULT) def mousePressEvent(self, ev): pos = self.transformPos(ev.pos()) if ev.button() == Qt.LeftButton: if self.drawing(): # self.handleDrawing(pos) # OLD if self.current: if self.fourpoint: # ADD IF # Add point to existing shape. # print('Adding points in mousePressEvent is ', self.line[1]) self.current.addPoint(self.line[1]) self.line[0] = self.current[-1] if self.current.isClosed(): # print('1111') self.finalise() elif self.drawSquare: # 增加 assert len(self.current.points) == 1 self.current.points = self.line.points self.finalise() elif not self.outOfPixmap(pos): # Create new shape. self.current = Shape() self.current.addPoint(pos) self.line.points = [pos, pos] self.setHiding() self.drawingPolygon.emit(True) self.update() else: group_mode = int(ev.modifiers()) == Qt.ControlModifier self.selectShapePoint(pos, multiple_selection_mode=group_mode) self.prevPoint = pos self.pan_initial_pos = pos elif ev.button() == Qt.RightButton and self.editing(): group_mode = int(ev.modifiers()) == Qt.ControlModifier self.selectShapePoint(pos, multiple_selection_mode=group_mode) self.prevPoint = pos self.update() def mouseReleaseEvent(self, ev): if ev.button() == Qt.RightButton: menu = self.menus[bool(self.selectedShapesCopy)] self.restoreCursor() if not menu.exec_(self.mapToGlobal(ev.pos()))\ and self.selectedShapesCopy: # Cancel the move by deleting the shadow copy. # self.selectedShapeCopy = None self.selectedShapesCopy = [] self.repaint() elif ev.button() == Qt.LeftButton and self.selectedShapes: if self.selectedVertex(): self.overrideCursor(CURSOR_POINT) else: self.overrideCursor(CURSOR_GRAB) elif ev.button() == Qt.LeftButton and not self.fourpoint: pos = self.transformPos(ev.pos()) if self.drawing(): self.handleDrawing(pos) else: #pan QApplication.restoreOverrideCursor() # ? if self.movingShape and self.hShape: index = self.shapes.index(self.hShape) if (self.shapesBackups[-1][index].points != self.shapes[index].points): self.storeShapes() self.shapeMoved.emit( ) # connect to updateBoxlist in PPOCRLabel.py self.movingShape = False def endMove(self, copy=False): assert self.selectedShapes and self.selectedShapesCopy assert len(self.selectedShapesCopy) == len(self.selectedShapes) if copy: for i, shape in enumerate(self.selectedShapesCopy): self.shapes.append(shape) self.selectedShapes[i].selected = False self.selectedShapes[i] = shape else: for i, shape in enumerate(self.selectedShapesCopy): self.selectedShapes[i].points = shape.points self.selectedShapesCopy = [] self.repaint() self.storeShapes() return True def hideBackroundShapes(self, value): self.hideBackround = value if self.selectedShapes: # Only hide other shapes if there is a current selection. # Otherwise the user will not be able to select a shape. self.setHiding(True) self.repaint() def handleDrawing(self, pos): if self.current and self.current.reachMaxPoints() is False: if self.fourpoint: targetPos = self.line[self.pointnum] self.current.addPoint(targetPos) print('current points in handleDrawing is ', self.line[self.pointnum]) self.update() if self.pointnum == 3: self.finalise() else: initPos = self.current[0] print('initPos', self.current[0]) minX = initPos.x() minY = initPos.y() targetPos = self.line[1] maxX = targetPos.x() maxY = targetPos.y() self.current.addPoint(QPointF(maxX, minY)) self.current.addPoint(targetPos) self.current.addPoint(QPointF(minX, maxY)) self.finalise() elif not self.outOfPixmap(pos): print('release') self.current = Shape() self.current.addPoint(pos) self.line.points = [pos, pos] self.setHiding() self.drawingPolygon.emit(True) self.update() def setHiding(self, enable=True): self._hideBackround = self.hideBackround if enable else False def canCloseShape(self): return self.drawing() and self.current and len(self.current) > 2 def mouseDoubleClickEvent(self, ev): # We need at least 4 points here, since the mousePress handler # adds an extra one before this handler is called. if self.canCloseShape() and len(self.current) > 3: if not self.fourpoint: self.current.popPoint() self.finalise() def selectShapes(self, shapes): for s in shapes: s.seleted = True self.setHiding() self.selectionChanged.emit(shapes) self.update() def selectShapePoint(self, point, multiple_selection_mode): """Select the first shape created which contains this point.""" if self.selectedVertex(): # A vertex is marked for selection. index, shape = self.hVertex, self.hShape shape.highlightVertex(index, shape.MOVE_VERTEX) return self.hVertex else: for shape in reversed(self.shapes): if self.isVisible(shape) and shape.containsPoint(point): self.calculateOffsets(shape, point) self.setHiding() if multiple_selection_mode: if shape not in self.selectedShapes: # list self.selectionChanged.emit(self.selectedShapes + [shape]) else: self.selectionChanged.emit([shape]) return self.deSelectShape() def calculateOffsets(self, shape, point): rect = shape.boundingRect() x1 = rect.x() - point.x() y1 = rect.y() - point.y() x2 = (rect.x() + rect.width()) - point.x() y2 = (rect.y() + rect.height()) - point.y() self.offsets = QPointF(x1, y1), QPointF(x2, y2) def snapPointToCanvas(self, x, y): """ Moves a point x,y to within the boundaries of the canvas. :return: (x,y,snapped) where snapped is True if x or y were changed, False if not. """ if x < 0 or x > self.pixmap.width() or y < 0 or y > self.pixmap.height( ): x = max(x, 0) y = max(y, 0) x = min(x, self.pixmap.width()) y = min(y, self.pixmap.height()) return x, y, True return x, y, False def boundedMoveVertex(self, pos): index, shape = self.hVertex, self.hShape point = shape[index] if self.outOfPixmap(pos): size = self.pixmap.size() clipped_x = min(max(0, pos.x()), size.width()) clipped_y = min(max(0, pos.y()), size.height()) pos = QPointF(clipped_x, clipped_y) if self.drawSquare: opposite_point_index = (index + 2) % 4 opposite_point = shape[opposite_point_index] min_size = min(abs(pos.x() - opposite_point.x()), abs(pos.y() - opposite_point.y())) directionX = -1 if pos.x() - opposite_point.x() < 0 else 1 directionY = -1 if pos.y() - opposite_point.y() < 0 else 1 shiftPos = QPointF( opposite_point.x() + directionX * min_size - point.x(), opposite_point.y() + directionY * min_size - point.y()) else: shiftPos = pos - point if [shape[0].x(), shape[0].y(), shape[2].x(), shape[2].y()] \ == [shape[3].x(),shape[1].y(),shape[1].x(),shape[3].y()]: shape.moveVertexBy(index, shiftPos) lindex = (index + 1) % 4 rindex = (index + 3) % 4 lshift = None rshift = None if index % 2 == 0: rshift = QPointF(shiftPos.x(), 0) lshift = QPointF(0, shiftPos.y()) else: lshift = QPointF(shiftPos.x(), 0) rshift = QPointF(0, shiftPos.y()) shape.moveVertexBy(rindex, rshift) shape.moveVertexBy(lindex, lshift) else: shape.moveVertexBy(index, shiftPos) def boundedMoveShape(self, shapes, pos): if type(shapes).__name__ != 'list': shapes = [shapes] if self.outOfPixmap(pos): return False # No need to move o1 = pos + self.offsets[0] if self.outOfPixmap(o1): pos -= QPointF(min(0, o1.x()), min(0, o1.y())) o2 = pos + self.offsets[1] if self.outOfPixmap(o2): pos += QPointF(min(0, self.pixmap.width() - o2.x()), min(0, self.pixmap.height() - o2.y())) # The next line tracks the new position of the cursor # relative to the shape, but also results in making it # a bit "shaky" when nearing the border and allows it to # go outside of the shape's area for some reason. XXX #self.calculateOffsets(self.selectedShape, pos) dp = pos - self.prevPoint if dp: for shape in shapes: shape.moveBy(dp) self.prevPoint = pos return True return False def deSelectShape(self): if self.selectedShapes: for shape in self.selectedShapes: shape.selected = False self.setHiding(False) self.selectionChanged.emit([]) self.update() def deleteSelected(self): deleted_shapes = [] if self.selectedShapes: for shape in self.selectedShapes: self.shapes.remove(shape) deleted_shapes.append(shape) self.storeShapes() self.selectedShapes = [] self.update() return deleted_shapes def storeShapes(self): shapesBackup = [] for shape in self.shapes: shapesBackup.append(shape.copy()) if len(self.shapesBackups) >= 10: self.shapesBackups = self.shapesBackups[-9:] self.shapesBackups.append(shapesBackup) def copySelectedShape(self): if self.selectedShapes: self.selectedShapesCopy = [s.copy() for s in self.selectedShapes] self.boundedShiftShapes(self.selectedShapesCopy) self.endMove(copy=True) return self.selectedShapes def boundedShiftShapes(self, shapes): # Try to move in one direction, and if it fails in another. # Give up if both fail. for shape in shapes: point = shape[0] offset = QPointF(2.0, 2.0) self.calculateOffsets(shape, point) self.prevPoint = point if not self.boundedMoveShape(shape, point - offset): self.boundedMoveShape(shape, point + offset) def paintEvent(self, event): if not self.pixmap: return super(Canvas, self).paintEvent(event) p = self._painter p.begin(self) p.setRenderHint(QPainter.Antialiasing) p.setRenderHint(QPainter.HighQualityAntialiasing) p.setRenderHint(QPainter.SmoothPixmapTransform) p.scale(self.scale, self.scale) p.translate(self.offsetToCenter()) p.drawPixmap(0, 0, self.pixmap) Shape.scale = self.scale for shape in self.shapes: if (shape.selected or not self._hideBackround) and self.isVisible(shape): shape.fill = shape.selected or shape == self.hShape shape.paint(p) if self.current: self.current.paint(p) self.line.paint(p) if self.selectedShapesCopy: for s in self.selectedShapesCopy: s.paint(p) # Paint rect if self.current is not None and len( self.line) == 2 and not self.fourpoint: # print('Drawing rect') leftTop = self.line[0] rightBottom = self.line[1] rectWidth = rightBottom.x() - leftTop.x() rectHeight = rightBottom.y() - leftTop.y() p.setPen(self.drawingRectColor) brush = QBrush(Qt.BDiagPattern) p.setBrush(brush) p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight) # ADD: if (self.fillDrawing() and self.fourpoint and self.current is not None and len(self.current.points) >= 2): print('paint event') drawing_shape = self.current.copy() drawing_shape.addPoint(self.line[1]) drawing_shape.fill = True drawing_shape.paint(p) if self.drawing() and not self.prevPoint.isNull( ) and not self.outOfPixmap(self.prevPoint): p.setPen(QColor(0, 0, 0)) p.drawLine(self.prevPoint.x(), 0, self.prevPoint.x(), self.pixmap.height()) p.drawLine(0, self.prevPoint.y(), self.pixmap.width(), self.prevPoint.y()) self.setAutoFillBackground(True) if self.verified: pal = self.palette() pal.setColor(self.backgroundRole(), QColor(184, 239, 38, 128)) self.setPalette(pal) else: pal = self.palette() pal.setColor(self.backgroundRole(), QColor(232, 232, 232, 255)) self.setPalette(pal) p.end() def fillDrawing(self): return self._fill_drawing def transformPos(self, point): """Convert from widget-logical coordinates to painter-logical coordinates.""" return point / self.scale - self.offsetToCenter() def offsetToCenter(self): s = self.scale area = super(Canvas, self).size() w, h = self.pixmap.width() * s, self.pixmap.height() * s aw, ah = area.width(), area.height() x = (aw - w) / (2 * s) if aw > w else 0 y = (ah - h) / (2 * s) if ah > h else 0 return QPointF(x, y) def outOfPixmap(self, p): w, h = self.pixmap.width(), self.pixmap.height() return not (0 <= p.x() <= w and 0 <= p.y() <= h) def finalise(self): assert self.current if self.current.points[0] == self.current.points[-1]: # print('finalse') self.current = None self.drawingPolygon.emit(False) self.update() return self.current.close() self.shapes.append(self.current) self.current = None self.setHiding(False) self.newShape.emit() self.update() def closeEnough(self, p1, p2): #d = distance(p1 - p2) #m = (p1-p2).manhattanLength() # print "d %.2f, m %d, %.2f" % (d, m, d - m) return distance(p1 - p2) < self.epsilon # These two, along with a call to adjustSize are required for the # scroll area. def sizeHint(self): return self.minimumSizeHint() def minimumSizeHint(self): if self.pixmap: return self.scale * self.pixmap.size() return super(Canvas, self).minimumSizeHint() def wheelEvent(self, ev): qt_version = 4 if hasattr(ev, "delta") else 5 if qt_version == 4: if ev.orientation() == Qt.Vertical: v_delta = ev.delta() h_delta = 0 else: h_delta = ev.delta() v_delta = 0 else: delta = ev.angleDelta() h_delta = delta.x() v_delta = delta.y() mods = ev.modifiers() if Qt.ControlModifier == int(mods) and v_delta: self.zoomRequest.emit(v_delta) else: v_delta and self.scrollRequest.emit(v_delta, Qt.Vertical) h_delta and self.scrollRequest.emit(h_delta, Qt.Horizontal) ev.accept() def keyPressEvent(self, ev): key = ev.key() shapesBackup = [] shapesBackup = copy.deepcopy(self.shapes) self.shapesBackups.pop() self.shapesBackups.append(shapesBackup) if key == Qt.Key_Escape and self.current: print('ESC press') self.current = None self.drawingPolygon.emit(False) self.update() elif key == Qt.Key_Return and self.canCloseShape(): self.finalise() elif key == Qt.Key_Left and self.selectedShapes: self.moveOnePixel('Left') elif key == Qt.Key_Right and self.selectedShapes: self.moveOnePixel('Right') elif key == Qt.Key_Up and self.selectedShapes: self.moveOnePixel('Up') elif key == Qt.Key_Down and self.selectedShapes: self.moveOnePixel('Down') def moveOnePixel(self, direction): # print(self.selectedShape.points) self.selectCount = len(self.selectedShapes) self.selectCountShape = True for i in range(len(self.selectedShapes)): self.selectedShape = self.selectedShapes[i] if direction == 'Left' and not self.moveOutOfBound(QPointF( -1.0, 0)): # print("move Left one pixel") self.selectedShape.points[0] += QPointF(-1.0, 0) self.selectedShape.points[1] += QPointF(-1.0, 0) self.selectedShape.points[2] += QPointF(-1.0, 0) self.selectedShape.points[3] += QPointF(-1.0, 0) elif direction == 'Right' and not self.moveOutOfBound( QPointF(1.0, 0)): # print("move Right one pixel") self.selectedShape.points[0] += QPointF(1.0, 0) self.selectedShape.points[1] += QPointF(1.0, 0) self.selectedShape.points[2] += QPointF(1.0, 0) self.selectedShape.points[3] += QPointF(1.0, 0) elif direction == 'Up' and not self.moveOutOfBound(QPointF( 0, -1.0)): # print("move Up one pixel") self.selectedShape.points[0] += QPointF(0, -1.0) self.selectedShape.points[1] += QPointF(0, -1.0) self.selectedShape.points[2] += QPointF(0, -1.0) self.selectedShape.points[3] += QPointF(0, -1.0) elif direction == 'Down' and not self.moveOutOfBound( QPointF(0, 1.0)): # print("move Down one pixel") self.selectedShape.points[0] += QPointF(0, 1.0) self.selectedShape.points[1] += QPointF(0, 1.0) self.selectedShape.points[2] += QPointF(0, 1.0) self.selectedShape.points[3] += QPointF(0, 1.0) shapesBackup = [] shapesBackup = copy.deepcopy(self.shapes) self.shapesBackups.append(shapesBackup) self.shapeMoved.emit() self.repaint() def moveOutOfBound(self, step): points = [ p1 + p2 for p1, p2 in zip(self.selectedShape.points, [step] * 4) ] return True in map(self.outOfPixmap, points) def setLastLabel(self, text, line_color=None, fill_color=None): assert text self.shapes[-1].label = text if line_color: self.shapes[-1].line_color = line_color if fill_color: self.shapes[-1].fill_color = fill_color self.storeShapes() return self.shapes[-1] def undoLastLine(self): assert self.shapes self.current = self.shapes.pop() self.current.setOpen() self.line.points = [self.current[-1], self.current[0]] self.drawingPolygon.emit(True) def undoLastPoint(self): if not self.current or self.current.isClosed(): return self.current.popPoint() if len(self.current) > 0: self.line[0] = self.current[-1] else: self.current = None self.drawingPolygon.emit(False) self.repaint() def resetAllLines(self): assert self.shapes self.current = self.shapes.pop() self.current.setOpen() self.line.points = [self.current[-1], self.current[0]] self.drawingPolygon.emit(True) self.current = None self.drawingPolygon.emit(False) self.update() def loadPixmap(self, pixmap): self.pixmap = pixmap self.shapes = [] self.repaint() def loadShapes(self, shapes, replace=True): if replace: self.shapes = list(shapes) else: self.shapes.extend(shapes) self.current = None self.hShape = None self.hVertex = None # self.hEdge = None self.storeShapes() self.repaint() def setShapeVisible(self, shape, value): self.visible[shape] = value self.repaint() def currentCursor(self): cursor = QApplication.overrideCursor() if cursor is not None: cursor = cursor.shape() return cursor def overrideCursor(self, cursor): self._cursor = cursor if self.currentCursor() is None: QApplication.setOverrideCursor(cursor) else: QApplication.changeOverrideCursor(cursor) def restoreCursor(self): QApplication.restoreOverrideCursor() def resetState(self): self.restoreCursor() self.pixmap = None self.update() self.shapesBackups = [] def setDrawingShapeToSquare(self, status): self.drawSquare = status def restoreShape(self): if not self.isShapeRestorable: return self.shapesBackups.pop() # latest shapesBackup = self.shapesBackups.pop() self.shapes = shapesBackup self.selectedShapes = [] for shape in self.shapes: shape.selected = False self.repaint() @property def isShapeRestorable(self): if len(self.shapesBackups) < 2: return False return True
class Canvas(QWidget): zoomRequest = pyqtSignal(int) scrollRequest = pyqtSignal(int, int) newShape = pyqtSignal() selectionChanged = pyqtSignal(bool) shapeMoved = pyqtSignal() drawingPolygon = pyqtSignal(bool) CREATE, EDIT = list(range(2)) epsilon = 11.0 FIXED_WIDTH = 192 FIXED_HEIGHT = 192 def __init__(self, *args, **kwargs): super(Canvas, self).__init__(*args, **kwargs) # Initialise local state. #self.mode = self.EDIT self.shapes = [] self.keeptissue = None self.current = None self.detectShape = None self.selectedShape = None # save the selected shape here self.selectedShapeCopy = None self.drawingLineColor = QColor(0, 0, 255) self.drawingRectColor = QColor(0, 0, 255) #self.line = Shape(line_color=self.drawingLineColor) self.prevPoint = QPointF() self.offsets = QPointF(), QPointF() self.scale = 1.0 self.pixmap = QPixmap() self.visible = {} self._hideBackround = False self.hideBackround = False self.hShape = None self.hVertex = None self._painter = QPainter() self._cursor = CURSOR_DEFAULT self.select_display = 0x01 # Menus: self.menus = (QMenu(), QMenu()) # Set widget options. self.setMouseTracking(True) self.setFocusPolicy(Qt.WheelFocus) self.current = None self.right_click_pos = None self.verified = False self.drawSquare = False self.drawRegion = True # Add by yuan : set drawRegion flag to true self.fileName = 'unknow' self.saveSplitImage = SaveImage.SaveImgeClass(None) def enterEvent(self, ev): self.overrideCursor(self._cursor) def leaveEvent(self, ev): self.restoreCursor() def focusOutEvent(self, ev): self.restoreCursor() def mousePressEvent(self, ev): pos = self.transformPos(ev.pos()) if ev.button() == Qt.LeftButton: self.selectShapePoint(pos) """ if self.drawing(): self.handleDrawing(pos) else: self.selectShapePoint(pos) self.prevPoint = pos self.repaint() """ elif ev.button() == Qt.RightButton: self.right_click_pos = pos #elif ev.button() == Qt.RightButton and self.editing(): # self.selectShapePoint(pos) # self.prevPoint = pos # self.repaint() """ if self.drawRegion: # pos_tmp添加到self.pos_xy中 if ev.button() == Qt.LeftButton: if self.drawing(): self.handleDrawing(pos) #self.pos_xy.append(pos) else: self.selectShapePoint(pos) self.prevPoint = pos self.repaint() elif ev.button() == Qt.RightButton and self.editing(): self.selectShapePoint(pos) self.prevPoint = pos self.repaint() else: if ev.button() == Qt.LeftButton: if self.drawing(): self.handleDrawing(pos) else: self.selectShapePoint(pos) self.prevPoint = pos self.repaint() elif ev.button() == Qt.RightButton and self.editing(): self.selectShapePoint(pos) self.prevPoint = pos self.repaint() """ def mouseReleaseEvent(self, ev): if ev.button() == Qt.RightButton: menu = self.menus[bool(self.selectedShapeCopy)] self.restoreCursor() if not menu.exec_(self.mapToGlobal(ev.pos())) \ and self.selectedShapeCopy: # Cancel the move by deleting the shadow copy. self.selectedShapeCopy = None self.repaint() elif ev.button() == Qt.LeftButton and self.selectedShape: pass """ if self.selectedVertex(): self.overrideCursor(CURSOR_POINT) else: self.overrideCursor(CURSOR_GRAB) """ elif ev.button() == Qt.LeftButton: pass """ pos = self.transformPos(ev.pos()) if self.drawing(): self.handleDrawing(pos) """ """ if self.drawRegion: if ev.button() == Qt.RightButton: menu = self.menus[bool(self.selectedShapeCopy)] self.restoreCursor() if not menu.exec_(self.mapToGlobal(ev.pos())) \ and self.selectedShapeCopy: # Cancel the move by deleting the shadow copy. self.selectedShapeCopy = None self.repaint() elif ev.button() == Qt.LeftButton and self.selectedShape: if self.selectedVertex(): self.overrideCursor(CURSOR_POINT) else: self.overrideCursor(CURSOR_GRAB) elif ev.button() == Qt.LeftButton: pos = self.transformPos(ev.pos()) if self.drawing(): self.handleDrawing(pos) else: if ev.button() == Qt.RightButton: menu = self.menus[bool(self.selectedShapeCopy)] self.restoreCursor() if not menu.exec_(self.mapToGlobal(ev.pos()))\ and self.selectedShapeCopy: # Cancel the move by deleting the shadow copy. self.selectedShapeCopy = None self.repaint() elif ev.button() == Qt.LeftButton and self.selectedShape: if self.selectedVertex(): self.overrideCursor(CURSOR_POINT) else: self.overrideCursor(CURSOR_GRAB) elif ev.button() == Qt.LeftButton: pos = self.transformPos(ev.pos()) if self.drawing(): self.handleDrawing(pos) """ def hideBackroundShapes(self, value): self.hideBackround = value if self.selectedShape: # Only hide other shapes if there is a current selection. # Otherwise the user will not be able to select a shape. self.setHiding(True) self.repaint() def handleDrawing(self, pos): initPos = self.current[0] minX = initPos.x() minY = initPos.y() self.current.clearPoints() orig_minX = minX orig_minY = minY minX = minX - Canvas.FIXED_WIDTH / 2 minY = minY - Canvas.FIXED_HEIGHT / 2 maxX = minX + Canvas.FIXED_WIDTH maxY = minY + Canvas.FIXED_HEIGHT # lef scale if minX < 25: maxX = maxX + (25 - minX) minX = 25 if maxX > 740: minX = minX - (maxX - 740) maxX = 740 # top if minY < 155: maxY = maxY + (155 - minY) minY = 155 # if maxY > 540: minY = minY - (maxY - 540) maxY = 540 if orig_minX <= 378: # left image if minY <= 200 and maxX > 310: # avoid color bar if orig_minY < 200: minX = minX - (maxX - 310) maxX = 310 else: minX = minX - (maxX - 378) maxX = 378 maxY = maxY - (minY - 200) minY = 200 elif maxX > 378: minX = minX - (maxX - 378) maxX = 378 else: # right image if minY <= 200 and maxX > 690: # avoid color bar if orig_minY < 200: minX = minX - (maxX - 690) maxX = 690 else: minX = minX - (maxX - 740) maxX = 740 maxY = maxY - (minY - 200) minY = 200 else: if maxX > 740: minX = minX - (maxX - 740) maxX = 740 if minX < 378: maxX = maxX + (378 - minX) minX = 378 if minY < 170: maxY = maxY + (170 - minY) minY = 170 self.current.addPoint(QPointF(minX, minY)) targetPos = QPointF(maxX, maxY) self.line[1] = targetPos self.current.addPoint(QPointF(maxX, minY)) self.current.addPoint(targetPos) self.current.addPoint(QPointF(minX, maxY)) self.finalise() """ if self.drawRegion: if self.current: #initPos = self.current[0] self.current.addPoint(self.line[1]) self.finalise_region() elif not self.outOfPixmap(pos): self.current = Shape(drawRegion=True) self.current.addPoint(pos) self.line.points = [pos, pos] self.setHiding() self.drawingPolygon.emit(True) self.update() else: if self.current and self.current.reachMaxPoints() is False: initPos = self.current[0] minX = initPos.x() minY = initPos.y() if Canvas.FIXED_HEIGHT == 0 and Canvas.FIXED_WIDTH ==0: targetPos = self.line[1] maxX = targetPos.x() maxY = targetPos.y() else: self.current.clearPoints() orig_minX = minX orig_minY = minY minX = minX - Canvas.FIXED_WIDTH/2 minY = minY - Canvas.FIXED_HEIGHT/2 maxX = minX + Canvas.FIXED_WIDTH maxY = minY + Canvas.FIXED_HEIGHT # lef scale if minX < 25: maxX = maxX + (25 - minX) minX = 25 if maxX > 740: minX = minX - (maxX - 740) maxX = 740 # top if minY < 155: maxY = maxY + (155 - minY) minY = 155 # if maxY > 540: minY = minY - (maxY - 540) maxY = 540 if orig_minX <= 378: # left image if minY <= 200 and maxX > 310: #avoid color bar if orig_minY < 200: minX = minX - (maxX - 310) maxX = 310 else: minX = minX - (maxX - 378) maxX = 378 maxY = maxY - (minY - 200) minY = 200 elif maxX > 378: minX = minX - (maxX - 378) maxX = 378 else: # right image if minY <= 200 and maxX > 690: # avoid color bar if orig_minY < 200: minX = minX - (maxX - 690) maxX = 690 else: minX = minX - (maxX - 740) maxX = 740 maxY = maxY - (minY - 200) minY = 200 else: if maxX > 740: minX = minX - (maxX - 740) maxX = 740 if minX < 378: maxX = maxX + (378 - minX ) minX = 378 if minY < 170: maxY = maxY + (170 - minY) minY = 170 self.current.addPoint(QPointF(minX, minY)) targetPos = QPointF(maxX, maxY) self.line[1] = targetPos self.current.addPoint(QPointF(maxX, minY)) self.current.addPoint(targetPos) self.current.addPoint(QPointF(minX, maxY)) self.finalise() elif not self.outOfPixmap(pos): self.current = Shape() self.current.addPoint(pos) self.line.points = [pos, pos] self.setHiding() self.drawingPolygon.emit(True) self.update() """ """ def setDrawingColor(self, qColor): self.drawingLineColor = qColor self.drawingRectColor = qColor def setHiding(self, enable=True): self._hideBackround = self.hideBackround if enable else False def isVisible(self, shape): return self.visible.get(shape, True) def drawing(self): return self.mode == self.CREATE def editing(self): return self.mode == self.EDIT def setEditing(self, value=True): self.mode = self.EDIT if value else self.CREATE if not value: # Create self.unHighlight() self.deSelectShape() self.prevPoint = QPointF() self.repaint() def unHighlight(self): if self.hShape: self.hShape.highlightClear() self.hVertex = self.hShape = None def selectedVertex(self): return self.hVertex is not None def mouseMoveEvent(self, ev): #Update line with last point and current coordinates. pos = self.transformPos(ev.pos()) # Update coordinates in status bar if image is opened window = self.parent().window() if window.filePath is not None: self.parent().window().labelCoordinates.setText( 'X: %d; Y: %d' % (pos.x(), pos.y())) # Polygon drawing. if self.drawing(): self.overrideCursor(CURSOR_DRAW) if self.current: color = self.drawingLineColor if self.outOfPixmap(pos): # Don't allow the user to draw outside the pixmap. # Project the point to the pixmap's edges. pos = self.intersectionPoint(self.current[-1], pos) elif len(self.current) > 1 and self.closeEnough(pos, self.current[0]): # Attract line to starting point and colorise to alert the # user: pos = self.current[0] color = self.current.line_color self.overrideCursor(CURSOR_POINT) self.current.highlightVertex(0, Shape.NEAR_VERTEX) # add by yuan ==> draw region if self.drawRegion: # pos_tmp添加到self.pos_xy中 # pos_tmp = (pos.x(), pos.y()) if self.current: self.current.addPoint(pos) #self.line[1] = pos else: if self.drawSquare: initPos = self.current[0] minX = initPos.x() minY = initPos.y() min_size = min(abs(pos.x() - minX), abs(pos.y() - minY)) directionX = -1 if pos.x() - minX < 0 else 1 directionY = -1 if pos.y() - minY < 0 else 1 self.line[1] = QPointF(minX + directionX * min_size, minY + directionY * min_size) else: self.line[1] = pos self.line.line_color = color self.prevPoint = QPointF() self.current.highlightClear() else: self.prevPoint = pos self.repaint() return # Polygon copy moving. if Qt.RightButton & ev.buttons(): if self.selectedShapeCopy and self.prevPoint: self.overrideCursor(CURSOR_MOVE) self.boundedMoveShape(self.selectedShapeCopy, pos) self.repaint() elif self.selectedShape: self.selectedShapeCopy = self.selectedShape.copy() self.repaint() return # Polygon/Vertex moving. if Qt.LeftButton & ev.buttons(): if self.selectedVertex(): self.boundedMoveVertex(pos) self.shapeMoved.emit() self.repaint() elif self.selectedShape and self.prevPoint: self.overrideCursor(CURSOR_MOVE) self.boundedMoveShape(self.selectedShape, pos) self.shapeMoved.emit() self.repaint() return # Just hovering over the canvas, 2 posibilities: # - Highlight shapes # - Highlight vertex # Update shape/vertex fill and tooltip value accordingly. self.setToolTip("Image") for shape in reversed([s for s in self.shapes if self.isVisible(s)]): # Look for a nearby vertex to highlight. If that fails, # check if we happen to be inside a shape. index = shape.nearestVertex(pos, self.epsilon) if index is not None: if self.selectedVertex(): self.hShape.highlightClear() self.hVertex, self.hShape = index, shape shape.highlightVertex(index, shape.MOVE_VERTEX) self.overrideCursor(CURSOR_POINT) self.setToolTip("Click & drag to move point") self.setStatusTip(self.toolTip()) self.update() break elif shape.containsPoint(pos): if self.selectedVertex(): self.hShape.highlightClear() self.hVertex, self.hShape = None, shape self.setToolTip( "Click & drag to move shape '%s'" % shape.label) self.setStatusTip(self.toolTip()) self.overrideCursor(CURSOR_GRAB) self.update() break else: # Nothing found, clear highlights, reset state. if self.hShape: self.hShape.highlightClear() self.update() self.hVertex, self.hShape = None, None self.overrideCursor(CURSOR_DEFAULT) def canCloseShape(self): return self.drawing() and self.current and len(self.current) > 2 def endMove(self, copy=False): assert self.selectedShape and self.selectedShapeCopy shape = self.selectedShapeCopy #del shape.fill_color #del shape.line_color if copy: self.shapes.append(shape) self.selectedShape.selected = False self.selectedShape = shape self.repaint() else: self.selectedShape.points = [p for p in shape.points] self.selectedShapeCopy = None def mouseDoubleClickEvent(self, ev): # We need at least 4 points here, since the mousePress handler # adds an extra one before this handler is called. if self.canCloseShape() and len(self.current) > 3: self.current.popPoint() self.finalise() def boundedMoveVertex(self, pos): index, shape = self.hVertex, self.hShape point = shape[index] if self.outOfPixmap(pos): pos = self.intersectionPoint(point, pos) if self.drawSquare: opposite_point_index = (index + 2) % 4 opposite_point = shape[opposite_point_index] min_size = min(abs(pos.x() - opposite_point.x()), abs(pos.y() - opposite_point.y())) directionX = -1 if pos.x() - opposite_point.x() < 0 else 1 directionY = -1 if pos.y() - opposite_point.y() < 0 else 1 shiftPos = QPointF(opposite_point.x() + directionX * min_size - point.x(), opposite_point.y() + directionY * min_size - point.y()) else: shiftPos = pos - point shape.moveVertexBy(index, shiftPos) lindex = (index + 1) % 4 rindex = (index + 3) % 4 lshift = None rshift = None if index % 2 == 0: rshift = QPointF(shiftPos.x(), 0) lshift = QPointF(0, shiftPos.y()) else: lshift = QPointF(shiftPos.x(), 0) rshift = QPointF(0, shiftPos.y()) shape.moveVertexBy(rindex, rshift) shape.moveVertexBy(lindex, lshift) def boundedMoveShape(self, shape, pos): if self.outOfPixmap(pos): return False # No need to move o1 = pos + self.offsets[0] if self.outOfPixmap(o1): pos -= QPointF(min(0, o1.x()), min(0, o1.y())) o2 = pos + self.offsets[1] if self.outOfPixmap(o2): pos += QPointF(min(0, self.pixmap.width() - o2.x()), min(0, self.pixmap.height() - o2.y())) # The next line tracks the new position of the cursor # relative to the shape, but also results in making it # a bit "shaky" when nearing the border and allows it to # go outside of the shape's area for some reason. XXX #self.calculateOffsets(self.selectedShape, pos) dp = pos - self.prevPoint if dp: shape.moveBy(dp) self.prevPoint = pos return True return False """ def setSelectDisplay(self, select_display): self.select_display = select_display for shape in reversed(self.shapes): shape.setSelectedDisplay(select_display) self.selectionChanged.emit(True) self.update() def UnselectedShape(self): for shape in self.shapes: shape.selected = False def focusShape(self, index): # self.drawRegion = shape.drawRegion # add by yuan self.selectedShape = self.shapes[index] # self.setHiding() self.selectionChanged.emit(True) self.update() def selectShape(self, shape, point): self.deSelectShape() if not shape.check_roi(point): shape.selected = True self.selectedShape = shape self.selectionChanged.emit(True) self.update() def selectShapePoint(self, point): """Select the first shape created which contains this point.""" self.deSelectShape() for shape in reversed(self.shapes): # if not shape.drawRegion: if shape.containsPoint(point): self.selectShape(shape, point) #self.calculateOffsets(shape, point) return elif shape.check_roi(point): # add out region point self.selectedShape = shape self.selectionChanged.emit(True) self.update() return """ if self.selectedVertex(): # A vertex is marked for selection. index, shape = self.hVertex, self.hShape shape.highlightVertex(index, shape.MOVE_VERTEX) self.selectShape(shape) return for shape in reversed(self.shapes): # if not shape.drawRegion: if self.isVisible(shape) and shape.containsPoint(point): self.selectShape(shape) self.calculateOffsets(shape, point) return """ def calculateOffsets(self, shape, point): rect = shape.boundingRect() x1 = rect.x() - point.x() y1 = rect.y() - point.y() x2 = (rect.x() + rect.width()) - point.x() y2 = (rect.y() + rect.height()) - point.y() self.offsets = QPointF(x1, y1), QPointF(x2, y2) def deSelectShape(self): if self.selectedShape: self.selectedShape.selected = False self.selectedShape = None #self.setHiding(False) self.selectionChanged.emit(False) self.update() def deleteSelected(self): if self.selectedShape: shape = self.selectedShape self.shapes.remove(self.selectedShape) self.selectedShape = None self.update() return shape def copySelectedShape(self): if self.selectedShape: shape = self.selectedShape.copy() self.deSelectShape() self.shapes.append(shape) shape.selected = True self.selectedShape = shape self.boundedShiftShape(shape) return shape def boundedShiftShape(self, shape): # Try to move in one direction, and if it fails in another. # Give up if both fail. point = shape[0] offset = QPointF(2.0, 2.0) self.calculateOffsets(shape, point) self.prevPoint = point if not self.boundedMoveShape(shape, point - offset): self.boundedMoveShape(shape, point + offset) def paintEvent(self, event): if not self.pixmap: return super(Canvas, self).paintEvent(event) p = self._painter p.begin(self) p.setRenderHint(QPainter.Antialiasing) p.setRenderHint(QPainter.HighQualityAntialiasing) p.setRenderHint(QPainter.SmoothPixmapTransform) p.scale(self.scale, self.scale) p.translate(self.offsetToCenter()) p.drawPixmap(0, 0, self.pixmap) Shape.scale = self.scale print('paint-shape') for shape in self.shapes: #if (shape.selected or not self._hideBackround) and self.isVisible(shape): # shape.fill = shape.selected or shape == self.hShape shape.paint(p) if self.current is not None: self.current.paint(p) #self.line.paint(p) p.end() return def transformPos(self, point): """Convert from widget-logical coordinates to painter-logical coordinates.""" return point / self.scale - self.offsetToCenter() def offsetToCenter(self): s = self.scale area = super(Canvas, self).size() w, h = self.pixmap.width() * s, self.pixmap.height() * s aw, ah = area.width(), area.height() x = (aw - w) / (2 * s) if aw > w else 0 y = (ah - h) / (2 * s) if ah > h else 0 return QPointF(x, y) def outOfPixmap(self, p): w, h = self.pixmap.width(), self.pixmap.height() return not (0 <= p.x() <= w and 0 <= p.y() <= h) # add by yuan def finalise_region(self): assert self.current # first equlas last is closed region if len(self.current.points) >= 10: if self.current.points[0] == self.current.points[-1]: self.current.close() self.shapes.append(self.current) self.current = None self.setHiding(False) self.drawingPolygon.emit(False) self.newShape.emit() # notify to create labedialog self.update() def finalise(self): assert self.current # first equlas last is closed region if self.current.points[0] == self.current.points[-1]: self.current = None self.drawingPolygon.emit(False) self.update() return self.current.close() self.shapes.append(self.current) self.current = None self.setHiding(False) self.newShape.emit() # notify to create labedialog self.update() def closeEnough(self, p1, p2): #d = distance(p1 - p2) #m = (p1-p2).manhattanLength() # print "d %.2f, m %d, %.2f" % (d, m, d - m) return distance(p1 - p2) < self.epsilon def intersectionPoint(self, p1, p2): # Cycle through each image edge in clockwise fashion, # and find the one intersecting the current line segment. # http://paulbourke.net/geometry/lineline2d/ size = self.pixmap.size() points = [(0, 0), (size.width(), 0), (size.width(), size.height()), (0, size.height())] x1, y1 = p1.x(), p1.y() x2, y2 = p2.x(), p2.y() d, i, (x, y) = min(self.intersectingEdges((x1, y1), (x2, y2), points)) x3, y3 = points[i] x4, y4 = points[(i + 1) % 4] if (x, y) == (x1, y1): # Handle cases where previous point is on one of the edges. if x3 == x4: return QPointF(x3, min(max(0, y2), max(y3, y4))) else: # y3 == y4 return QPointF(min(max(0, x2), max(x3, x4)), y3) return QPointF(x, y) def intersectingEdges(self, x1y1, x2y2, points): """For each edge formed by `points', yield the intersection with the line segment `(x1,y1) - (x2,y2)`, if it exists. Also return the distance of `(x2,y2)' to the middle of the edge along with its index, so that the one closest can be chosen.""" x1, y1 = x1y1 x2, y2 = x2y2 for i in range(4): x3, y3 = points[i] x4, y4 = points[(i + 1) % 4] denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) if denom == 0: # This covers two cases: # nua == nub == 0: Coincident # otherwise: Parallel continue ua, ub = nua / denom, nub / denom if 0 <= ua <= 1 and 0 <= ub <= 1: x = x1 + ua * (x2 - x1) y = y1 + ua * (y2 - y1) m = QPointF((x3 + x4) / 2, (y3 + y4) / 2) d = distance(m - QPointF(x2, y2)) yield d, i, (x, y) # add by yuan def covert2Rectangle(self, points): xmin = float('inf') ymin = float('inf') xmax = float('-inf') ymax = float('-inf') for p in points: x = p.x() y = p.y() xmin = min(x, xmin) ymin = min(y, ymin) xmax = max(x, xmax) ymax = max(y, ymax) # Martin Kersner, 2015/11/12 # 0-valued coordinates of BB caused an error while # training faster-rcnn object detector. if xmin < 1: xmin = 1 if ymin < 1: ymin = 1 width = int(xmax - xmin) if width < 1: width = 1 height = int(ymax - ymin) if height < 1: height = 1 return (int(xmin), int(ymin), width, height) def storePoint(self, points): xmin = float('inf') ymin = float('inf') xmax = float('-inf') ymax = float('-inf') for p in points: x = p.x() y = p.y() xmin = min(x, xmin) ymin = min(y, ymin) xmax = max(x, xmax) ymax = max(y, ymax) # Martin Kersner, 2015/11/12 # 0-valued coordinates of BB caused an error while # training faster-rcnn object detector. if xmin < 1: xmin = 1 if ymin < 1: ymin = 1 return (int(xmin), int(ymin), int(xmax), int(ymax)) # add by yuan def saveSplitImage(self, shape, split_img, ismask, image_side, serial_num): print("begin - saveSplitImage") label_name = 'unknow' if not shape.label is None: label_name = str(shape.label) #self.fileName if ismask: img_file = '{}_{}_{}_{}_mask.jpg'.format(label_name, self.fileName, image_side, serial_num) else: img_file = '{}_{}_{}_{}.jpg'.format(label_name, self.fileName, image_side, serial_num) img_path = path.abspath('breast dataset/train dataset') if not path.exists(img_path): mkdir(img_path) split_img.save(path.join(img_path, img_file), 'JPG', 100) print("end - saveSplitImage ") def saveSplitHSImage(self, splitimg, serial_num): print("begin - saveSplitHSImage") # self.fileName img_file = '{}.jpg'.format(serial_num) hsimg_file = '{}_HS.jpg'.format(serial_num) afimg_file = '{}_after.jpg'.format(serial_num) beimg_file = '{}_before.jpg'.format(serial_num) img_path = path.abspath('breast dataset/train HSdataset') if not path.exists(img_path): mkdir(img_path) # print (splitimg) min_gray = np.min(splitimg) max_gray = np.max(splitimg) # print (img[0][:10]) print('min:', min_gray, 'max:', max_gray) x_axis = list(range(0, 255)) # y_axis = splitimg.flatten().tolist() plt.figure() plt.hist(splitimg.flatten().tolist(), bins=x_axis) plt.title('Original Histogram - Min:{} Max:{}'.format( min_gray, max_gray)) plt.savefig(path.join(img_path, beimg_file)) # splitimg.save(path.join(img_path, img_file), 'JPG', 100) cv2.imwrite(path.join(img_path, img_file), splitimg, [cv2.IMWRITE_JPEG_QUALITY, 100]) scale = 255 / (max_gray - min_gray) print(scale) trasfor_img = (splitimg - min_gray) splitimg = trasfor_img * scale splitimg = splitimg.astype(int) cv2.imwrite(path.join(img_path, hsimg_file), splitimg, [cv2.IMWRITE_JPEG_QUALITY, 100]) min_gray = np.min(splitimg) max_gray = np.max(splitimg) print('min:', min_gray, 'max:', max_gray) plt.figure() plt.hist(splitimg.flatten().tolist(), bins=x_axis) plt.title('Histogram Stretching - Min:{} Max:{}'.format( min_gray, max_gray)) plt.savefig(path.join(img_path, afimg_file)) print("end - saveHSSplitImage ") def saveSelectedShape(self, split_img): print('begin - saveSelectedShape') #select_shape = self.selectedShape # the tiles are known to divide evenly # width = self.pixmap.width() height = self.pixmap.height() str_time = strftime("%m%d%H%M%S", gmtime()) #self.saveSplitHSImage(split_img, str_time) self.saveSplitImage.saveSplitImage(split_img, str_time) print("end - findContainRegion") # add by yuan def findFitRectangleShape(self, rectangle): min_rectx = 0 min_recty = 0 max_rectx = float('inf') max_recty = float('inf') isfindrect = False for shape in self.shapes: if not shape.drawRegion: rect = self.storePoint(shape.points) if rect[0] <= rectangle[0] and rect[1] <= rectangle[1]: if rect[2] >= rectangle[2] and rect[3] >= rectangle[3]: if rect[0] >= min_rectx and rect[1] >= min_recty: if rect[2] <= max_rectx and rect[3] <= max_recty: min_rectx = rect[0] min_recty = rect[1] max_rectx = rect[2] max_recty = rect[3] isfindrect = True if isfindrect: rect_x = min_rectx rect_y = min_recty rect_width = int(max_rectx - min_rectx) rect_height = int(max_recty - min_recty) else: rect_x = rectangle[0] - 5 if rect_x <= 0: rect_x = 1 rect_y = rectangle[1] - 5 if rect_y <= 0: rect_y = 1 rect_width = int(rectangle[2] - rectangle[0]) + 10 rect_height = int(rectangle[3] - rectangle[1]) + 10 return (rect_x, rect_y, rect_width, rect_height) def saveRegionByOutsideRectangle(self, shape, rectangle, str_time): print('begin - ') pix = QPixmap(self.pixmap.width(), self.pixmap.height()) pp = QPainter() pp.begin(pix) pp.setPen(QPen(QColor(0, 0, 0))) pp.setBrush(QBrush(QColor(255, 255, 255))) polygon = QPolygonF() for point in shape: polygon.append(QPointF(point.x(), point.y())) print('draw polygon') pp.drawPolygon(polygon) pp.end() print('copy image') pix = pix.copy(rectangle[0], rectangle[1], rectangle[2], rectangle[3]) print('save image') imgside = 'right' if rectangle[0] < 380: imgside = 'left' self.saveSplitImage(shape, pix, True, imgside, str_time) print('end - ') # add by yuan => save mask by rectangle shape def findContainRegion(self, shape, rectangle, str_time): pix = QPixmap(self.pixmap.width(), self.pixmap.height()) #size = pix.size() pp = QPainter() pp.begin(pix) pp.setPen(QPen(QColor(0, 0, 0))) pp.setBrush(QBrush(QColor(255, 255, 255))) polygon = QPolygonF() for s in self.shapes: if s.drawRegion: polygon.clear() for point in s: polygon.append(QPointF(point.x(), point.y())) pp.drawPolygon(polygon) pp.end() pix = pix.copy(rectangle[0], rectangle[1], rectangle[2], rectangle[3]) imgside = 'right' if rectangle[0] < 380: imgside = 'left' self.saveSplitImage(shape, pix, True, imgside, str_time) # These two, along with a call to adjustSize are required for the # scroll area. def sizeHint(self): return self.minimumSizeHint() def minimumSizeHint(self): if self.pixmap: return self.scale * self.pixmap.size() return super(Canvas, self).minimumSizeHint() def wheelEvent(self, ev): qt_version = 4 if hasattr(ev, "delta") else 5 if qt_version == 4: if ev.orientation() == Qt.Vertical: v_delta = ev.delta() h_delta = 0 else: h_delta = ev.delta() v_delta = 0 else: delta = ev.angleDelta() h_delta = delta.x() v_delta = delta.y() mods = ev.modifiers() if Qt.ControlModifier == int(mods) and v_delta: self.zoomRequest.emit(v_delta) else: v_delta and self.scrollRequest.emit(v_delta, Qt.Vertical) h_delta and self.scrollRequest.emit(h_delta, Qt.Horizontal) ev.accept() def keyPressEvent(self, ev): key = ev.key() if key == Qt.Key_Escape and self.current: print('ESC press') self.current = None self.drawingPolygon.emit(False) self.update() elif key == Qt.Key_Return and self.canCloseShape(): self.finalise() elif key == Qt.Key_Left and self.selectedShape: self.moveOnePixel('Left') elif key == Qt.Key_Right and self.selectedShape: self.moveOnePixel('Right') elif key == Qt.Key_Up and self.selectedShape: self.moveOnePixel('Up') elif key == Qt.Key_Down and self.selectedShape: self.moveOnePixel('Down') def moveOnePixel(self, direction): # print(self.selectedShape.points) if direction == 'Left' and not self.moveOutOfBound(QPointF(-1.0, 0)): # print("move Left one pixel") self.selectedShape.points[0] += QPointF(-1.0, 0) self.selectedShape.points[1] += QPointF(-1.0, 0) self.selectedShape.points[2] += QPointF(-1.0, 0) self.selectedShape.points[3] += QPointF(-1.0, 0) elif direction == 'Right' and not self.moveOutOfBound(QPointF(1.0, 0)): # print("move Right one pixel") self.selectedShape.points[0] += QPointF(1.0, 0) self.selectedShape.points[1] += QPointF(1.0, 0) self.selectedShape.points[2] += QPointF(1.0, 0) self.selectedShape.points[3] += QPointF(1.0, 0) elif direction == 'Up' and not self.moveOutOfBound(QPointF(0, -1.0)): # print("move Up one pixel") self.selectedShape.points[0] += QPointF(0, -1.0) self.selectedShape.points[1] += QPointF(0, -1.0) self.selectedShape.points[2] += QPointF(0, -1.0) self.selectedShape.points[3] += QPointF(0, -1.0) elif direction == 'Down' and not self.moveOutOfBound(QPointF(0, 1.0)): # print("move Down one pixel") self.selectedShape.points[0] += QPointF(0, 1.0) self.selectedShape.points[1] += QPointF(0, 1.0) self.selectedShape.points[2] += QPointF(0, 1.0) self.selectedShape.points[3] += QPointF(0, 1.0) self.shapeMoved.emit() self.repaint() def moveOutOfBound(self, step): points = [ p1 + p2 for p1, p2 in zip(self.selectedShape.points, [step] * 4) ] return True in map(self.outOfPixmap, points) def setLastLabel(self, text, line_color=None, fill_color=None): assert text self.shapes[-1].label = text if line_color: self.shapes[-1].line_color = line_color if fill_color: self.shapes[-1].fill_color = fill_color return self.shapes[-1] def loadPixmap(self, pixmap): self.pixmap = pixmap self.shapes = [] self.repaint() def loadShapes(self, shapes): self.shapes = list(shapes) self.current = None self.repaint() def setShapeVisible(self, shape, value): self.visible[shape] = value self.repaint() def currentCursor(self): cursor = QApplication.overrideCursor() if cursor is not None: cursor = cursor.shape() return cursor def overrideCursor(self, cursor): self._cursor = cursor if self.currentCursor() is None: QApplication.setOverrideCursor(cursor) else: QApplication.changeOverrideCursor(cursor) def restoreCursor(self): QApplication.restoreOverrideCursor() def resetState(self): self.restoreCursor() self.pixmap = None self.update() def setDrawingShapeToSquare(self, status): self.drawSquare = status def setDrawRegion(self, status): self.drawRegion = status # ai medicine function def capture_predict_area(self): if self.right_click_pos is None: return None #self.drawRegion = False # create detect rectangle self.current = Shape() minX = self.right_click_pos.x() minY = self.right_click_pos.y() orig_minX = minX orig_minY = minY minX = minX - Canvas.FIXED_WIDTH / 2 minY = minY - Canvas.FIXED_HEIGHT / 2 maxX = minX + Canvas.FIXED_WIDTH maxY = minY + Canvas.FIXED_HEIGHT # lef scale if minX < 25: maxX = maxX + (25 - minX) minX = 25 if maxX > 740: minX = minX - (maxX - 740) maxX = 740 # top if minY < 155: maxY = maxY + (155 - minY) minY = 155 # if maxY > 540: minY = minY - (maxY - 540) maxY = 540 if orig_minX <= 378: # left image if minY <= 200 and maxX > 310: # avoid color bar if orig_minY < 200: minX = minX - (maxX - 310) maxX = 310 else: minX = minX - (maxX - 378) maxX = 378 maxY = maxY - (minY - 200) minY = 200 elif maxX > 378: minX = minX - (maxX - 378) maxX = 378 else: # right image if minY <= 200 and maxX > 690: # avoid color bar if orig_minY < 200: minX = minX - (maxX - 690) maxX = 690 else: minX = minX - (maxX - 740) maxX = 740 maxY = maxY - (minY - 200) minY = 200 else: if maxX > 740: minX = minX - (maxX - 740) maxX = 740 if minX < 378: maxX = maxX + (378 - minX) minX = 378 if minY < 170: maxY = maxY + (170 - minY) minY = 170 self.current.addPoint(QPointF(minX, minY)) targetPos = QPointF(maxX, maxY) # self.line[1] = targetPos self.current.addPoint(QPointF(maxX, minY)) self.current.addPoint(targetPos) self.current.addPoint(QPointF(minX, maxY)) self.current.close() self.repaint() self.keeptissue = self.current.copy() rect = self.covert2Rectangle(self.current.points) split_img = self.pixmap.copy(rect[0], rect[1], rect[2], rect[3]).toImage() split_img = split_img.convertToFormat(QImage.Format_Grayscale8) split_img = qp.qimageview(split_img) split_img = np.stack((split_img, ) * 3, axis=-1) return split_img def get_check_point(self): return (self.right_click_pos.x(), self.right_click_pos.y()) def get_rect_point(self): rect = self.covert2Rectangle(self.current.points) return [(rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3])] def set_tumor_area(self, tumor_area): if self.detectShape is not None: self.detectShape.set_tumor_area(tumor_area) def set_detect_shape(self, bi_rads, relative_point, shape_points): # self.selectedShape = self.current # self.deleteSelected() print('relative pt : ', relative_point) self.detectShape = DetectedShape(label=bi_rads, selectdisplay=self.select_display) #print(len(shape_points)) #shape_points.sort(key=lambda tup: tup[0]) print('shape pt len :', len(shape_points)) for sp in shape_points: pt_list = [] for pt in sp: pt_list.append( QPointF(relative_point[0] + pt[0], relative_point[1] + pt[1])) #self.detectShape.addPoint(QPointF(relative_point[0] + pt[0], relative_point[1] + pt[1])) self.detectShape.addPoint(pt_list) for sp in self.shapes: sp.selected = False self.shapes.append(self.detectShape) # test to keep self.selectedShape = self.detectShape # self.setHiding() #self.current = None #self.setHiding(False) #self.drawingPolygon.emit(False) print('shape count:', len(self.shapes)) self.update() def set_axis_information(self, relative_point, axis_points, horizontal_len, vertical_len): if self.detectShape is not None: self.detectShape.set_Horizontal_Line(relative_point, axis_points, horizontal_len) self.detectShape.set_Vertical_Line(relative_point, axis_points, vertical_len) def set_detected_rectangle(self, relative_point, detected_rectangle): if self.detectShape is not None: self.detectShape.set_Detected_Rectangle(relative_point, detected_rectangle) self.detectShape.set_tissue_rect(self.keeptissue) def get_selected_shape_information(self): if self.selectedShape is not None: return self.selectedShape.get_selected_shape_information() return None def clear_detect_rect(self): self.current = None self.detectShape = None self.update() def findRectanglePointforshape(self): rect_list = [] for sh in self.shapes: rect = self.covert2Rectangle(sh.get_select_rect()) rect_list.append( (rect[0], rect[1], rect[0] + rect[2], rect[1] + rect[3])) return rect_list def getImageArray(self): return qp.qimageview(self.pixmap.toImage()) def get_tissue_rect_list(self): pt_list = [] for sp in self.shapes: rect = self.covert2Rectangle(sp.tissue_rect) pt_list.append( [rect[0], rect[1], rect[0] + rect[2], rect[1] + rect[3]]) # self.detectShape.addPoint(QPointF(relative_point[0] + pt[0], relative_point[1] + pt[1])) return pt_list def get_shape_property(self): property_list = [] for sp in self.shapes: property_list.append(sp.get_property()) return property_list def get_axis_property(self): property_list = [] for sp in self.shapes: property_list.append(sp.get_roi_long_short_axis()) return property_list def get_shapes_points(self): shapes_points = [] for sp in self.shapes: x_list, y_list = sp.get_shape_points() shapes_points.append([x_list, y_list]) return shapes_points