def diagonal(rect: Rectangle, to: tuple, step: float = .1, interval: float = .001): end = to[0] if to[0] < rect.get_x(): step *= -1 end += step else: end += step x_range = np.arange(rect.get_x(), end, step) slope = (to[1] - rect.get_y()) / (to[0] - rect.get_x()) start_y = rect.get_y() start_x = rect.get_x() figure = rect.figure for x in x_range: rect.set_xy((x, slope * (x - start_x) + start_y)) figure.canvas.draw() figure.canvas.flush_events() plt.pause(interval)
class click_xrange: '''An interactive xrange selector. Given an axis and a starting x0 location, draw a full-height rectange that follows the mouise. Similar to click_window, but more appropriate for selecting out an x-range.''' def __init__(self, ax, x0): self.ax = ax self.x0 = x0 y0,y1 = ax.get_ybound() self.rect = Rectangle((x0,y0), width=0, height=(y1-y0), alpha=0.1) ax.add_artist(self.rect) def connect(self): self.cidmotion = self.rect.figure.canvas.mpl_connect( 'motion_notify_event', self.on_motion) def on_motion(self, event): # Have we left the axes? if event.inaxes != self.rect.axes: return self.rect.set_width(event.xdata - self.x0) self.ax.figure.canvas.draw() def close(self): self.rect.figure.canvas.mpl_disconnect(self.cidmotion) self.rect.remove() self.ax.figure.canvas.draw() return(self.x0, self.rect.get_x()+self.rect.get_width())
class click_xrange: '''An interactive xrange selector. Given an axis and a starting x0 location, draw a full-height rectange that follows the mouise. Similar to click_window, but more appropriate for selecting out an x-range.''' def __init__(self, ax, x0): self.ax = ax self.x0 = x0 y0, y1 = ax.get_ybound() self.rect = Rectangle((x0, y0), width=0, height=(y1 - y0), alpha=0.1) ax.add_artist(self.rect) def connect(self): self.cidmotion = self.rect.figure.canvas.mpl_connect( 'motion_notify_event', self.on_motion) def on_motion(self, event): # Have we left the axes? if event.inaxes != self.rect.axes: return self.rect.set_width(event.xdata - self.x0) self.ax.figure.canvas.draw() def close(self): self.rect.figure.canvas.mpl_disconnect(self.cidmotion) self.rect.remove() self.ax.figure.canvas.draw() return (self.x0, self.rect.get_x() + self.rect.get_width())
def show_image(image, labels): rect = Rectangle((labels[0], labels[1]), labels[2] - labels[0], labels[3] - labels[1], edgecolor='r', fill=False) plt.imshow(image) gca = plt.gca() gca.add_patch(rect) return rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()
def right_left(rect: Rectangle, to: tuple, step: float = .1, interval: float = .001): new_step = step if to[0] < rect.get_x(): new_step *= -1 figure = rect.figure right = rect.get_x() < to[0] while (rect.get_x() - to[0] <= step / 2) if right else ( rect.get_x() - to[0] >= step / 2): rect.set_x(rect.get_x() + new_step) figure.canvas.draw() figure.canvas.flush_events() plt.pause(interval)
class ChipSelector(object): def __init__(self, image_name, chip, **kwargs): plt.ioff() self.chip = chip fig, ax = plt.subplots() im, extent = create_image_for_viewer(image_name, chip) plt.imshow(np.flipud(im), extent=extent) xy = (chip['coordinates']['xmin'], chip['coordinates']['ymin']) width = chip['coordinates']['xmax'] - chip['coordinates']['xmin'] height = chip['coordinates']['ymax'] - chip['coordinates']['ymin'] from matplotlib.patches import Rectangle self.__rect = Rectangle(xy, width, height, fill=False, color='r') ax.add_patch(self.__rect) drr = DraggableResizeableRectangle(self.__rect, fixed_aspect_ratio=False) drr.connect() plt.subplots_adjust(bottom=0.2) ax.axes.get_xaxis().set_visible(False) ax.axes.get_yaxis().set_visible(False) ax_reject = plt.axes([0.2, 0.05, 0.1, 0.075]) ax_accept = plt.axes([0.75, 0.05, 0.1, 0.075]) self._b_reject = Button(ax_reject, 'Reject') self._b_accept = Button(ax_accept, "Accept") self._b_reject.on_clicked(self.answer) self._b_accept.on_clicked(self.answer) plt.show() def answer(self, event): if event.inaxes.texts[0]._text == 'Accept': self.chip = {'name': self.chip['name'], 'coordinates': {'xmin': self.__rect.get_x(), 'ymin': self.__rect.get_y(), 'xmax': self.__rect.get_x()+self.__rect.get_width(), 'ymax': self.__rect.get_y()+self.__rect.get_height()} } else: self.chip = None plt.close()
def draw_label(self, bar: Rectangle, fmt: str) -> plt.Annotation: w, h = bar.get_width(), bar.get_height() x = bar.get_x() + w * 0.5 y = bar.get_y() + h label = self._ax.annotate(format(h, fmt), (x, y), xytext=(0.0, 4.0), textcoords='offset points', ha='center', va='bottom') label.draggable() label.set_rotation(self.label_rot) return label
def move_to(rect: Rectangle, to: tuple, step: float = .1, interval: float = .001, show_animation: bool = True): if show_animation: if abs(rect.get_x() - to[0]) < .001: if not abs(rect.get_y() - to[1]) < .001: up_down(rect, to, step, interval) else: return elif abs(rect.get_y() - to[1]) < .001: right_left(rect, to, step, interval) else: diagonal(rect, to, step, interval) figure = rect.figure rect.set_xy(to) figure.canvas.draw() figure.canvas.flush_events() plt.pause(interval)
class ZoneInteret: @staticmethod def verifier_presence_fichier_ini(): return os.path.isfile('./zi/param.ini') @staticmethod def supprimer_ZI(window): if os.path.isfile('./zi/param.ini'): try: os.remove("./zi/param.ini") os.remove("./zi/image_modele.png") os.remove("./zi/image_zone_interet.png") QMessageBox.information( window, "Information", "Supprimer la Zone d'intérêt avec succès", QMessageBox.Ok) except OSError: QMessageBox.warning( window, "Erreur", "Impossible de supprimer les fichiers dans le repertoire /zi", QMessageBox.Ok) else: QMessageBox.warning( window, "Erreur", "Impossible de trouver les fichiers dans le repertoire /zi", QMessageBox.Ok) # Initialise les variables nécessaires à l'affichage de l'image et aux événements def __init__(self, video): self.flag = False self.get_one_image_from_video(video) # On se sert de l'image extraite précédemment self.img = mpimg.imread('./zi/image_modele.png') # On initialise le titre de la fenêtre fig = plt.figure(1) fig.canvas.set_window_title("Zone Interet") # On récupère les infos des axes self.ax = plt.gca() # On initialise le futur rectangle dessiné (non rempli aux bordures rouges) self.rect = Rectangle((0, 0), 1, 1, fill=False, edgecolor="red") # Initialisation des points du rectangle self.x0 = None self.y0 = None self.x1 = None self.y1 = None self.ax.add_patch(self.rect) # Liaison des événements self.ax.figure.canvas.mpl_connect('button_press_event', self.on_mouseclick_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_mouseclick_release) self.ax.figure.canvas.mpl_connect('key_press_event', self.on_keyboard_press) # Affichage de l'image dans la fenêtre self.imgplot = plt.imshow(self.img) self.show_window() # Un click gauche -> sauvegarde des coordonnées du pointeur def on_mouseclick_press(self, event): self.x0 = event.xdata self.y0 = event.ydata # Click gauche relâché -> dessin du rectangle def on_mouseclick_release(self, event): self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.ax.figure.canvas.draw() # Si la touche "enter" est appuyée, on sauvegarde la zone d'intérêt def on_keyboard_press(self, event): if event.key == 'enter': self.flag = True with open("./zi/param.ini", "w") as file: file.write(str(int(self.rect.get_x())) + ",") file.write(str(int(self.rect.get_y())) + ",") file.write(str(int(self.rect.get_width())) + ",") file.write(str(int(self.rect.get_height()))) # On cache les axes avant d'enregistrer l'image modele avec la zone d'interet self.ax.get_xaxis().set_visible(False) self.ax.get_yaxis().set_visible(False) plt.title("Zone interet") plt.savefig("./zi/image_zone_interet.png") plt.close() def show_window(self): plt.title( "Selectionner la zone interet avec la souris. Appuyez sur entrer pour valider." ) plt.show() # extrait une image de la vidéo selectionnée def get_one_image_from_video(self, video): video_capture = cv2.VideoCapture(video) # TODO : bien récupérer la dernière frame nb_frame = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) video_capture.set(cv2.CAP_PROP_FRAME_COUNT, int(nb_frame - 1)) success, self.image = video_capture.read() print(success) cv2.imwrite("zi/image_modele.png", self.image)
class WindowSelectionRectangle(object): def __init__(self, event, axis, on_window_selection_callback): self.axis = axis if event.inaxes != self.axis: return # Store the axes it has been initialized in. self.axes = event.inaxes ymin, ymax = self.axes.get_ylim() self.min_x = event.xdata self.intial_selection_active = True self.rect = Rectangle((event.xdata, ymin), 0, ymax - ymin, color="0.3", alpha=0.5, edgecolor="0.5") self.axes.add_patch(self.rect) # Get the canvas. self.canvas = self.rect.figure.canvas # Use blittig for fast animations. self.rect.set_animated(True) self.background = self.canvas.copy_from_bbox(self.rect.axes.bbox) self._connect() self.on_window_selection_callback = on_window_selection_callback #def __del__(self): #""" #Disconnect the events upon deallocating. #""" #self.canvas.mpl_disconnect(self.conn_button_press) #self.canvas.mpl_disconnect(self.conn_button_release) #self.canvas.mpl_disconnect(self.conn_mouse_motion) def _connect(self): """ Connect to the necessary events. """ self.conn_button_press = self.rect.figure.canvas.mpl_connect( 'button_press_event', self.on_button_press) self.conn_button_release = self.rect.figure.canvas.mpl_connect( 'button_release_event', self.on_button_release) self.conn_mouse_motion = self.rect.figure.canvas.mpl_connect( 'motion_notify_event', self.on_mouse_motion) def on_button_press(self, event): pass def on_button_release(self, event): if event.inaxes != self.axis: return if event.button != 1: return # turn off the rect animation property and reset the background self.rect.set_animated(False) self.background = None self.intial_selection_active = False self.canvas.draw() x = self.rect.get_x() width = self.rect.get_width() if width < 0: x = x + width width = abs(width) self.on_window_selection_callback(x, width, self.axis) def on_mouse_motion(self, event): if event.button != 1 or \ self.intial_selection_active is not True: return if event.xdata is not None: self.rect.set_width(event.xdata - self.min_x) # restore the background region self.canvas.restore_region(self.background) # redraw just the current rectangle self.axes.draw_artist(self.rect) # blit just the redrawn area self.canvas.blit(self.axes.bbox)
class RectangleCreationOnClick(PanOnClick): """Class providing rectangle creation patch to a matplotlib Figure. Left button to create a rectangle with the desired width. """ def __init__(self, figure=None, scale_factor=1.1): """Initializer :param Figure figure: The matplotlib figure to attach the behavior to. :param float scale_factor: The scale factor to apply on wheel event. """ super(RectangleCreationOnClick, self).__init__(figure, scale_factor=1.1) self._add_connection_rectangle_creation('button_press_event', self._on_mouse_press_creation) self._add_connection_rectangle_creation( 'button_release_event', self._on_mouse_release_creation) self._add_connection_rectangle_creation('motion_notify_event', self._on_mouse_motion_creation) self._pressed_button = None # To store active button self._axes = None # To store x and y axes concerned by interaction self._event = None # To store reference event during interaction def update_current_rectangle(self, ax, curr_event, past_event): """Create the rectangle with the width stablished from initial click and current position of the mouse :param matplotlib.axes._subplots.AxesSubplot ax: axis where the rectangle is gonna be drawed or it's width modified :param matplotlib.backend_bases.MouseEvent curr_event: data from the current position of the mouse of the motion event :param matplotlib.backend_bases.MouseEvent past_event: data from the initial position where a click has been done """ try: #If current click does not goes to the limit (which would cause to return None) if curr_event.xdata != None: yAxis_limits = self._rectangle_yAxis_limits #If rectangle creation goes from left to right if curr_event.xdata < past_event.xdata: #If the rectangle has not been created, create it if not isinstance(self._rectangle_interactions, Rectangle): self._rectangle_interactions = Rectangle( [curr_event.xdata, yAxis_limits[0] * 0.9], abs(past_event.xdata - curr_event.xdata), yAxis_limits[1] * 1.1, color='black', alpha=0.3) self._rectangleStats.direction = -1 ax.add_patch(self._rectangle_interactions) #If it has been created and goes from left to right, update width elif isinstance(self._rectangle_interactions, Rectangle ) and self._rectangleStats.direction == -1: self._rectangle_interactions.set_height( abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1)) self._rectangle_interactions.set_y(yAxis_limits[0] * 0.9) self._rectangle_interactions.set_x(curr_event.xdata) self._rectangle_interactions.set_width( abs(past_event.xdata - curr_event.xdata)) #If it has been created but changed direction from right-left to left-right, update width elif self._rectangleStats.direction == 1: self._rectangle_interactions.set_height( abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1)) self._rectangle_interactions.set_y(yAxis_limits[0] * 0.9) self._rectangleStats.direction = -1 self._rectangle_interactions.set_x(curr_event.xdata) self._rectangle_interactions.set_width( abs(past_event.xdata - curr_event.xdata)) #If rectangle creation goes from right to left else: #If the rectangle has not been created, create it if not isinstance(self._rectangle_interactions, Rectangle): self._rectangle_interactions = Rectangle( [past_event.xdata, yAxis_limits[0] * 0.9], abs(past_event.xdata - curr_event.xdata), yAxis_limits[1] * 1.1, color='black', alpha=0.3) self._rectangleStats.direction = 1 ax.add_patch(self._rectangle_interactions) #If it has been created and goes from left to right, update width elif isinstance( self._rectangle_interactions, Rectangle) and self._rectangleStats.direction == 1: self._rectangle_interactions.set_height( abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1)) self._rectangle_interactions.set_y(yAxis_limits[0] * 0.9) self._rectangle_interactions.set_x(past_event.xdata) self._rectangle_interactions.set_width( abs(past_event.xdata - curr_event.xdata)) #If it has been created but changed direction from right-left to left-right, update width elif self._rectangleStats.direction == -1: self._rectangle_interactions.set_height( abs(yAxis_limits[0] * 0.9 - yAxis_limits[1] * 1.1)) self._rectangle_interactions.set_y(yAxis_limits[0] * 0.9) self._rectangleStats.direction = 1 self._rectangle_interactions.set_x(past_event.xdata) self._rectangle_interactions.set_width( abs(past_event.xdata - curr_event.xdata)) except Exception as e: pass def _rectangle_creation(self, event): """Execute function based on the name of it""" if event.name == 'button_press_event': # begin creation self._original_event = event self._event = event elif event.name == 'button_release_event': # end creation self._event = None pub.sendMessage('rangeData', iw=self._rectangle_interactions.get_x(), ew=self._rectangle_interactions.get_bbox().x1) elif event.name == 'motion_notify_event': # set range for creation if self._event is None: return if event.x != self._event.x: for ax in self._axes[0]: self.update_current_rectangle(ax, event, self._original_event) if event.x != self._event.x: self._draw() self._event = event def _on_mouse_press_creation(self, event): """Set axes values based on point selected""" if self._pressed_button is not None: return # Discard event if a button is already pressed x_axes = set() y_axes = set() for ax in self.figure.axes: #Simmilar to event.inaxis = axis if ax.contains(event)[0]: x_axes.add(ax) y_axes.add(ax) self._axes = x_axes, y_axes self._pressed_button = event.button if self._pressed_button == 1: self._rectangle_creation(event) def _on_mouse_release_creation(self, event): if self._pressed_button == 1: self._rectangle_creation(event) self._pressed_button = None def _on_mouse_motion_creation(self, event): if self._pressed_button == 1: self._rectangle_creation(event)
class cropWidget(QWidget): cropDoneSignal = pyqtSignal(cropInfo) def __init__(self, currentFileLocation = ''): super().__init__() self.filename = currentFileLocation self.cropInfo = cropInfo() self.isPressed = False self.cropCheckWidget = cropCheckWidget(self.cropInfo) self.initUI() def initUI(self): self.hbox = QHBoxLayout() self.fig = plt.Figure() self.canvas = FigureCanvas(self.fig) self.ax = self.fig.add_subplot(111) self.canvas.mpl_connect("button_press_event", self.on_press) self.canvas.mpl_connect("motion_notify_event", self.on_move) self.canvas.mpl_connect("button_release_event", self.on_release) self.hbox.addWidget(self.canvas) self.setLayout(self.hbox) if (self.filename !=''): self.data = fits.open(Path(self.filename))[0].data zimshow(self.ax, self.data) self.canvas.draw() def setFileName(self, fileName): self.filename = fileName self.data = fits.open(Path(self.filename))[0].data zimshow(self.ax, self.data) self.canvas.draw() def on_press(self, event): if not event.inaxes : return if event.inaxes != self.ax: return self.rect = Rectangle((0, 0), 1, 1, alpha=0.5) self.ax.add_patch(self.rect) self.x0 = event.xdata self.y0 = event.ydata self.isPressed = True def on_move(self, event): if not event.inaxes : return if event.inaxes != self.ax: return if not self.isPressed : return self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.ax.figure.canvas.draw() def on_release(self, event): if not event.inaxes : return if event.inaxes != self.ax: return if not self.isPressed: return x = int(self.rect.get_x()) y = int(self.rect.get_y()) width = int(self.rect.get_width()) height = int(self.rect.get_height()) x0 = x x1 = x + width y0 = y y1 = y + height if (x0 > x1): x0, x1 = x1, x0 if (y0 > y1): y0, y1 = y1, y0 self.cropInfo.x0 = x0 self.cropInfo.x1 = x1 self.cropInfo.y0 = y0 self.cropInfo.y1 = y1 self.cropInfo.filename = self.filename self.cropDoneSignal.emit(self.cropInfo) self.rect.remove() self.ax.figure.canvas.draw() self.isPressed = False self.cropCheckWidget.setCropInfo(self.cropInfo) self.cropCheckWidget.show() self.cropCheckWidget.raise_()
class EditableRectangle: _angle = 0 def __init__(self, ax): self.ax = ax # Set up main rectangle self.rect = Rectangle((0, 0), 0, 0, visible=False, transform=None, picker=True) self.ax.add_patch(self.rect) # Set up anchors self.anchors = [] for i in range(len(RECTANGLE_ANCHOR_LOCS)): anchor = Rectangle((0, 0), ANCHOR_SIZE, ANCHOR_SIZE, visible=False, transform=None, facecolor='red', picker=True) self.anchors.append(anchor) self.ax.add_patch(anchor) self.press = None self.mode = None self.connect() def connect(self): self.cid_press = self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.cid_release = self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release) self.cid_motion = self.ax.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) self.cid_pick = self.ax.figure.canvas.mpl_connect('pick_event', self.on_pick) def on_pick(self, event): if event.artist in self.anchors: if event.mouseevent.key == 'r': self.mode = 'anchor-rotate' self.center_x = self.x0 + self.width * 0.5 self.center_y = self.y0 + self.height * 0.5 self.angle_start = self.angle self.angle_drag_start = np.degrees(np.arctan2(event.mouseevent.y - self.center_y, event.mouseevent.x - self.center_x)) print(self.angle_start) else: self.mode = 'anchor-drag' anchor_index = self.anchors.index(event.artist) self.active_anchor_index = anchor_index self.press = True elif event.artist is self.rect: self.mode = 'rectangle-drag' self.drag_start_x0 = self.x0 self.drag_start_y0 = self.y0 self.drag_start_x = event.mouseevent.x self.drag_start_y = event.mouseevent.y self.press = True def on_press(self, event): if event.inaxes != self.ax: return if self.mode == 'create': self.x0 = event.x self.y0 = event.y self.rect.set_visible(True) self.press = True # contains, attrd = self.rect.contains(event) # if not contains: return # print('event contains', self.rect.xy) # x0, y0 = self.rect.xy # self.press = x0, y0, event.x, event.y @property def angle(self): return self._angle @angle.setter def angle(self, value): self._angle = value self.rect.angle = value self.rect._update_patch_transform() @property def x0(self): return self.rect.get_x() @x0.setter def x0(self, value): self.rect.set_x(value) @property def y0(self): return self.rect.get_y() @y0.setter def y0(self, value): self.rect.set_y(value) @property def width(self): return self.rect.get_width() @width.setter def width(self, value): self.rect.set_width(value) @property def height(self): return self.rect.get_height() @height.setter def height(self, value): self.rect.set_height(value) def on_motion(self, event): if self.press is None: return if event.inaxes != self.ax: return if self.mode == 'create': self.width = event.x - self.x0 self.height = event.y - self.y0 self.rect.figure.canvas.draw() elif self.mode == 'rectangle-drag': self.x0 = self.drag_start_x0 + event.x - self.drag_start_x self.y0 = self.drag_start_y0 + event.y - self.drag_start_y self.update_anchors() self.rect.figure.canvas.draw() elif self.mode == 'anchor-drag': px, py = RECTANGLE_ANCHOR_LOCS[self.active_anchor_index] if px == -1: self.x0, self.width = event.x, self.x0 + self.width - event.x elif px == 1: self.width = event.x - self.x0 if py == -1: self.y0, self.height = event.y, self.y0 + self.height - event.y elif py == 1: self.height = event.y - self.y0 self.update_anchors() self.rect.figure.canvas.draw() elif self.mode == 'anchor-rotate': angle_current = np.degrees(np.arctan2(event.y - self.center_y, event.x - self.center_x)) self.angle = self.angle_start + (angle_current - self.angle_drag_start) self.update_anchors() self.rect.figure.canvas.draw() def on_release(self, event): if self.mode == 'create': self.update_anchors() self.set_anchor_visibility(True) self.press = None self.mode = None self.rect.figure.canvas.draw() def set_anchor_visibility(self, visible): for anchor in self.anchors: anchor.set_visible(visible) def update_anchors(self): for anchor, (dx, dy) in zip(self.anchors, RECTANGLE_ANCHOR_LOCS): xc = self.x0 + 0.5 * self.width yc = self.y0 + 0.5 * self.height dx = 0.5 * (dx * self.width - ANCHOR_SIZE) dy = 0.5 * (dy * self.height - ANCHOR_SIZE) dxr = dx * np.cos(np.radians(self.angle)) - dy * np.sin(np.radians(self.angle)) dyr = dx * np.sin(np.radians(self.angle)) + dy * np.cos(np.radians(self.angle)) anchor.set_xy((xc + dxr, yc + dyr)) def disconnect(self): self.ax.figure.canvas.mpl_disconnect(self.cid_press) self.ax.figure.canvas.mpl_disconnect(self.cid_release) self.ax.figure.canvas.mpl_disconnect(self.cid_motion)
def show_image(im: Union[np.ndarray, Tensor], axis: plt.Axes = None, fig: plt.Figure = None, title: Optional[str] = None, color_map: str = "inferno", stack_depth: int = 0) -> Optional[plt.Figure]: """Plots a given image onto an axis. The repeated invocation of this function will cause figure plot overlap. If `im` is 2D and the length of second dimension are 4 or 5, it will be viewed as bounding box data (x0, y0, w, h, <label>). ```python boxes = np.array([[0, 0, 10, 20, "apple"], [10, 20, 30, 50, "dog"], [40, 70, 200, 200, "cat"], [0, 0, 0, 0, "not_shown"], [0, 0, -10, -20, "not_shown2"]]) img = np.zeros((150, 150)) fig, axis = plt.subplots(1, 1) fe.util.show_image(img, fig=fig, axis=axis) # need to plot image first fe.util.show_image(boxes, fig=fig, axis=axis) ``` Users can also directly plot text ```python fig, axis = plt.subplots(1, 1) fe.util.show_image("apple", fig=fig, axis=axis) ``` Args: axis: The matplotlib axis to plot on, or None for a new plot. fig: A reference to the figure to plot on, or None if new plot. im: The image (width X height) / bounding box / text to display. title: A title for the image. color_map: Which colormap to use for greyscale images. stack_depth: Multiple images can be drawn onto the same axis. When stack depth is greater than zero, the `im` will be alpha blended on top of a given axis. Returns: plotted figure. It will be the same object as user have provided in the argument. """ if axis is None: fig, axis = plt.subplots(1, 1) axis.axis('off') # Compute width of axis for text font size bbox = axis.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) width, height = bbox.width * fig.dpi, bbox.height * fig.dpi space = min(width, height) if not hasattr(im, 'shape') or len(im.shape) < 2: # text data im = to_number(im) if hasattr(im, 'shape') and len(im.shape) == 1: im = im[0] im = im.item() if isinstance(im, bytes): im = im.decode('utf8') text = "{}".format(im) axis.text(0.5, 0.5, im, ha='center', transform=axis.transAxes, va='center', wrap=False, family='monospace', fontsize=min(45, space // len(text))) elif len(im.shape) == 2 and (im.shape[1] == 4 or im.shape[1] == 5): # Bounding Box Data. Should be (x0, y0, w, h, <label>) boxes = [] im = to_number(im) color = ["m", "r", "c", "g", "y", "b"][stack_depth % 6] for box in im: # Unpack the box, which may or may not have a label x0 = float(box[0]) y0 = float(box[1]) width = float(box[2]) height = float(box[3]) label = None if len(box) < 5 else str(box[4]) # Don't draw empty boxes, or invalid box if width <= 0 or height <= 0: continue r = Rectangle((x0, y0), width=width, height=height, fill=False, edgecolor=color, linewidth=3) boxes.append(r) if label: axis.text(r.get_x() + 3, r.get_y() + 3, label, ha='left', va='top', color=color, fontsize=max(8, min(14, width // len(label))), fontweight='bold', family='monospace') pc = PatchCollection(boxes, match_original=True) axis.add_collection(pc) else: if isinstance(im, torch.Tensor) and len(im.shape) > 2: # Move channel first to channel last channels = list(range(len(im.shape))) channels.append(channels.pop(0)) im = im.permute(*channels) # image data im = to_number(im) im_max = np.max(im) im_min = np.min(im) if np.issubdtype(im.dtype, np.integer): # im is already in int format im = im.astype(np.uint8) elif 0 <= im_min <= im_max <= 1: # im is [0,1] im = (im * 255).astype(np.uint8) elif -0.5 <= im_min < 0 < im_max <= 0.5: # im is [-0.5, 0.5] im = ((im + 0.5) * 255).astype(np.uint8) elif -1 <= im_min < 0 < im_max <= 1: # im is [-1, 1] im = ((im + 1) * 127.5).astype(np.uint8) else: # im is in some arbitrary range, probably due to the Normalize Op ma = abs( np.max(im, axis=tuple([i for i in range(len(im.shape) - 1)]) if len(im.shape) > 2 else None)) mi = abs( np.min(im, axis=tuple([i for i in range(len(im.shape) - 1)]) if len(im.shape) > 2 else None)) im = (((im + mi) / (ma + mi)) * 255).astype(np.uint8) # matplotlib doesn't support (x,y,1) images, so convert them to (x,y) if len(im.shape) == 3 and im.shape[2] == 1: im = np.reshape(im, (im.shape[0], im.shape[1])) alpha = 1 if stack_depth == 0 else 0.3 if len(im.shape) == 2: axis.imshow(im, cmap=plt.get_cmap(name=color_map), alpha=alpha) else: axis.imshow(im, alpha=alpha) if title is not None: axis.set_title(title, fontsize=min(20, 1 + width // len(title)), family='monospace') return fig
def drawHeatPlot(comparisonDescription, detector, layer, dof, outputDir, layerFillList, GeometryDict, diffColorRange, drawNames=False): fig = plt.figure(figsize=(11.6929134, 8.26771654), subplotpars=SubplotParams(wspace=0.35, left=0.1, bottom=0.1, right=0.98)) ax = fig.add_subplot(111, axisbg='#E6E6E6') ax.set_aspect("equal", adjustable="box") ax.set_title(comparisonDescription + "\n Detector: %s, Layer: %s, Degree of Freedom: %s" % (detector, layer, dof)) ax.set_xlabel( "A side $\qquad \qquad \qquad \qquad \qquad$ x (cm) $\qquad \qquad \qquad \qquad \qquad$ C side" ) # ax.set_xlabel("x (cm)") ax.set_ylabel("y (cm)") ax.grid(True, linestyle='-', linewidth=1.5, alpha=0.1) # put grid behind polygons ax.set_axisbelow(True) # reverse x axis to match LHCb coodrinates from VELO perspective ax.set_xlim(ax.get_xlim()[::-1]) patches = [] # values will be overwritten, we just need a numpy array at least as big as the fill list colorArray = np.array([x for x in range(len(layerFillList))], dtype=np.float64) stereoRotation = 0 if layer.find("U") != -1: stereoRotation = -5 if layer.find("V") != -1: stereoRotation = 5 logging.debug( "Building list of alignment elements and color array of corresponding alignment parameters" ) for i, (name, unused, matrix) in enumerate(layerFillList): _shape = lambda j: GeometryDict[name][ j] # (xy, width, height, rotateY, zorder) # nb: with x axis reversed, xy of rectangle is lower right point poly = Rectangle(_shape(0), _shape(1), _shape(2), zorder=_shape(4)) if stereoRotation != 0: rotate = mpl.transforms.Affine2D().rotate_deg_around( poly.get_x() + _shape(1) * 0.5, _shape(3), stereoRotation) poly.set_transform(rotate) patches.append(poly) colorArray[i] = getattr(matrix, dof) # element labels if drawNames: splitName = name.split("/") if detector == "TT": elementName = "\n".join(splitName[-3:]) labelRotation = 0 textSize = 4 elif detector == "IT": elementName = "\n".join(splitName[-3::2]) labelRotation = 90 textSize = 8 elif detector == "OT": elementName = "/".join(splitName[-2:]) labelRotation = 90 textSize = 10 smallAngleShift = 0 if stereoRotation != 0 and detector != "IT": tan = 0.08748866 smallAngleShift = -(poly.get_y() + _shape(2) * 0.5) * tan * cmp(stereoRotation, 0) elementLabel = plt.text(poly.get_x() + _shape(1) * 0.5 + smallAngleShift, poly.get_y() + _shape(2) * 0.5, elementName, verticalalignment='center', horizontalalignment='center', rotation=labelRotation - stereoRotation, size=textSize) polyCollection = PatchCollection(patches, cmap=mpl.cm.RdBu) polyCollection.set_array(colorArray) polyCollection.set_clim([-diffColorRange, diffColorRange]) ax.add_collection(polyCollection) cbar = plt.colorbar(polyCollection) if dof.startswith("T"): cbar.set_label("%s (mm)" % dof) elif dof.startswith("R"): cbar.set_label("%s (mrad)" % dof) plt.axis('equal') # this is busted for stereo layers, just putting the labels in the x axis title # if detector == "IT": # ax.text(ax.get_xlim()[0], 0, '$\quad$A side', horizontalalignment='left', verticalalignment='center') # ax.text(ax.get_xlim()[1], 0, '$\!\!\!$ C side', horizontalalignment='right', verticalalignment='center') # else: # ax.text(ax.get_xlim()[0], 0, 'A side $\qquad$', horizontalalignment='right', verticalalignment='center') # ax.text(ax.get_xlim()[1], 0, '$\quad$C side', horizontalalignment='left', verticalalignment='center') detectorOutputDir = os.path.join(*([outputDir] + [detector])) if not os.path.isdir(detectorOutputDir): os.makedirs(detectorOutputDir) layerName = layer.replace("/", "_") fileName = "_".join((detector, layerName, dof)) + ".pdf" outputPath = os.path.join(*([detectorOutputDir] + [fileName])) print " Writing %s" % outputPath fig.savefig(outputPath)
class rectSelector(object): def __init__(self, fig, df): self.fig = fig self.df = df self.cid_on = fig.canvas.mpl_connect('button_press_event', self) self.cid_off = fig.canvas.mpl_connect('button_release_event', self) self.cid_move = None self.feats = {'lim1': [-np.inf, +np.inf], 'lim2': [-np.inf, +np.inf], 'feat1': None, 'feat2': None} self.rect = None def __call__(self, event): if event.inaxes is None: return if event.name=='button_press_event': self.on_press(event) elif event.name=='button_release_event': self.on_release(event) def on_press(self, event): if self.rect is not None: self.rect.remove() self.feats['lim1'] = [-np.inf, +np.inf] self.feats['lim2'] = [-np.inf, +np.inf] (feat1, feat2) = [feat for ax, feat in ax2feat.iteritems() if ax==event.inaxes][0] self.feats = {'feat1': feat1, 'feat2': feat2, 'lim1': [event.xdata]*2, 'lim2':[event.ydata]*2} self.rect = Rectangle([event.xdata, event.ydata], 0., 0., alpha=0.3) event.inaxes.add_patch(self.rect) self.cid_move = self.fig.canvas.mpl_connect('motion_notify_event', self.on_move) def on_release(self, event): self.fig.canvas.mpl_disconnect(self.cid_move) def on_move(self, event): if event.inaxes is None: return # resize the rectangle so the new width tracks the mouse pointer self.rect.set_width(event.xdata - self.rect.get_x()) self.rect.set_height(event.ydata - self.rect.get_y()) # update the limits for each feature self.feats['lim1'] = [self.rect.get_x(), event.xdata] if event.xdata<self.rect.get_x(): self.feats['lim1'] = self.feats['lim1'][::-1] self.feats['lim2'] = [self.rect.get_y(), event.ydata] if event.ydata<self.rect.get_y(): self.feats['lim2'] = self.feats['lim2'][::-1] self.filterData() def filterData(self): feat1 = self.feats['feat1'] feat2 = self.feats['feat2'] lim1 = self.feats['lim1'] lim2 = self.feats['lim2'] df = self.df ix = np.vstack((df[feat1]>lim1[0], df[feat1]<lim1[1], df[feat2]>lim2[0], df[feat2]<lim2[1])).all(0) colors = np.array([colordict[i] for i in df['species']], dtype = 'S4') colors[~ix] = 'gray' for sc in scatters: sc.set_color(colors) plt.show()
class StatsPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) self.ztv_frame = self.GetTopLevelParent() self.ztv_frame.primary_image_panel.popup_menu_cursor_modes.append( 'Stats box') self.ztv_frame.primary_image_panel.available_cursor_modes[ 'Stats box'] = { 'set-to-mode': self.set_cursor_to_stats_box_mode, 'on_button_press': self.on_button_press, 'on_motion': self.on_motion, 'on_button_release': self.on_button_release } self.stats_info = None self.last_string_values = { 'x0': '', 'xsize': '', 'x1': '', 'y0': '', 'ysize': '', 'y1': '' } self.stats_rect = Rectangle((0, 0), 10, 10, color='magenta', fill=False, zorder=100) # use self.stats_rect as where we store/retrieve the x0,y0,x1,y1 # x0,y0,x1,y1 should be limited to range of 0 to shape-1 # but, stats should be calculated over e.g. x0:x1+1 (so that have pixels to do stats on even if x0==x1) # and, width/height of stats_rect should always be >= 0 textentry_font = wx.Font(14, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.FONTWEIGHT_LIGHT, False) values_sizer = wx.FlexGridSizer(10, 5, 0, 0) values_sizer.SetFlexibleDirection(wx.BOTH) values_sizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.low_static_text = wx.StaticText(self, wx.ID_ANY, u"Low", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) self.low_static_text.Wrap(-1) values_sizer.Add(self.low_static_text, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 0) self.low_static_text = wx.StaticText(self, wx.ID_ANY, u"# pix", wx.DefaultPosition, wx.DefaultSize, 0) self.low_static_text.Wrap(-1) values_sizer.Add(self.low_static_text, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 0) self.high_static_text = wx.StaticText(self, wx.ID_ANY, u"High", wx.DefaultPosition, wx.DefaultSize, 0) self.high_static_text.Wrap(-1) values_sizer.Add(self.high_static_text, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 0) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.x_static_text = wx.StaticText(self, wx.ID_ANY, u"x", wx.DefaultPosition, wx.DefaultSize, 0) self.x_static_text.Wrap(-1) values_sizer.Add(self.x_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 0) self.x0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.x0_textctrl.SetFont(textentry_font) values_sizer.Add(self.x0_textctrl, 0, wx.ALL, 2) self.x0_textctrl.Bind(wx.EVT_TEXT, self.x0_textctrl_changed) self.x0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x0_textctrl_entered) self.xsize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.xsize_textctrl.SetFont(textentry_font) values_sizer.Add(self.xsize_textctrl, 0, wx.ALL, 2) self.xsize_textctrl.Bind(wx.EVT_TEXT, self.xsize_textctrl_changed) self.xsize_textctrl.Bind(wx.EVT_TEXT_ENTER, self.xsize_textctrl_entered) self.x1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.x1_textctrl.SetFont(textentry_font) values_sizer.Add(self.x1_textctrl, 0, wx.ALL, 2) self.x1_textctrl.Bind(wx.EVT_TEXT, self.x1_textctrl_changed) self.x1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x1_textctrl_entered) self.npix_static_text = wx.StaticText(self, wx.ID_ANY, u"# pixels", wx.DefaultPosition, wx.DefaultSize, 0) self.npix_static_text.Wrap(-1) values_sizer.Add(self.npix_static_text, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_BOTTOM, 0) self.y_static_text = wx.StaticText(self, wx.ID_ANY, u"y", wx.DefaultPosition, wx.DefaultSize, 0) self.y_static_text.Wrap(-1) values_sizer.Add(self.y_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 0) self.y0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.y0_textctrl.SetFont(textentry_font) values_sizer.Add(self.y0_textctrl, 0, wx.ALL, 2) self.y0_textctrl.Bind(wx.EVT_TEXT, self.y0_textctrl_changed) self.y0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y0_textctrl_entered) self.ysize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.ysize_textctrl.SetFont(textentry_font) values_sizer.Add(self.ysize_textctrl, 0, wx.ALL, 2) self.ysize_textctrl.Bind(wx.EVT_TEXT, self.ysize_textctrl_changed) self.ysize_textctrl.Bind(wx.EVT_TEXT_ENTER, self.ysize_textctrl_entered) self.y1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.y1_textctrl.SetFont(textentry_font) values_sizer.Add(self.y1_textctrl, 0, wx.ALL, 2) self.y1_textctrl.Bind(wx.EVT_TEXT, self.y1_textctrl_changed) self.y1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y1_textctrl_entered) self.npix_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.npix_textctrl.SetFont(textentry_font) self.npix_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.npix_textctrl, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, 0) values_sizer.AddSpacer((0, 15), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.median_static_text = wx.StaticText(self, wx.ID_ANY, u"Median", wx.DefaultPosition, wx.DefaultSize, 0) self.median_static_text.Wrap(-1) values_sizer.Add(self.median_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0) self.median_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.median_textctrl.SetFont(textentry_font) self.median_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.median_textctrl, 0, wx.ALL, 2) self.robust_static_text = wx.StaticText(self, wx.ID_ANY, u"Robust", wx.DefaultPosition, wx.DefaultSize, 0) self.robust_static_text.Wrap(-1) values_sizer.Add(self.robust_static_text, 0, wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 0) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.mean_static_text = wx.StaticText(self, wx.ID_ANY, u"Mean", wx.DefaultPosition, wx.DefaultSize, 0) self.mean_static_text.Wrap(-1) values_sizer.Add(self.mean_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0) self.mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.mean_textctrl.SetFont(textentry_font) self.mean_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.mean_textctrl, 0, wx.ALL, 2) self.robust_mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.robust_mean_textctrl.SetFont(textentry_font) self.robust_mean_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.robust_mean_textctrl, 0, wx.ALL, 2) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.stdev_static_text = wx.StaticText(self, wx.ID_ANY, u"Stdev", wx.DefaultPosition, wx.DefaultSize, 0) self.stdev_static_text.Wrap(-1) values_sizer.Add(self.stdev_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0) self.stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.stdev_textctrl.SetFont(textentry_font) self.stdev_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.stdev_textctrl, 0, wx.ALL, 2) self.robust_stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.robust_stdev_textctrl.SetFont(textentry_font) self.robust_stdev_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.robust_stdev_textctrl, 0, wx.ALL, 2) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 15), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.min_static_text = wx.StaticText(self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, 0) self.min_static_text.Wrap(-1) values_sizer.Add(self.min_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0) self.minval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.minval_textctrl.SetFont(textentry_font) self.minval_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.minval_textctrl, 0, wx.ALL, 2) self.minpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.minpos_textctrl.SetFont(textentry_font) self.minpos_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.minpos_textctrl, 0, wx.ALL, 2) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) values_sizer.AddSpacer((0, 0), 0, wx.EXPAND) self.max_static_text = wx.StaticText(self, wx.ID_ANY, u"Max", wx.DefaultPosition, wx.DefaultSize, 0) self.max_static_text.Wrap(-1) values_sizer.Add(self.max_static_text, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0) self.maxval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.maxval_textctrl.SetFont(textentry_font) self.maxval_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.maxval_textctrl, 0, wx.ALL, 2) self.maxpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.maxpos_textctrl.SetFont(textentry_font) self.maxpos_textctrl.SetBackgroundColour( textctrl_output_only_background_color) values_sizer.Add(self.maxpos_textctrl, 0, wx.ALL, 2) self.hideshow_button = wx.Button(self, wx.ID_ANY, u"Show", wx.DefaultPosition, wx.DefaultSize, 0) values_sizer.Add( self.hideshow_button, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 2) self.hideshow_button.Bind(wx.EVT_BUTTON, self.on_hideshow_button) v_sizer1 = wx.BoxSizer(wx.VERTICAL) v_sizer1.AddStretchSpacer(1.0) v_sizer1.Add(values_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL) v_sizer1.AddStretchSpacer(1.0) self.SetSizer(v_sizer1) pub.subscribe(self.queue_update_stats, 'recalc-display-image-called') pub.subscribe(self._set_stats_box_parameters, 'set-stats-box-parameters') pub.subscribe(self.publish_stats_to_stream, 'get-stats-box-info') def publish_stats_to_stream(self, msg=None): wx.CallAfter(send_to_stream, sys.stdout, ('stats-box-info', self.stats_info)) def on_button_press(self, event): self.select_panel() self.stats_start_timestamp = event.guiEvent.GetTimestamp() # millisec self.update_stats_box(event.xdata, event.ydata, event.xdata, event.ydata) self.redraw_overplot_on_image() self.cursor_stats_box_x0, self.cursor_stats_box_y0 = event.xdata, event.ydata def on_motion(self, event): self.update_stats_box(self.cursor_stats_box_x0, self.cursor_stats_box_y0, event.xdata, event.ydata) self.redraw_overplot_on_image() self.update_stats() def on_button_release(self, event): self.redraw_overplot_on_image() self.update_stats() def set_cursor_to_stats_box_mode(self, event): self.ztv_frame.primary_image_panel.cursor_mode = 'Stats box' self.ztv_frame.stats_panel.select_panel() self.ztv_frame.stats_panel.highlight_panel() def queue_update_stats(self, msg=None): """ wrapper to call update_stats from CallAfter in order to make GUI as responsive as possible. """ wx.CallAfter(self.update_stats, msg=None) def _set_stats_box_parameters(self, msg): """ wrapper to update_stats_box to receive messages & translate them correctly """ x0, x1, y0, y1 = [None] * 4 if msg['xrange'] is not None: x0, x1 = msg['xrange'] if msg['yrange'] is not None: y0, y1 = msg['yrange'] if msg['xrange'] is not None or msg['yrange'] is not None: self.update_stats_box(x0, y0, x1, y1) if msg['show_overplot'] is not None: if msg['show_overplot']: self.redraw_overplot_on_image() else: self.remove_overplot_on_image() send_to_stream(sys.stdout, ('set-stats-box-parameters-done', True)) def update_stats_box(self, x0=None, y0=None, x1=None, y1=None): if x0 is None: x0 = self.stats_rect.get_x() if y0 is None: y0 = self.stats_rect.get_y() if x1 is None: x1 = self.stats_rect.get_x() + self.stats_rect.get_width() if y1 is None: y1 = self.stats_rect.get_y() + self.stats_rect.get_height() if x0 > x1: x0, x1 = x1, x0 if y0 > y1: y0, y1 = y1, y0 x0 = min(max(0, x0), self.ztv_frame.display_image.shape[1] - 1) y0 = min(max(0, y0), self.ztv_frame.display_image.shape[0] - 1) x1 = min(max(0, x1), self.ztv_frame.display_image.shape[1] - 1) y1 = min(max(0, y1), self.ztv_frame.display_image.shape[0] - 1) self.stats_rect.set_bounds(x0, y0, x1 - x0, y1 - y0) self.ztv_frame.primary_image_panel.figure.canvas.draw() self.update_stats() def remove_overplot_on_image(self): if self.stats_rect in self.ztv_frame.primary_image_panel.axes.patches: self.ztv_frame.primary_image_panel.axes.patches.remove( self.stats_rect) self.ztv_frame.primary_image_panel.figure.canvas.draw() self.hideshow_button.SetLabel(u"Show") def redraw_overplot_on_image(self): if self.stats_rect not in self.ztv_frame.primary_image_panel.axes.patches: self.ztv_frame.primary_image_panel.axes.add_patch(self.stats_rect) self.ztv_frame.primary_image_panel.figure.canvas.draw() self.hideshow_button.SetLabel(u"Hide") def on_hideshow_button(self, evt): if self.hideshow_button.GetLabel() == 'Hide': self.remove_overplot_on_image() else: self.redraw_overplot_on_image() def get_x0y0x1y1_from_stats_rect(self): x0 = self.stats_rect.get_x() y0 = self.stats_rect.get_y() x1 = x0 + self.stats_rect.get_width() y1 = y0 + self.stats_rect.get_height() return x0, y0, x1, y1 def update_stats(self, msg=None): x0, y0, x1, y1 = self.get_x0y0x1y1_from_stats_rect() x0, y0 = int(np.round(x0)), int(np.round(y0)) x1, y1 = int(np.round(x1)), int(np.round(y1)) self.last_string_values['x0'] = str(int(x0)) self.x0_textctrl.SetValue(self.last_string_values['x0']) self.last_string_values['y0'] = str(int(y0)) self.y0_textctrl.SetValue(self.last_string_values['y0']) x_npix = int(x1 - x0 + 1) self.last_string_values['xsize'] = str(x_npix) self.xsize_textctrl.SetValue(self.last_string_values['xsize']) y_npix = int(y1 - y0 + 1) self.last_string_values['ysize'] = str(y_npix) self.ysize_textctrl.SetValue(self.last_string_values['ysize']) self.last_string_values['x1'] = str(int(x1)) self.x1_textctrl.SetValue(self.last_string_values['x1']) self.last_string_values['y1'] = str(int(y1)) self.y1_textctrl.SetValue(self.last_string_values['y1']) self.npix_textctrl.SetValue(str(x_npix * y_npix)) stats_data = self.ztv_frame.display_image[y0:y1 + 1, x0:x1 + 1] finite_mask = np.isfinite(stats_data) if finite_mask.max() is np.True_: stats_data_mean = stats_data[finite_mask].mean() stats_data_median = np.median(stats_data[finite_mask]) stats_data_std = stats_data[finite_mask].std() robust_mean, robust_median, robust_std = sigma_clipped_stats( stats_data[finite_mask]) else: stats_data_mean = np.nan stats_data_median = np.nan stats_data_std = np.inf robust_mean, robust_median, robust_std = np.nan, np.nan, np.inf self.stats_info = { 'xrange': [x0, x1], 'yrange': [y0, y1], 'mean': stats_data_mean, 'median': stats_data_median, 'std': stats_data_std, 'min': stats_data.min(), 'max': stats_data.max() } # want min/max to reflect any Inf/NaN self.mean_textctrl.SetValue("{:0.4g}".format(self.stats_info['mean'])) self.median_textctrl.SetValue("{:0.4g}".format( self.stats_info['median'])) self.stdev_textctrl.SetValue("{:0.4g}".format(self.stats_info['std'])) self.stats_info['robust-mean'] = robust_mean self.stats_info['robust-median'] = robust_median self.stats_info['robust-std'] = robust_std self.robust_mean_textctrl.SetValue("{:0.4g}".format(robust_mean)) self.robust_stdev_textctrl.SetValue("{:0.4g}".format(robust_std)) self.minval_textctrl.SetValue("{:0.4g}".format(self.stats_info['min'])) self.maxval_textctrl.SetValue("{:0.4g}".format(self.stats_info['max'])) wmin = np.where(stats_data == stats_data.min()) wmin = [(wmin[1][i] + x0, wmin[0][i] + y0) for i in np.arange(wmin[0].size)] if len(wmin) == 1: wmin = wmin[0] self.minpos_textctrl.SetValue("{}".format(wmin)) self.stats_info['wmin'] = wmin wmax = np.where(stats_data == stats_data.max()) wmax = [(wmax[1][i] + x0, wmax[0][i] + y0) for i in np.arange(wmax[0].size)] if len(wmax) == 1: wmax = wmax[0] self.maxpos_textctrl.SetValue("{}".format(wmax)) self.stats_info['wmax'] = wmax set_textctrl_background_color(self.x0_textctrl, 'ok') set_textctrl_background_color(self.x1_textctrl, 'ok') set_textctrl_background_color(self.xsize_textctrl, 'ok') set_textctrl_background_color(self.y0_textctrl, 'ok') set_textctrl_background_color(self.y1_textctrl, 'ok') set_textctrl_background_color(self.ysize_textctrl, 'ok') def x0_textctrl_changed(self, evt): validate_textctrl_str(self.x0_textctrl, int, self.last_string_values['x0']) def x0_textctrl_entered(self, evt): if validate_textctrl_str(self.x0_textctrl, int, self.last_string_values['x0']): self.last_string_values['x0'] = self.x0_textctrl.GetValue() self.update_stats_box(int(self.last_string_values['x0']), None, None, None) self.x0_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def xsize_textctrl_changed(self, evt): validate_textctrl_str(self.xsize_textctrl, int, self.last_string_values['xsize']) def xsize_textctrl_entered(self, evt): if validate_textctrl_str(self.xsize_textctrl, int, self.last_string_values['xsize']): self.last_string_values['xsize'] = self.xsize_textctrl.GetValue() xsize = int(self.last_string_values['xsize']) sys.stderr.write("\n\nxsize = {}\n\n".format(xsize)) x0, y0, x1, y1 = self.get_x0y0x1y1_from_stats_rect() xc = (x0 + x1) / 2. x0 = max(0, int(xc - xsize / 2.)) x1 = x0 + xsize - 1 x1 = min(x1, self.ztv_frame.display_image.shape[1] - 1) x0 = x1 - xsize + 1 x0 = max(0, int(xc - xsize / 2.)) self.update_stats_box(x0, y0, x1, y1) self.xsize_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def x1_textctrl_changed(self, evt): validate_textctrl_str(self.x1_textctrl, int, self.last_string_values['x1']) def x1_textctrl_entered(self, evt): if validate_textctrl_str(self.x1_textctrl, int, self.last_string_values['x1']): self.last_string_values['x1'] = self.x1_textctrl.GetValue() self.update_stats_box(None, None, int(self.last_string_values['x1']), None) self.x1_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def y0_textctrl_changed(self, evt): validate_textctrl_str(self.y0_textctrl, int, self.last_string_values['y0']) def y0_textctrl_entered(self, evt): if validate_textctrl_str(self.y0_textctrl, int, self.last_string_values['y0']): self.last_string_values['y0'] = self.y0_textctrl.GetValue() self.update_stats_box(None, int(self.last_string_values['y0']), None, None) self.y0_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def ysize_textctrl_changed(self, evt): validate_textctrl_str(self.ysize_textctrl, int, self.last_string_values['ysize']) def ysize_textctrl_entered(self, evt): if validate_textctrl_str(self.ysize_textctrl, int, self.last_string_values['ysize']): self.last_string_values['ysize'] = self.ysize_textctrl.GetValue() ysize = int(self.last_string_values['ysize']) x0, y0, x1, y1 = self.get_x0y0x1y1_from_stats_rect() yc = (y0 + y1) / 2. y0 = max(0, int(yc - ysize / 2.)) y1 = y0 + ysize - 1 y1 = min(y1, self.ztv_frame.display_image.shape[0] - 1) y0 = y1 - ysize + 1 y0 = max(0, int(yc - ysize / 2.)) self.update_stats_box(x0, y0, x1, y1) self.ysize_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def y1_textctrl_changed(self, evt): validate_textctrl_str(self.y1_textctrl, int, self.last_string_values['y1']) def y1_textctrl_entered(self, evt): if validate_textctrl_str(self.y1_textctrl, int, self.last_string_values['y1']): self.last_string_values['y1'] = self.y1_textctrl.GetValue() self.update_stats_box(None, None, None, int(self.last_string_values['y1'])) self.y1_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image()
class ZoneInteret: """ Cette classe permet de gérer les information de zone interet Une zone interet est un cadre d'image sur l'image entière. On se concentre sur cette zone interet pour faire le traitement. C'est une manière de réduire le bruit sur le résultat de traitement @version 2.0 """ @staticmethod def verifier_presence_fichier_ini(): """ Vérifier si les fichiers de zone interet sont déjà présents dans le dossier :return: true si présent, false sinon """ return os.path.isfile('./zi/param.ini') @staticmethod def supprimer_ZI(window): """ La méthode pour gérer la suppresion de zone interet :param window: le fenetre principale :return: """ if os.path.isfile('./zi/param.ini'): try: os.remove("./zi/param.ini") os.remove("./zi/image_modele.png") os.remove("./zi/image_zone_interet.png") QMessageBox.information(window, "Information", "Supprimer la Zone d'intérêt avec succès", QMessageBox.Ok) except OSError: QMessageBox.warning(window, "Erreur", "Impossible de supprimer les fichiers dans le repertoire /zi", QMessageBox.Ok) else: QMessageBox.warning(window, "Erreur", "Impossible de trouver les fichiers dans le repertoire /zi", QMessageBox.Ok) def __init__(self, video): """ Initialise les variables nécessaires à l'affichage de l'image et aux événements :param video: la vidéo à traiter """ self.flag = False self.get_one_image_from_video(video) # On se sert de l'image extraite précédemment self.img = mpimg.imread('./zi/image_modele.png') # On initialise le titre de la fenêtre fig = plt.figure(1) fig.canvas.set_window_title("Zone Interet") # On récupère les infos des axes self.ax = plt.gca() # On initialise le futur rectangle dessiné (non rempli aux bordures rouges) self.rect = Rectangle((0, 0), 1, 1, fill=False, edgecolor="red") # Initialisation des points du rectangle self.x0 = None self.y0 = None self.x1 = None self.y1 = None self.ax.add_patch(self.rect) # Liaison des événements self.ax.figure.canvas.mpl_connect('button_press_event', self.on_mouseclick_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_mouseclick_release) self.ax.figure.canvas.mpl_connect('key_press_event', self.on_keyboard_press) # Affichage de l'image dans la fenêtre self.imgplot = plt.imshow(self.img) self.show_window() def on_mouseclick_press(self, event): """ Un click gauche -> sauvegarde des coordonnées du pointeur :param event: évènement de clique :return: """ self.x0 = event.xdata self.y0 = event.ydata def on_mouseclick_release(self, event): """ Click gauche relâché -> dessin du rectangle :param event: évènement de souris :return: """ self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.ax.figure.canvas.draw() def on_keyboard_press(self, event): """ Si la touche "enter" est appuyée, on sauvegarde la zone d'intérêt :param event: évenenment de keyboard :return: """ if event.key == 'enter': self.flag = True with open("./zi/param.ini", "w") as file: file.write(str(int(self.rect.get_x())) + ",") file.write(str(int(self.rect.get_y())) + ",") file.write(str(int(self.rect.get_width())) + ",") file.write(str(int(self.rect.get_height()))) # On cache les axes avant d'enregistrer l'image modele avec la zone d'interet self.ax.get_xaxis().set_visible(False) self.ax.get_yaxis().set_visible(False) plt.title("Zone interet") plt.savefig("./zi/image_zone_interet.png") plt.close() def show_window(self): """ Pour afficher la fenêtre qui est utilisée pour choisir une zone interet :return: """ plt.title("Selectionner la zone interet avec la souris. Appuyez sur entrer pour valider.") plt.show() def get_one_image_from_video(self, video): """ Extrait une image de la vidéo selectionnée Cette image est utilisée pour choisir une zone interet :param video: la vidéo choisie :return: """ video_capture = cv2.VideoCapture(video) nb_frame = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) video_capture.set(cv2.CAP_PROP_FRAME_COUNT, int(nb_frame - 1)) success, self.image = video_capture.read() #sauvegarder l'image cv2.imwrite("zi/image_modele.png", self.image)
class DraggableRectangle(object): def __init__(self, axes, rectwidth=100, lrwidth=10, recty=100): self.ax = axes # plt.gca() self.rect_x = axes.get_xlim()[1] - rectwidth - lrwidth self.rect = Rectangle((self.rect_x, 0), rectwidth, recty, facecolor='g', alpha=0.2) self.rectl = Rectangle((self.rect_x - lrwidth, 0), lrwidth, recty, facecolor='b', alpha=0.8) self.rectr = Rectangle((self.rect_x + rectwidth, 0), lrwidth, recty, facecolor='b', alpha=0.8) self.ax.add_patch(self.rect) self.ax.add_patch(self.rectl) self.ax.add_patch(self.rectr) self.rect_width = rectwidth self.press = None # 0, self.pressl = None self.pressr = None self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release) self.ax.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): if event.inaxes != self.rect.axes: return contains, attrd = self.rect.contains(event) containsl, attrdl = self.rectl.contains(event) containsr, attrdr = self.rectr.contains(event) if contains: x0, y0 = self.rect.xy self.press = x0, y0, event.xdata, event.ydata elif containsl: xl, yl = self.rectl.xy self.pressl = xl, yl, event.xdata, event.ydata elif containsr: xr, yr = self.rectr.xy self.pressr = xr, yr, event.xdata, event.ydata else: return def on_release(self, event): # print ('release') self.press = None self.pressl = None self.pressr = None self.ax.figure.canvas.draw() def on_motion(self, event): 'on motion we will move the rect if the mouse is over us' if self.press is None and self.pressl is None and self.pressr is None: return if event.inaxes != self.rect.axes and event.inaxes != self.rectl.axes and event.inaxes != self.rectr.axes: return rectwidth = self.rect_width if self.press != None: x0, y0, xpress, ypress = self.press dx = event.xdata - xpress rectl_x = x0 + dx - self.rectl.get_width() # to tell the left edge overriding if rectl_x < 0: rectl_x = 0 # to tell the right edge overriding if rectl_x + self.rectl.get_width( ) + rectwidth + self.rectr.get_width() > self.ax.get_xlim()[1] - 1: rectl_x = self.ax.get_xlim( )[1] - rectwidth - self.rectr.get_width( ) - self.rectl.get_width() print("self.ax.get_xlim()[1]=", self.ax.get_xlim()[1], " rectLx=", rectl_x) self.rect_x = rectl_x + self.rectl.get_width() self.rect.set_x(self.rect_x) self.rectl.set_x(rectl_x) self.rectr.set_x(self.rect_x + self.rect.get_width()) # self.rect.figure.canvas.draw() elif self.pressl != None: if event.xdata >= self.rectr.get_x() - 10: self.rect_width = 10 return xl, yl, xpress, ypress = self.pressl dx = event.xdata - xpress rectl_x = xl + dx #- self.rectl.get_width() # to tell the left edge overriding if rectl_x < 0: self.rect_width = self.rectr.get_x() - self.rectl.get_width() rectl_x = 0 self.rectl.set_x(rectl_x) # draw rect self.rect.set_x(rectl_x + self.rectl.get_width()) xr = self.rectr.get_x() rectwidth = xr - rectl_x - self.rectl.get_width() # self.rectl.figure.canvas.draw() elif self.pressr != None: if event.xdata <= self.rectl.get_x() + self.rectl.get_width() + 10: self.rect_width = 10 return xr, yr, xpress, ypress = self.pressr dx = event.xdata - xpress rectr_x = xr + dx # to tell the right edge overriding xlim = self.ax.get_xlim()[1] print('xlim=', xlim) if rectr_x + self.rectr.get_width() > xlim: self.rect_width = xlim - self.rectl.get_x( ) - self.rectl.get_width() rectr_x = xlim - self.rectr.get_width() self.rectr.set_x(rectr_x) # draw rect rectwidth = rectr_x - self.rectl.get_x() - self.rectl.get_width() # self.rectr.figure.canvas.draw() self.rect.set_width(rectwidth) self.rect_width = rectwidth self.ax.figure.canvas.draw() # self.rectl.figure.canvas.draw() # why we just draw rect here, but rectl and rectr are drawed too ? def reset_rects(self, xl, xr): xlim = self.ax.get_xlim()[1] if xr > xlim: return if xl < 0: return self.rectl.set_x(xl) self.rectr.set_x(xr - self.rectr.get_width()) self.rect_x = xl + self.rectl.get_width() self.rect.set_x(self.rect_x) self.rect_width = xr - self.rectr.get_width() - self.rect_x self.rect.set_width(self.rect_width) self.ax.figure.canvas.draw() def is_pressed(self): r = 0 if self.press: r = 1 elif self.pressl: r = 2 elif self.pressr: r = 3 else: r = 0 return r def get_rect_x1(self): return int(self.rectl.get_x()) def get_rect_x2(self): return int(self.rectr.get_x() + self.rectr.get_width() - 1)
class StatsPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) self.ztv_frame = self.GetTopLevelParent() self.ztv_frame.primary_image_panel.popup_menu_cursor_modes.append('Stats box') self.ztv_frame.primary_image_panel.available_cursor_modes['Stats box'] = { 'set-to-mode':self.set_cursor_to_stats_box_mode, 'on_button_press':self.on_button_press, 'on_motion':self.on_motion, 'on_button_release':self.on_button_release} self.textentry_font = wx.Font(14, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.FONTWEIGHT_LIGHT, False) self.stats_info = None self.last_string_values = {'x0':'', 'xsize':'', 'x1':'', 'y0':'', 'ysize':'', 'y1':''} self.stats_rect = Rectangle((0, 0), 10, 10, color='magenta', fill=False, zorder=100) # use self.stats_rect as where we store/retrieve the x0,y0,x1,y1 # x0,y0,x1,y1 should be limited to range of 0 to shape-1 # but, stats should be calculated over e.g. x0:x1+1 (so that have pixels to do stats on even if x0==x1) # and, width/height of stats_rect should always be >= 0 values_sizer = wx.FlexGridSizer( 10, 5, 0, 0 ) values_sizer.SetFlexibleDirection( wx.BOTH ) values_sizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.low_static_text = wx.StaticText( self, wx.ID_ANY, u"Low", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) self.low_static_text.Wrap( -1 ) values_sizer.Add(self.low_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 0) self.low_static_text = wx.StaticText( self, wx.ID_ANY, u"# pix", wx.DefaultPosition, wx.DefaultSize, 0 ) self.low_static_text.Wrap( -1 ) values_sizer.Add(self.low_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 0) self.high_static_text = wx.StaticText( self, wx.ID_ANY, u"High", wx.DefaultPosition, wx.DefaultSize, 0 ) self.high_static_text.Wrap( -1 ) values_sizer.Add(self.high_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 0) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.x_static_text = wx.StaticText( self, wx.ID_ANY, u"x", wx.DefaultPosition, wx.DefaultSize, 0 ) self.x_static_text.Wrap( -1 ) values_sizer.Add(self.x_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 0) self.x0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.x0_textctrl.SetFont(self.textentry_font) values_sizer.Add(self.x0_textctrl, 0, wx.ALL, 2) self.x0_textctrl.Bind(wx.EVT_TEXT, self.x0_textctrl_changed) self.x0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x0_textctrl_entered) self.xsize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.xsize_textctrl.SetFont(self.textentry_font) values_sizer.Add(self.xsize_textctrl, 0, wx.ALL, 2) self.xsize_textctrl.Bind(wx.EVT_TEXT, self.xsize_textctrl_changed) self.xsize_textctrl.Bind(wx.EVT_TEXT_ENTER, self.xsize_textctrl_entered) self.x1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.x1_textctrl.SetFont(self.textentry_font) values_sizer.Add(self.x1_textctrl, 0, wx.ALL, 2) self.x1_textctrl.Bind(wx.EVT_TEXT, self.x1_textctrl_changed) self.x1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.x1_textctrl_entered) self.npix_static_text = wx.StaticText( self, wx.ID_ANY, u"# pixels", wx.DefaultPosition, wx.DefaultSize, 0 ) self.npix_static_text.Wrap( -1 ) values_sizer.Add(self.npix_static_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM, 0) self.y_static_text = wx.StaticText( self, wx.ID_ANY, u"y", wx.DefaultPosition, wx.DefaultSize, 0 ) self.y_static_text.Wrap( -1 ) values_sizer.Add(self.y_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 0) self.y0_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.y0_textctrl.SetFont(self.textentry_font) values_sizer.Add(self.y0_textctrl, 0, wx.ALL, 2) self.y0_textctrl.Bind(wx.EVT_TEXT, self.y0_textctrl_changed) self.y0_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y0_textctrl_entered) self.ysize_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.ysize_textctrl.SetFont(self.textentry_font) values_sizer.Add(self.ysize_textctrl, 0, wx.ALL, 2) self.ysize_textctrl.Bind(wx.EVT_TEXT, self.ysize_textctrl_changed) self.ysize_textctrl.Bind(wx.EVT_TEXT_ENTER, self.ysize_textctrl_entered) self.y1_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.y1_textctrl.SetFont(self.textentry_font) values_sizer.Add(self.y1_textctrl, 0, wx.ALL, 2) self.y1_textctrl.Bind(wx.EVT_TEXT, self.y1_textctrl_changed) self.y1_textctrl.Bind(wx.EVT_TEXT_ENTER, self.y1_textctrl_entered) self.npix_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.npix_textctrl.SetFont(self.textentry_font) self.npix_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.npix_textctrl, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT, 0) values_sizer.AddSpacer((0,15), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.median_static_text = wx.StaticText( self, wx.ID_ANY, u"Median", wx.DefaultPosition, wx.DefaultSize, 0 ) self.median_static_text.Wrap( -1 ) values_sizer.Add(self.median_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0) self.median_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.median_textctrl.SetFont(self.textentry_font) self.median_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.median_textctrl, 0, wx.ALL, 2) self.robust_static_text = wx.StaticText( self, wx.ID_ANY, u"Robust", wx.DefaultPosition, wx.DefaultSize, 0 ) self.robust_static_text.Wrap( -1 ) values_sizer.Add(self.robust_static_text, 0, wx.ALL|wx.ALIGN_BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, 0) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.mean_static_text = wx.StaticText( self, wx.ID_ANY, u"Mean", wx.DefaultPosition, wx.DefaultSize, 0 ) self.mean_static_text.Wrap( -1 ) values_sizer.Add(self.mean_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0) self.mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.mean_textctrl.SetFont(self.textentry_font) self.mean_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.mean_textctrl, 0, wx.ALL, 2) self.robust_mean_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.robust_mean_textctrl.SetFont(self.textentry_font) self.robust_mean_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.robust_mean_textctrl, 0, wx.ALL, 2) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.stdev_static_text = wx.StaticText( self, wx.ID_ANY, u"Stdev", wx.DefaultPosition, wx.DefaultSize, 0 ) self.stdev_static_text.Wrap( -1 ) values_sizer.Add(self.stdev_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0) self.stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.stdev_textctrl.SetFont(self.textentry_font) self.stdev_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.stdev_textctrl, 0, wx.ALL, 2) self.robust_stdev_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.robust_stdev_textctrl.SetFont(self.textentry_font) self.robust_stdev_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.robust_stdev_textctrl, 0, wx.ALL, 2) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,15), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.min_static_text = wx.StaticText( self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, 0 ) self.min_static_text.Wrap( -1 ) values_sizer.Add(self.min_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0) self.minval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.minval_textctrl.SetFont(self.textentry_font) self.minval_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.minval_textctrl, 0, wx.ALL, 2) self.minpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.minpos_textctrl.SetFont(self.textentry_font) self.minpos_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.minpos_textctrl, 0, wx.ALL, 2) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) values_sizer.AddSpacer((0,0), 0, wx.EXPAND) self.max_static_text = wx.StaticText( self, wx.ID_ANY, u"Max", wx.DefaultPosition, wx.DefaultSize, 0 ) self.max_static_text.Wrap( -1 ) values_sizer.Add(self.max_static_text, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, 0) self.maxval_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.maxval_textctrl.SetFont(self.textentry_font) self.maxval_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.maxval_textctrl, 0, wx.ALL, 2) self.maxpos_textctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY) self.maxpos_textctrl.SetFont(self.textentry_font) self.maxpos_textctrl.SetBackgroundColour(textctrl_output_only_background_color) values_sizer.Add(self.maxpos_textctrl, 0, wx.ALL, 2) self.hideshow_button = wx.Button(self, wx.ID_ANY, u"Show", wx.DefaultPosition, wx.DefaultSize, 0) values_sizer.Add(self.hideshow_button, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 2) self.hideshow_button.Bind(wx.EVT_BUTTON, self.on_hideshow_button) v_sizer1 = wx.BoxSizer(wx.VERTICAL) v_sizer1.AddStretchSpacer(1.0) v_sizer1.Add(values_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL) v_sizer1.AddStretchSpacer(1.0) self.SetSizer(v_sizer1) pub.subscribe(self.queue_update_stats, 'recalc-display-image-called') pub.subscribe(self._set_stats_box_parameters, 'set-stats-box-parameters') pub.subscribe(self.publish_stats_to_stream, 'get-stats-box-info') def publish_stats_to_stream(self, msg=None): wx.CallAfter(send_to_stream, sys.stdout, ('stats-box-info', self.stats_info)) def on_button_press(self, event): self.select_panel() self.update_stats_box(event.xdata, event.ydata, event.xdata, event.ydata) self.redraw_overplot_on_image() self.cursor_stats_box_x0, self.cursor_stats_box_y0 = event.xdata, event.ydata def on_motion(self, event): if event.button is not None: self.update_stats_box(self.cursor_stats_box_x0, self.cursor_stats_box_y0, event.xdata, event.ydata) self.redraw_overplot_on_image() self.update_stats() def on_button_release(self, event): self.redraw_overplot_on_image() self.update_stats() def set_cursor_to_stats_box_mode(self, event): self.ztv_frame.primary_image_panel.cursor_mode = 'Stats box' self.ztv_frame.stats_panel.select_panel() self.ztv_frame.stats_panel.highlight_panel() def queue_update_stats(self, msg=None): """ wrapper to call update_stats from CallAfter in order to make GUI as responsive as possible. """ wx.CallAfter(self.update_stats, msg=None) def _set_stats_box_parameters(self, msg): """ wrapper to update_stats_box to receive messages & translate them correctly """ x0,x1,y0,y1 = [None]*4 if msg['xrange'] is not None: x0,x1 = msg['xrange'] if msg['yrange'] is not None: y0,y1 = msg['yrange'] if msg['xrange'] is not None or msg['yrange'] is not None: self.update_stats_box(x0, y0, x1, y1) if msg['show_overplot'] is not None: if msg['show_overplot']: self.redraw_overplot_on_image() else: self.remove_overplot_on_image() send_to_stream(sys.stdout, ('set-stats-box-parameters-done', True)) def update_stats_box(self, x0=None, y0=None, x1=None, y1=None): if x0 is None: x0 = self.stats_rect.get_x() if y0 is None: y0 = self.stats_rect.get_y() if x1 is None: x1 = self.stats_rect.get_x() + self.stats_rect.get_width() if y1 is None: y1 = self.stats_rect.get_y() + self.stats_rect.get_height() if x0 > x1: x0, x1 = x1, x0 if y0 > y1: y0, y1 = y1, y0 x0 = min(max(0, x0), self.ztv_frame.display_image.shape[1] - 1) y0 = min(max(0, y0), self.ztv_frame.display_image.shape[0] - 1) x1 = min(max(0, x1), self.ztv_frame.display_image.shape[1] - 1) y1 = min(max(0, y1), self.ztv_frame.display_image.shape[0] - 1) self.stats_rect.set_bounds(x0, y0, x1 - x0, y1 - y0) if self.hideshow_button.GetLabel() == 'Hide': self.ztv_frame.primary_image_panel.figure.canvas.draw() self.update_stats() def remove_overplot_on_image(self): self.ztv_frame.primary_image_panel.remove_patch('stats_panel:stats_rect') self.hideshow_button.SetLabel(u"Show") def redraw_overplot_on_image(self): self.ztv_frame.primary_image_panel.add_patch('stats_panel:stats_rect', self.stats_rect) self.hideshow_button.SetLabel(u"Hide") def on_hideshow_button(self, evt): if self.hideshow_button.GetLabel() == 'Hide': self.remove_overplot_on_image() else: self.redraw_overplot_on_image() def get_x0y0x1y1_from_stats_rect(self): x0 = self.stats_rect.get_x() y0 = self.stats_rect.get_y() x1 = x0 + self.stats_rect.get_width() y1 = y0 + self.stats_rect.get_height() return x0,y0,x1,y1 def update_stats(self, msg=None): x0,y0,x1,y1 = self.get_x0y0x1y1_from_stats_rect() x0, y0 = int(np.round(x0)), int(np.round(y0)) x1, y1 = int(np.round(x1)), int(np.round(y1)) self.last_string_values['x0'] = str(int(x0)) self.x0_textctrl.SetValue(self.last_string_values['x0']) self.last_string_values['y0'] = str(int(y0)) self.y0_textctrl.SetValue(self.last_string_values['y0']) x_npix = int(x1 - x0 + 1) self.last_string_values['xsize'] = str(x_npix) self.xsize_textctrl.SetValue(self.last_string_values['xsize']) y_npix = int(y1 - y0 + 1) self.last_string_values['ysize'] = str(y_npix) self.ysize_textctrl.SetValue(self.last_string_values['ysize']) self.last_string_values['x1'] = str(int(x1)) self.x1_textctrl.SetValue(self.last_string_values['x1']) self.last_string_values['y1'] = str(int(y1)) self.y1_textctrl.SetValue(self.last_string_values['y1']) self.npix_textctrl.SetValue(str(x_npix * y_npix)) stats_data = self.ztv_frame.display_image[y0:y1+1, x0:x1+1] finite_mask = np.isfinite(stats_data) if finite_mask.max() is np.True_: stats_data_mean = stats_data[finite_mask].mean() stats_data_median = np.median(stats_data[finite_mask]) stats_data_std = stats_data[finite_mask].std() robust_mean, robust_median, robust_std = sigma_clipped_stats(stats_data[finite_mask]) else: stats_data_mean = np.nan stats_data_median = np.nan stats_data_std = np.inf robust_mean, robust_median, robust_std = np.nan, np.nan, np.inf self.stats_info = {'xrange':[x0,x1], 'yrange':[y0,y1], 'mean':stats_data_mean, 'median':stats_data_median, 'std':stats_data_std, 'min':stats_data.min(), 'max':stats_data.max()} # want min/max to reflect any Inf/NaN self.mean_textctrl.SetValue("{:0.4g}".format(self.stats_info['mean'])) self.median_textctrl.SetValue("{:0.4g}".format(self.stats_info['median'])) self.stdev_textctrl.SetValue("{:0.4g}".format(self.stats_info['std'])) self.stats_info['robust-mean'] = robust_mean self.stats_info['robust-median'] = robust_median self.stats_info['robust-std'] = robust_std self.robust_mean_textctrl.SetValue("{:0.4g}".format(robust_mean)) self.robust_stdev_textctrl.SetValue("{:0.4g}".format(robust_std)) self.minval_textctrl.SetValue("{:0.4g}".format(self.stats_info['min'])) self.maxval_textctrl.SetValue("{:0.4g}".format(self.stats_info['max'])) wmin = np.where(stats_data == stats_data.min()) wmin = [(wmin[1][i] + x0,wmin[0][i] + y0) for i in np.arange(wmin[0].size)] if len(wmin) == 1: wmin = wmin[0] self.minpos_textctrl.SetValue("{}".format(wmin)) self.stats_info['wmin'] = wmin wmax = np.where(stats_data == stats_data.max()) wmax = [(wmax[1][i] + x0,wmax[0][i] + y0) for i in np.arange(wmax[0].size)] if len(wmax) == 1: wmax = wmax[0] self.maxpos_textctrl.SetValue("{}".format(wmax)) self.stats_info['wmax'] = wmax set_textctrl_background_color(self.x0_textctrl, 'ok') set_textctrl_background_color(self.x1_textctrl, 'ok') set_textctrl_background_color(self.xsize_textctrl, 'ok') set_textctrl_background_color(self.y0_textctrl, 'ok') set_textctrl_background_color(self.y1_textctrl, 'ok') set_textctrl_background_color(self.ysize_textctrl, 'ok') def x0_textctrl_changed(self, evt): validate_textctrl_str(self.x0_textctrl, int, self.last_string_values['x0']) def x0_textctrl_entered(self, evt): if validate_textctrl_str(self.x0_textctrl, int, self.last_string_values['x0']): self.last_string_values['x0'] = self.x0_textctrl.GetValue() self.update_stats_box(int(self.last_string_values['x0']), None, None, None) self.x0_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def xsize_textctrl_changed(self, evt): validate_textctrl_str(self.xsize_textctrl, int, self.last_string_values['xsize']) def xsize_textctrl_entered(self, evt): if validate_textctrl_str(self.xsize_textctrl, int, self.last_string_values['xsize']): self.last_string_values['xsize'] = self.xsize_textctrl.GetValue() xsize = int(self.last_string_values['xsize']) sys.stderr.write("\n\nxsize = {}\n\n".format(xsize)) x0,y0,x1,y1 = self.get_x0y0x1y1_from_stats_rect() xc = (x0 + x1) / 2. x0 = max(0, int(xc - xsize / 2.)) x1 = x0 + xsize - 1 x1 = min(x1, self.ztv_frame.display_image.shape[1] - 1) x0 = x1 - xsize + 1 x0 = max(0, int(xc - xsize / 2.)) self.update_stats_box(x0, y0, x1, y1) self.xsize_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def x1_textctrl_changed(self, evt): validate_textctrl_str(self.x1_textctrl, int, self.last_string_values['x1']) def x1_textctrl_entered(self, evt): if validate_textctrl_str(self.x1_textctrl, int, self.last_string_values['x1']): self.last_string_values['x1'] = self.x1_textctrl.GetValue() self.update_stats_box(None, None, int(self.last_string_values['x1']), None) self.x1_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def y0_textctrl_changed(self, evt): validate_textctrl_str(self.y0_textctrl, int, self.last_string_values['y0']) def y0_textctrl_entered(self, evt): if validate_textctrl_str(self.y0_textctrl, int, self.last_string_values['y0']): self.last_string_values['y0'] = self.y0_textctrl.GetValue() self.update_stats_box(None, int(self.last_string_values['y0']), None, None) self.y0_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def ysize_textctrl_changed(self, evt): validate_textctrl_str(self.ysize_textctrl, int, self.last_string_values['ysize']) def ysize_textctrl_entered(self, evt): if validate_textctrl_str(self.ysize_textctrl, int, self.last_string_values['ysize']): self.last_string_values['ysize'] = self.ysize_textctrl.GetValue() ysize = int(self.last_string_values['ysize']) x0,y0,x1,y1 = self.get_x0y0x1y1_from_stats_rect() yc = (y0 + y1) / 2. y0 = max(0, int(yc - ysize / 2.)) y1 = y0 + ysize - 1 y1 = min(y1, self.ztv_frame.display_image.shape[0] - 1) y0 = y1 - ysize + 1 y0 = max(0, int(yc - ysize / 2.)) self.update_stats_box(x0, y0, x1, y1) self.ysize_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image() def y1_textctrl_changed(self, evt): validate_textctrl_str(self.y1_textctrl, int, self.last_string_values['y1']) def y1_textctrl_entered(self, evt): if validate_textctrl_str(self.y1_textctrl, int, self.last_string_values['y1']): self.last_string_values['y1'] = self.y1_textctrl.GetValue() self.update_stats_box(None, None, None, int(self.last_string_values['y1'])) self.y1_textctrl.SetSelection(-1, -1) self.redraw_overplot_on_image()
def visualize(Grid): fig, axes = plt.subplots(2, 1, dpi=100, figsize=(12, 8)) axes[0].plot(Grid.Ez) axes[0].xaxis.grid(linestyle='dotted') axes[0].set_xlabel('Cell') axes[0].set_ylabel(r'$E_z$', fontsize=12, rotation=0) axes[0].set_title( 'time passed: {0:.3e}s, timesteps passed: {timesteps}'.format( Grid.time_passed, timesteps=Grid.timesteps_passed)) #axes[0].set_ylim([-1.5, 1.5]) axes[1].plot(Grid.Hy) axes[1].xaxis.grid(linestyle='dotted') axes[1].set_xlabel('Cell') axes[1].set_ylabel(r'$H_y$', fontsize=12, rotation=0) for mat in Grid.materials: media_repr_0 = Rectangle(xy=(mat.position[0] - 0.5, -1.4), height=2.8, width=(mat.position[-1] - mat.position[0] + 1), color='grey', fill=True, alpha=mat.eps * 0.12) axes[0].add_patch(media_repr_0) axes[0].annotate(s=r'$\epsilon_r$={0:.2f}'.format(mat.eps) + '\n' + r'$\sigma$={0:.2f}'.format(mat.conductivity), xy=(media_repr_0.get_x() + 0.1, media_repr_0.get_y() + 0.2), color='black') media_repr_1 = Rectangle(xy=(mat.position[0] - 0.5, -1.4), height=2.8, width=(mat.position[-1] - mat.position[0] + 1), color='grey', fill=True, alpha=mat.eps * 0.12) axes[1].add_patch(media_repr_1) if mat.model == 'Lorentz': s = r'$\epsilon(\omega)$' + '\n' + r'$\sigma$={0:.2f}'.format( mat.conductivity) else: s = r'$\epsilon_r$={0:.2f}'.format( mat.eps) + '\n' + r'$\sigma$={0:.2f}'.format(mat.conductivity) axes[1].annotate(s, xy=(media_repr_1.get_x() + 0.1, media_repr_1.get_y() + 0.2), color='black') for src in Grid.sources: src_repr = Rectangle(xy=(src.position - 0.5, -src.ampl), height=2 * src.ampl, width=1, color='red', alpha=0.3) axes[0].add_patch(src_repr) for obs in Grid.local_observers: if isinstance(obs, QuasiHarmonicObserver): obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4), height=2.8, width=1, color='green', alpha=0.3) elif isinstance(obs, E_FFTObserver): obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4), height=2.8, width=1, color='indigo', alpha=0.3) elif isinstance(obs, P_FFTObserver): obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4), height=2.8, width=1, color='orange', alpha=0.3) elif isinstance(obs, MovingFrame): obs_repr = Rectangle(xy=(obs.position[0] - 0.5, -1.4), height=2.8, width=obs.position[-1] - obs.position[0], fc='none', color='red') axes[0].add_patch(obs_repr) fig.tight_layout() plt.show()
def __init__(self, grid_obj, final_timestep): self.grid = grid_obj fig_ani, self.axes_ani = plt.subplots(2, 1, dpi=100, figsize=(12, 10)) self.final_timestep = final_timestep self.grid.timesteps = self.final_timestep self.axes_ani[0].xaxis.grid(linestyle='dotted') self.axes_ani[0].set_xlabel('Cell') self.axes_ani[0].set_ylabel(r'$E_z$', fontsize=12, rotation=0) self.axes_ani[1].xaxis.grid(linestyle='dotted') self.axes_ani[1].set_xlabel('Cell') self.axes_ani[1].set_ylabel(r'$H_y$', fontsize=12, rotation=0) self.line_0 = Line2D([], []) self.line_1 = Line2D([], []) self.axes_ani[0].add_line(self.line_0) self.axes_ani[1].add_line(self.line_1) self.axes_ani[0].set_xlim([0, self.grid.nx - 1]) self.axes_ani[1].set_xlim([0, self.grid.nx - 1]) self.axes_ani[0].set_ylim([-1.5, 1.5]) self.axes_ani[1].set_ylim( [-1.5 / np.sqrt(mu0 / eps0), 1.5 / np.sqrt(mu0 / eps0)]) for mat in self.grid.materials: media_repr_0 = Rectangle(xy=(mat.position[0] - 0.5, -1.4), height=2.8, width=(mat.position[-1] - mat.position[0] + 1), color='grey', fill=True, alpha=mat.eps * 0.12) self.axes_ani[0].add_patch(media_repr_0) if mat.model == 'Lorentz': s = r'$\epsilon(\omega)$' + '\n' + r'$\sigma$={0:.2f}'.format( mat.conductivity) else: s = r'$\epsilon_r$={0:.2f}'.format( mat.eps) + '\n' + r'$\sigma$={0:.2f}'.format( mat.conductivity) self.axes_ani[0].annotate(s, xy=(media_repr_0.get_x() + 0.1, media_repr_0.get_y() + 0.2), color='black') media_repr_1 = Rectangle(xy=(mat.position[0] - 0.5, -1.4), height=2.8, width=(mat.position[-1] - mat.position[0] + 1), color='grey', fill=True, alpha=mat.eps * 0.12) self.axes_ani[1].add_patch(media_repr_1) for src in self.grid.sources: src_repr = Rectangle(xy=(src.position - 0.5, -src.ampl), height=2 * src.ampl, width=1, color='red', alpha=0.3) self.axes_ani[0].add_patch(src_repr) for obs in self.grid.local_observers: if isinstance(obs, QuasiHarmonicObserver): obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4), height=2.8, width=1, color='green', alpha=0.3) elif isinstance(obs, E_FFTObserver): obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4), height=2.8, width=1, color='indigo', alpha=0.3) elif isinstance(obs, P_FFTObserver): obs_repr = Rectangle(xy=(obs.position - 0.5, -1.4), height=2.8, width=1, color='orange', alpha=0.3) self.axes_ani[0].add_patch(obs_repr) ani.TimedAnimation.__init__(self, fig_ani, blit=True, interval=5, repeat=False)
class SpanSelector(_SelectorWidget): """Custom SpanSelector.""" # pylint: disable=too-many-instance-attributes # pylint: disable=too-many-arguments # pylint: disable=attribute-defined-outside-init # pylint: disable=invalid-name def __init__(self, ax, onselect, direction, minspan=None, useblit=False, rectprops=None, onmove_callback=None, span_stays=False, button=None): _SelectorWidget.__init__(self, ax, onselect, useblit=useblit, button=button) if rectprops is None: rectprops = dict(facecolor='red', alpha=0.5) rectprops['animated'] = self.useblit if direction not in ['horizontal', 'vertical']: msg = "direction must be in [ 'horizontal' | 'vertical' ]" raise ValueError(msg) self.direction = direction self.rect = None self.pressv = None self.rectprops = rectprops self.onmove_callback = onmove_callback self.minspan = minspan self.span_stays = span_stays # Needed when dragging out of axes self.prev = (0, 0) # Reset canvas so that `new_axes` connects events. self.canvas = None self.new_axes(ax) def new_axes(self, ax): """Set SpanSelector to operate on a new Axes""" self.ax = ax if self.canvas is not ax.figure.canvas: if self.canvas is not None: self.disconnect_events() self.canvas = ax.figure.canvas self.connect_default_events() if self.direction == 'horizontal': trans = blended_transform_factory(self.ax.transData, self.ax.transAxes) w, h = 0, 2 else: trans = blended_transform_factory(self.ax.transAxes, self.ax.transData) w, h = 1, 0 self.rect = Rectangle((0, -0.5), w, h, transform=trans, visible=False, **self.rectprops) if self.span_stays: self.stay_rect = Rectangle((0, 0), w, h, transform=trans, visible=False, **self.rectprops) self.stay_rect.set_animated(False) self.ax.add_patch(self.stay_rect) self.ax.add_patch(self.rect) self.artists = [self.rect] def set_rectprops(self, rectprops): """Custom: set new rectprops.""" self.rectprops = rectprops self.new_axes(self.ax) def ignore(self, event): """return *True* if *event* should be ignored""" return _SelectorWidget.ignore(self, event) or not self.visible def _press(self, event): """on button press event""" if self.ignore(event): return True self.rect.set_visible(self.visible) if self.span_stays: self.stay_rect.set_visible(False) # really force a draw so that the stay rect is not in # the blit background if self.useblit: self.canvas.draw() xdata, ydata = self._get_data(event) if self.direction == 'horizontal': self.pressv = xdata else: self.pressv = ydata return False def _release(self, event): """on button release event""" if self.ignore(event): return True if self.pressv is None: return True self.buttonDown = False self.rect.set_visible(False) if self.span_stays: self.stay_rect.set_x(self.rect.get_x()) self.stay_rect.set_y(self.rect.get_y()) self.stay_rect.set_width(self.rect.get_width()) self.stay_rect.set_height(self.rect.get_height()) self.stay_rect.set_visible(True) self.canvas.draw_idle() vmin = self.pressv xdata, ydata = self._get_data(event) if self.direction == 'horizontal': vmax = xdata or self.prev[0] else: vmax = ydata or self.prev[1] if vmin > vmax: vmin, vmax = vmax, vmin span = vmax - vmin if self.minspan is not None and span < self.minspan: return True self.onselect(vmin, vmax) self.pressv = None return False def _onmove(self, event): """on motion notify event""" if self.ignore(event): return True if self.pressv is None: return True x, y = self._get_data(event) if x is None: return True self.prev = x, y if self.direction == 'horizontal': v = x else: v = y minv, maxv = v, self.pressv if minv > maxv: minv, maxv = maxv, minv if self.direction == 'horizontal': self.rect.set_x(minv) self.rect.set_width(maxv - minv) else: self.rect.set_y(minv) self.rect.set_height(maxv - minv) if self.onmove_callback is not None: vmin = self.pressv xdata, ydata = self._get_data(event) if self.direction == 'horizontal': vmax = xdata or self.prev[0] else: vmax = ydata or self.prev[1] if vmin > vmax: vmin, vmax = vmax, vmin self.onmove_callback(vmin, vmax) self.update() return False
class CustomToolbar(NavToolbar): toolitems = NavToolbar.toolitems + ( (None, None, None, None), ("ROI", "Select ROI", "selection", "_on_custom_select"), ) def __init__(self, plotCanvas): # create the default toolbar NavToolbar.__init__(self, plotCanvas) self.selector = RectSelector( self.canvas.figure.axes[0], self.onSelect, button=[1, 3], minspanx=5, minspany=5 # don't use middle button ) self.selector.set_active(True) self.ax = self.canvas.figure.axes[0] self.roi = None self.fixedSize = False if wx.Platform == "__WXMAC__": self.to_draw = Rectangle( (0, 0), 0, 1, visible=False, facecolor="yellow", edgecolor="black", alpha=0.5, fill=True ) self.ax.add_patch(self.to_draw) self.background = None def _init_toolbar(self): self._parent = self.canvas.GetParent() self.wx_ids = {} for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.AddSeparator() continue self.wx_ids[text] = wx.NewId() try: bitmap = _load_bitmap(image_file + ".png") except IOError: bitmap = wx.Bitmap(image_file + ".png") if text in ["Pan", "Zoom", "ROI"]: self.AddCheckTool(self.wx_ids[text], bitmap, shortHelp=text, longHelp=tooltip_text) else: self.AddSimpleTool(self.wx_ids[text], bitmap, text, tooltip_text) bind(self, wx.EVT_TOOL, getattr(self, callback), id=self.wx_ids[text]) self.ToggleTool(self.wx_ids["ROI"], True) self.Realize() def _set_markers(self): self.canvas.parentFrame.set_markers() def _update_view(self): NavToolbar._update_view(self) self._set_markers() # MacOS needs a forced draw to update plot if wx.Platform == "__WXMAC__": self.canvas.draw() def draw(self): self._set_markers() NavToolbar.draw(self) # MacOS needs a forced draw to update plot if wx.Platform == "__WXMAC__": self.canvas.draw() def zoom(self, ev): if wx.Platform == "__WXMAC__": self.ToggleTool(self.wx_ids["Zoom"], self.GetToolState(self.wx_ids["Zoom"])) NavToolbar.zoom(self, ev) def pan(self, ev): if wx.Platform == "__WXMAC__": self.ToggleTool(self.wx_ids["Pan"], self.GetToolState(self.wx_ids["Pan"])) NavToolbar.pan(self, ev) def press_zoom(self, ev): if wx.Platform == "__WXMAC__": self.update_background() self.to_draw.set_visible(True) NavToolbar.press_zoom(self, ev) def release_zoom(self, ev): if wx.Platform == "__WXMAC__": self.to_draw.set_visible(False) NavToolbar.release_zoom(self, ev) def draw_rubberband(self, event, x0, y0, x1, y1): # XOR does not work on MacOS ... if wx.Platform != "__WXMAC__": NavToolbar.draw_rubberband(self, event, x0, y0, x1, y1) else: if self.background is not None: self.canvas.restore_region(self.background) c0, c1 = self.ax.transData.inverted().transform([[x0, y0], [x1, y1]]) l, b = c0 r, t = c1 self.to_draw.set_bounds(l, b, r - l, t - b) self.ax.draw_artist(self.to_draw) self.canvas.blit(self.ax.bbox) def update_background(self): """force an update of the background""" self.background = self.canvas.copy_from_bbox(self.ax.bbox) # Turn on selection # TODO: Proper handling of states, actual functionality. def _on_custom_select(self, evt): # for id in ['Zoom','Pan']: # self.ToggleTool(self.wx_ids[id], False) # print('Select ROI: %s' % (self.GetToolState(self.wx_ids['ROI']))) # self.ToggleTool(self.wx_ids['ROI'], # self.GetToolState(self.wx_ids['ROI']) ) self.toggle_selector() # print('Select ROI: %s' % (self.GetToolState(self.wx_ids['ROI']))) def onSelect(self, eclick, erelease): "eclick and erelease are matplotlib events at press and release" # print(' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata)) # print(' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata)) # print(' used button : ', eclick.button) self.updateROI( min(eclick.xdata, erelease.xdata), min(eclick.ydata, erelease.ydata), abs(eclick.xdata - erelease.xdata), abs(eclick.ydata - erelease.ydata), ) if self.canvas.parentFrame.fixedNumberCB.IsChecked(): # We are working in the fixed-number mode # We need to find new roi for this center point # The handler will call the update ROI function for us. self.canvas.parentFrame.handleROIforN() def updateROI(self, x, y, w, h): if self.roi is None: # print('upd ROI:', x, y, w, h) self.roi = Rectangle((x, y), w, h, ls="solid", lw=2, color="r", fill=False, zorder=5) self.canvas.figure.axes[0].add_patch(self.roi) else: self.roi.set_bounds(x, y, w, h) self.updateCanvas() def toggle_selector(self): self.selector.set_active(not self.selector.active) def onFixedSize(self, ev): self.fixedSize = ev.IsChecked() self.updateCanvas() def onWidthChange(self, ev): if self.roi: x = self.roi.get_x() w = self.roi.get_width() nw = ev.GetValue() dw = {"C": (w - nw) / 2, "L": 0, "R": w - nw}[self.canvas.parentFrame.anchorRB.GetStringSelection()[0]] self.roi.set_x(x + dw) self.roi.set_width(nw) self.updateCanvas() def onHeightChange(self, ev): if self.roi: y = self.roi.get_y() h = self.roi.get_height() nh = ev.GetValue() dh = {"C": (h - nh) / 2, "B": 0, "T": h - nh}[self.canvas.parentFrame.anchorRB.GetStringSelection()[-1]] self.roi.set_y(y + dh) self.roi.set_height(nh) self.updateCanvas() def updateCanvas(self, redraw=True): if self.roi: self.canvas.parentFrame.showROI( self.roi.get_x(), self.roi.get_y(), self.roi.get_width(), self.roi.get_height() ) self.canvas.parentFrame.setWH(self.roi.get_width(), self.roi.get_height()) if self.fixedSize: self.selector.setSize(self.roi.get_width(), self.roi.get_height()) else: self.selector.setSize() if redraw: self.draw()