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 place(self, item: Rectangle) -> tuple: item_width = (item.get_width() + self.gap_width) for i in range(len(self.shelves)): pass return ()
def ask_ROI(self): if self.data == []: return plt.figure() r = self.picture.ROI ROI = Rectangle((r[0], r[2]), r[1] - r[0], r[3] - r[2], alpha=1, fc='none', ec='green', linewidth=5) plt.imshow(self.data, extent=(self.xm.min(), self.xm.max(), self.ym.max(), self.ym.min())) plt.gca().add_patch(ROI) ed_ROI = EditableRectangle(ROI, fixed_aspect_ratio=False) ed_ROI.connect() plt.show() self.picture.ROI = (ROI.xy[0], ROI.xy[0] + ROI.get_width(), ROI.xy[1], ROI.xy[1] + ROI.get_height())
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 draw_rectangle(self, lower, upper, node): r = Rectangle(lower, upper[0] - lower[0], upper[1]-lower[1], edgecolor='k', facecolor = (0,0,0)) self.ax.add_patch(r) if node.is_leaf(): rx, ry = r.get_xy() cx = rx + r.get_width()/2.0 cy = ry + r.get_height()/2.0 r.set_facecolor( node.get_colour()) self.ax.annotate(node.get_weight(), (cx, cy), color=(0,0,0), fontsize = 10, ha='center', va='center') print node.name, rx, ry, cx, cy
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 add_box(ax,box_bounds,clrs, fill = False, linewidth = 2): # lonmin,lonmax, latmin, latmax from matplotlib.patches import Rectangle for ii, box in enumerate(box_bounds): p = Rectangle( (box[0], box[2]), box[1]-box[0], box[3]-box[2], linewidth=linewidth,fill=False,color=clrs[ii], zorder = 3) ax.add_patch(p) rx, ry = p.get_xy() cx = rx + p.get_width()/2.0 cy = ry + p.get_height()/2.0
def demo(self): model = self.model() model.load_weights('./checkpoints/model') test_dataset = self.database.get_tf_dataset(partition='test', batch_size=1, all_info=True) for i, item in enumerate(test_dataset): img, annot, orig_img, y1, y2, x1, x2 = item x1 = x1[0, ...].numpy() x2 = x2[0, ...].numpy() y1 = y1[0, ...].numpy() y2 = y2[0, ...].numpy() prediction = model.predict(img) prediction = 1 / (1 + np.exp(prediction)) prediction = prediction[0, 0] if annot == 1: prediction = 1 - prediction text = f'with_mask\n{prediction:0.2f}' else: text = f'no_mask\n{prediction:0.2f}' if prediction > 0.5: color = 'g' else: color = 'r' rect = Rectangle((y1, x1), y2 - y1, x2 - x1, fill=False, linewidth=4, edgecolor=color) position = list(rect.get_xy()) position[0] += rect.get_width() / 2 position[1] -= 20 plt.imshow(orig_img[0, ...]) plt.gca().add_patch(rect) plt.gca().annotate(text, position, color='w', weight='bold', fontsize=18, ha='center', va='center') plt.show()
def place(self, item: Rectangle) -> tuple: item_width = item.get_width() + self.gap_width for shelve in self.shelves: w, x, y, ingots = shelve if item_width <= w: shelve[0] -= item_width to = (x, y) shelve[1] += item_width ingots.append(item) return to return None
def draw_trip(base_line, start, duration, color, text): rect_w = duration * 0.1 rect_h = 0.08 x = start * 0.1 y = base_line + 0.1 rect = Rectangle((x, y), rect_w, rect_h, alpha=1, facecolor=color) rx, ry = rect.get_xy() cx = rx + rect.get_width() / 2.0 cy = ry + rect.get_height() / 2.0 currentAxis.annotate(str(start), (cx, cy), color='w', weight='bold', fontsize=6, ha='center', va='center') currentAxis.add_patch(rect)
def draw_days(base_line, number): rect_w = 0.1 rect_h = 0.08 for day in range(0, number): x = day * 0.1 y = base_line rect = Rectangle((x, y), rect_w, rect_h, alpha=1, fill=None) rx, ry = rect.get_xy() cx = rx + rect.get_width() / 2.0 cy = ry + rect.get_height() / 2.0 currentAxis.annotate(day, (cx, cy), color='b', weight='bold', fontsize=6, ha='center', va='center') currentAxis.add_patch(rect)
def ask_Hole(self): if self.data == []: return if self.picture.ROI == []: return if self.hole == []: self.hole = self.picture.ROI xm = self.xm ym = self.ym data_fit = self.data # Restrict to region of Interest : x = xm[1, :] y = ym[:, 1] ix_start = x[x <= self.picture.ROI[0]].argmax() ix_stop = x[x <= self.picture.ROI[1]].argmax() iy_start = y[y <= self.picture.ROI[2]].argmax() iy_stop = y[y <= self.picture.ROI[3]].argmax() xm = xm[iy_start:iy_stop, ix_start:ix_stop] ym = ym[iy_start:iy_stop, ix_start:ix_stop] data_fit = data_fit[iy_start:iy_stop, ix_start:ix_stop] plt.figure('Hole') r = self.hole hole = Rectangle((r[0], r[2]), r[1] - r[0], r[3] - r[2], alpha=1, fc='none', ec='red', linewidth=2) plt.imshow(data_fit, extent=(xm.min(), xm.max(), ym.max(), ym.min())) plt.gca().add_patch(hole) ed_hole = EditableRectangle(hole, fixed_aspect_ratio=False) ed_hole.connect() plt.show() self.hole = (hole.xy[0], hole.xy[0] + hole.get_width(), hole.xy[1], hole.xy[1] + hole.get_height())
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_rectangle(self, lower, upper, node): r = Rectangle(lower, upper[0] - lower[0], upper[1] - lower[1], edgecolor='k', facecolor=node.get_color(), label=node.name) self.ax.add_patch(r) rx, ry = r.get_xy() rw = r.get_width() rh = r.get_height() cx = rx + rw / 2.0 cy = ry + rh / 2.0 if isinstance(node, PathNode): t = node.name if rw * 3 < rh: t += ", " else: t += "\n" t += str(node.size) + ", " + node.stype c = 'w' if rw < rh: o = "vertical" else: o = "horizontal" else: t = node.name if node.isfile: c = 'k' o = 45 else: return self.ax.annotate(t, (cx, cy), color=c, weight='bold', ha='center', va='center', rotation=o)
def draw_rectangle(self, lower, upper, node): r = Rectangle( lower, upper[0]-lower[0], upper[1] - lower[1], edgecolor='k', facecolor= node.get_color(), label=node.name) self.ax.add_patch(r) rx, ry = r.get_xy() rw = r.get_width() rh = r.get_height() cx = rx + rw/2.0 cy = ry + rh/2.0 if isinstance(node, PathNode): t = node.name if rw * 3 < rh: t += ", " else: t += "\n" t += str(node.size) + ", " + node.stype c='w' if rw < rh: o = "vertical" else: o = "horizontal" else: t = node.name if node.isfile: c='k' o = 45 else: return self.ax.annotate( t, (cx,cy), color=c, weight='bold', ha='center', va='center', rotation=o )
def plot_rectangle(startTime, endTime, low, high, color, text): #print("plot rectangle: startTime {} endTime {} low {} high {} color {}".format(startTime, endTime, low, high, color)) from matplotlib.patches import Rectangle #convert to matplotlib date representation start = mdates.date2num(startTime) end = mdates.date2num(endTime) width = (end - start) height = high - low rect = Rectangle((start, low), width, height, color=color, alpha=0.4) rx, ry = rect.get_xy() cx = rx + rect.get_width() / 2.0 cy = ry + rect.get_height() / 2.0 ax = plt.gca() ax.annotate(text, (cx, cy), color='w', weight='bold', fontsize=6, ha='center', va='center') ax.add_patch(rect)
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 main_gui(wx.Frame): '''Setting up the placement and ''' def __init__(self, parent, file_path): if args.debug: print('main_gui.__init__') self.args = args self.initialize_controls(parent) self.box_sizer.Add(self.panel1, 0, wx.EXPAND) ## Initialize GUI plot self.figure = Figure() self.canvas = FigureCanvas(self, -1, self.figure) self.box_sizer.Add(self.canvas, 1, wx.EXPAND) ## Default rectangle variable self.pressed = False ## Initialize bottom text bar self.box_sizer.Add(self.status_bar, 0, border=0, flag=0) self.status_bar.SetStatusText('Ready', 0) rect = self.status_bar.GetFieldRect(0) ## Set up names self.video_file = file_path ## Create a dialog box at the beginning if the video path is not a real file if self.args.video_file == None: openFileDialog = wx.FileDialog(self, "Open Video file", "", "", "Video files (*.*)|*.*", wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) ## Closing program if Browse box is closed if openFileDialog.ShowModal() == wx.ID_CANCEL: print( '\nExiting Program. Must select a video from box or enter path into command line\n' ) raise SystemExit ## Setting video name from dialog box selection self.video_file = openFileDialog.GetPath() ## Passing individual inputs from GUI to a list self.update_names() self.input_names = [ 'x', 'y', 'w', 'h', 'check_frame', 'blank_0', 'blank_n', 'crop_0', 'crop_n', 'threshold', 'diameter', 'minmass', 'maxsize', 'ecc_low', 'ecc_high', 'vials', 'window', 'pixel_to_cm', 'frame_rate', 'vial_id_vars', 'outlier_TB', 'outlier_LR', 'naming_convention', 'path_project', 'file_suffix', 'convert_to_cm_sec', 'trim_outliers' ] self.parameter_names = [ 'self.input_' + item for item in self.input_names ] self.input_values = [ ## int self.input_x, self.input_y, self.input_w, self.input_h, self.input_check_frame, self.input_blank_0, self.input_blank_n, self.input_crop_0, self.input_crop_n, self.input_threshold, self.input_diameter, self.input_minmass, self.input_maxsize, self.input_ecc_low, self.input_ecc_high, self.input_vials, self.input_window, self.input_pixel_to_cm, self.input_frame_rate, self.input_vial_id_vars, self.input_outlier_TB, self.input_outlier_LR, ## str self.input_naming_convention, self.input_path_project, self.input_file_suffix, ## bool self.input_convert_to_cm_sec, self.input_checkBox_trim_outliers ] ## Enable all buttons button_list = [ 'browse_video', 'reload_video', 'test_parameters', 'store_parameters' ] for button in button_list: exec('self.button_' + button + '.Enable(True)') ## Load video self.load_video() self.status_bar.SetStatusText("Ready...", 0) if args.debug: print('End of init') return def update_variables(self): '''Updates the detection variables''' if args.debug: print('main_gui.update_variables') variables = [] ## Including integers for item, jtem in zip(self.input_names[:22], self.input_values[:22]): # print('int',item,jtem) phrase = str(item + '=' + jtem.GetValue()) if args.debug: print(' ' + phrase) variables.append(phrase) ## Including strings - type I for item, jtem in zip(self.input_names[22:24], self.input_values[22:24]): # print('str-I',item,jtem) phrase = str(item + '="' + jtem.GetValue() + '"') if args.debug: print(' ' + phrase) variables.append(phrase) ## Including strings - type II for item, jtem in zip(self.input_names[24:25], self.input_values[24:25]): # print('str-II',item,jtem) phrase = str(item + '="' + str(jtem) + '"') if args.debug: print(phrase) variables.append(phrase) ## Including booleans for item, jtem in zip(self.input_names[25:], self.input_values[25:]): # print('bool',item,jtem) phrase = str(item + '=%s' % str(jtem.GetValue())) if args.debug: print(' ' + phrase) variables.append(phrase) return variables def load_video(self): '''Function for loading the video when the respective button is pressed''' if args.debug: print('main_gui.load_video') ## Set up self.status_bar.SetStatusText("Loading video", 0) self.figure.clear() self.axes = [ self.figure.add_subplot(111), ] ## Confirm file is a path, or folder has specified suffix status = self.check_specified_video() if status: print("Loading:", self.video_file) self.update_names() for item in self.input_values[:4]: item.SetEditable(True) item.Enable(True) self.checkBox_fixed_ROI.Enable(True) self.input_convert_to_cm_sec.Enable(True) ## Busy cursor while the detector object is called and initialized wx.BeginBusyCursor() try: vars = self.update_variables() self.detector = detector(self.video_file, gui=True, variables=vars) self.axes[0].imshow(self.detector.image_stack[0]) self.figure.canvas.draw() finally: wx.EndBusyCursor() ## Setting mechanism for drawing the ROI rectangle self.canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor) self.canvas.mpl_connect('button_press_event', self.draw_rectangle) self.canvas.mpl_connect('button_release_event', self.on_release) self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.rect = Rectangle((0, 0), 1, 1, fill=False, ec='r') self.axes[0].add_patch(self.rect) ## Auto-set GUI parameters from the video self.input_blank_0.SetValue('0') self.input_blank_n.SetValue(str(self.detector.n_frames)) self.input_crop_0.SetValue('0') self.input_crop_n.SetValue(str(self.detector.n_frames)) self.input_check_frame.SetValue('0') self.input_ecc_low.SetValue('0') self.input_ecc_high.SetValue('1') self.input_ecc_high.SetValue('1') self.input_path_project.SetValue(self.folder) self.input_naming_convention.SetValue(self.name) self.input_vial_id_vars.SetValue( str(len(self.input_naming_convention.GetValue().split('_')))) ## Display the 0th and frame corresponding with (most likely) t = 2 seconds try: self.input_frame_rate = int(self.input_frame_rate.GetValue()) except: pass if self.detector.n_frames < self.input_frame_rate * 2: self.input_check_frame.SetValue(str(self.detector.n_frames)) ## Try to make the local linear regression window size 2 seconds, but if not then 35% of the frames in the video if self.detector.n_frames < self.input_frame_rate * 2: self.input_window.SetValue( str(int(len(self.detector.image_stack) * .35))) else: self.input_window.SetValue(str(int(self.input_frame_rate) * 2)) ## Enable Test parameter button if disabled from prior testing self.button_test_parameters.Enable(True) self.x0, self.y0 = 0, 0 self.x1, self.y1 = self.detector.width, self.detector.height ## Display the first frame of the video in the GUI self.update_ROIdisp() self.canvas.draw() else: return def update_names(self): '''Updates the names of variables within the program. Generally variables set for naming files.''' if args.debug: print('main_gui.update_names') self.status_bar.SetStatusText("Updating file names...", 0) self.text_video_path.SetLabelText(self.video_file) self.folder, self.name = os.path.split(self.video_file) self.name, self.input_file_suffix = self.name.split('.') ## Naming files to be generated self.name_noext = os.path.join(self.folder, self.name) self.path_data = self.name_noext + '.raw.csv' self.path_filter = self.name_noext + '.filter.csv' self.path_plot = self.name_noext + '.diag.png' self.path_slope = self.name_noext + '.slopes.csv' if args.debug: print('name:', self.name_noext, "+ file suffixes") ## Set path_project default to the folder of the selected video file if self.input_path_project == '': self.input_path_project = self.folder return def check_specified_video(self): if args.debug: print('main_gui.check_specified_video') self.status_bar.SetStatusText("Checking specified video...", 0) ## Check file path and update names if os.path.isfile(self.video_file): self.button_reload_video.Enable(True) self.button_test_parameters.Enable(True) self.update_names() self.input_file_suffix = '.' + self.video_file.split( '/')[-1].split('.')[-1] return True else: self.video_file = "No or invalid file entered. Please change the file path" self.button_browse_video.Enable(True) return False ## Commands for drawing the ROI rectangle def ChangeCursor(self, event): '''Change cursor into crosshair type when enter the plot area''' self.canvas.SetCursor(wx.Cursor(wx.CURSOR_CROSS)) return def draw_rectangle(self, event): '''Draw ROI rectangle''' self.status_bar.SetStatusText( "Draw rectangle from upper-left to lower-right", 0) self.pressed = True if self.checkBox_fixed_ROI.Enabled: try: self.x0 = int(event.xdata) self.y0 = int(event.ydata) ## If the fixed_ROI box is checked, handle values differently if self.checkBox_fixed_ROI.GetValue(): self.x1 = self.x0 + int(eval(self.input_w.GetValue())) self.y1 = self.y0 + int(eval(self.input_h.GetValue())) 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.canvas.draw() ## Set the values in the GUI and program to drawn rectangle self.input_x.SetValue(str(self.x0)) self.input_y.SetValue(str(self.y0)) self.input_h.SetValue(str(self.rect.get_height())) self.input_w.SetValue(str(self.rect.get_width())) except: pass return def on_release(self, event): '''When mouse is on plot and button is released, redraw ROI rectangle, update ROI values''' self.status_bar.SetStatusText("Specify the detector parameters...", 0) self.pressed = False if self.checkBox_fixed_ROI.Enabled: if self.checkBox_fixed_ROI.GetValue(): pass else: self.redraw_rect(event) self.update_ROIdisp() return def on_motion(self, event): '''If the mouse is on plot and if the mouse button is pressed, redraw ROI rectangle''' if self.pressed & self.checkBox_fixed_ROI.Enabled & ( not self.checkBox_fixed_ROI.GetValue()): # Redraw the rectangle self.redraw_rect(event) self.update_ROIdisp() return def redraw_rect(self, event): '''Draw the ROI rectangle overlay''' try: x1 = int(event.xdata) y1 = int(event.ydata) if any([self.x1 != x1, self.y1 != y1]): self.x1 = x1 self.y1 = y1 self.rect.set_xy((self.x0, self.y0)) self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.canvas.draw() else: pass except: pass return def update_ROIdisp(self): '''Updates the ROI coordinates as the rectangle is drawn.''' self.input_x.SetValue(str(self.x0)) self.input_y.SetValue(str(self.y0)) self.input_h.SetValue(str(int(self.y1) - int(self.y0))) self.input_w.SetValue(str(int(self.x1) - int(self.x0))) return def OnButton_testParButton(self, event): '''Tests the entered parameters when the `Test parameters` button is pressed''' if args.debug: print('main_gui.OnButton_testParButton') self.status_bar.SetStatusText("Testing parameters...", 0) #Prep the parameters variables = self.update_variables() self.checkBox_fixed_ROI.Enable(False) ## Set up figure for plots self.figure.clear() self.axes = [ self.figure.add_subplot(231), self.figure.add_subplot(232), self.figure.add_subplot(233), self.figure.add_subplot(234), self.figure.add_subplot(235), self.figure.add_subplot(236) ] ## Busy cursor while the main function runs wx.BeginBusyCursor() try: variables = variables + ['debug=' + str(args.debug)] self.detector.parameter_testing(variables, self.axes) finally: wx.EndBusyCursor() ## Renders plots in the GUI self.figure.tight_layout() self.figure.canvas.draw() # Enable buttons and print statements once parameter testing is complete self.button_reload_video.Enable(True) self.button_store_parameters.Enable(True) if args.debug: print('Parameter testing complete') self.status_bar.SetStatusText( "Refine detector parameters by reloading the video, or finish optimization by pressing 'Save configuration'", 0) return def OnButton_strParButton(self, event): '''Runs the 'save_parameter' function for creating the configuration file''' if args.debug: print('main_gui.OnButton_strParButton') self.save_parameter() self.button_store_parameters.SetBackgroundColour( wx.Colour(241, 241, 241)) def set_config_file(self): '''Set path for the project folder''' if args.debug: print('main_gui.OnButton_strParButton') ## Figure out where to save configuration file if os.path.isdir(self.input_path_project): if not self.input_path_project.endswith('/'): self.input_path_project = self.input_path_project + '/' self.path_parameters = self.input_path_project + self.name + '.cfg' else: self.path_parameters = self.path_noext + '.cfg' return self.path_parameters def save_parameter(self): if args.debug: print('main_gui.save_parameter') ''' Save parameters as python list. New parameter sets appended to the config file. Each parameter sets come with a comment line, contain the datetime of analysis ''' variables = self.update_variables() try: self.input_path_project = self.input_path_project.GetValue() except: pass self.path_parameters = self.set_config_file() ## Printing output to configuration file print('Saving parameters to:', self.path_parameters) with open(self.path_parameters, 'w') as f: print('## FreeClimber ##', file=f) f.close() now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open(self.path_parameters, 'a') as f: print('## Generated from file: ' + self.video_file, file=f) print('## @ ' + now, file=f) print('##', file=f) print('## Analysis parameters:', file=f) for item in variables: print(item, file=f) f.close() print("Configuration settings saved") return def OnButton_Browse(self, event): if args.debug: print('main_gui.OnButton_Browse') openFileDialog = wx.FileDialog(self, "Open Video file", "", "", "Video files (*.*)|*.*", wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if openFileDialog.ShowModal() == wx.ID_CANCEL: pass else: self.video_file = openFileDialog.GetPath() self.update_names() self.load_video() self.figure.clear() return def OnButton_LoadVideo(self, event): '''Calls function to load the video when the `reload` button is pressed''' if args.debug: print('main_gui.OnButton_LoadVideo') self.load_video() return def initialize_sizers(self): '''Initializes the GUI window orientation''' if args.debug: print('main_gui.initialize_sizers') # Generated method, do not edit self.box_sizer = wx.BoxSizer(orient=wx.VERTICAL) self.SetSizer(self.box_sizer) return def initialize_controls(self, prnt): '''Initializes the GUI controls, for which there are many''' if args.debug: print('main_gui.initialize_controls') # Generated method, do not edit wx.Frame.__init__(self, id=wxID_text_title, name='', parent=prnt, pos=wx.Point(100, 30), size=wx.Size(950, 759), style=wx.DEFAULT_FRAME_STYLE, title='FreeClimber') self.SetClientSize(wx.Size(950, 737)) ###### ## Inputs for ROI Rectangle self.panel1 = wx.Panel(id=wxID_panel_1, name='panel1', parent=self, pos=wx.Point(0, 0), size=wx.Size(950, 231), style=wx.TAB_TRAVERSAL) ## Step 1 boxes self.text_step_1a = wx.StaticText(id=wxID_text_step_1a, label=u'Step 1a: Specify a video', name='text_step_1a', parent=self.panel1, pos=wx.Point(col1, 10), size=wx.Size(box_dimensions), style=wx.ALIGN_CENTER) ## Browse self.button_browse_video = wx.Button(id=wxID_browse_video, label=u'Browse...', name=u'button_browse_video', parent=self.panel1, pos=wx.Point(col1, 30), size=wx.Size(box_dimensions), style=0) self.button_browse_video.Bind(wx.EVT_BUTTON, self.OnButton_Browse, id=wxID_browse_video) self.text_step_1b = wx.StaticText(id=wxID_text_step_1b, label=u'Step 1b: Define options', name='text_step_1b', parent=self.panel1, pos=wx.Point(col1, 65), size=wx.Size(box_dimensions), style=wx.ALIGN_CENTER) ## Pixel to cm self.text_pixel_to_cm = wx.StaticText( id=wxID_text_pixel_to_cm, label=u"Pixels / cm:", name='text_pixel_to_cm', parent=self.panel1, pos=wx.Point(col1, 85), size=wx.Size(medium_box_dimensions), style=0) self.input_pixel_to_cm = wx.TextCtrl( id=wxID_input_pixel_to_cm, name=u'input_pixel_to_cm', parent=self.panel1, pos=wx.Point(col1 + 95, 85), size=wx.Size(medium_box_dimensions), style=0, value=u"1") ## Frame Rate self.text_frame_rate = wx.StaticText(id=wxID_frame_rate, label=u'Frames / sec:', name='text_frame_rate', parent=self.panel1, pos=wx.Point(col1, 115), size=wx.Size(box_dimensions), style=0) self.input_frame_rate = wx.TextCtrl( id=wxID_frame_rate, name=u'input_frame_rate', parent=self.panel1, pos=wx.Point(col1 + 95, 115), size=wx.Size(medium_box_dimensions), style=0, value='25') ## Check box to convert final slope to cm self.input_convert_to_cm_sec = wx.CheckBox( id=wxID_input_convert_to_cm_sec, label=u'Convert to cm / sec', name=u'input_convert_to_cm_sec', parent=self.panel1, pos=wx.Point(col1, 145), size=wx.Size(250, 22), style=0) ## Step 2 boxes self.text_step_2 = wx.StaticText(id=wxID_text_step_2, label=u'Step 2: Select ROI', name='text_step_2', parent=self.panel1, pos=wx.Point(col2, 10), size=wx.Size(box_dimensions), style=wx.ALIGN_LEFT) ## X self.text_x = wx.StaticText(id=wxID_text_x, label=u'x-pos.', name='text_x', parent=self.panel1, pos=wx.Point(col2, 30), size=wx.Size(medium_box_dimensions), style=wx.ALIGN_LEFT) self.input_x = wx.TextCtrl(id=wxID_input_x, name=u'input_x', parent=self.panel1, style=0, value=u'0', pos=wx.Point(col2 + 55, 30), size=wx.Size(medium_box_dimensions)) ## Y self.text_y = wx.StaticText(id=wxID_text_y, label=u'y-pos.', name='text_y', parent=self.panel1, pos=wx.Point(col2, 55), size=wx.Size(medium_box_dimensions), style=wx.ALIGN_LEFT) self.input_y = wx.TextCtrl(id=wxID_input_y, name=u'input_y', parent=self.panel1, pos=wx.Point(col2 + 55, 55), size=wx.Size(medium_box_dimensions), style=0, value=u'0') ## Width self.text_w = wx.StaticText(id=wxID_text_w, label=u'Width:', name='text_w', parent=self.panel1, pos=wx.Point(col2, 80), size=wx.Size(medium_box_dimensions), style=wx.ALIGN_LEFT) self.input_w = wx.TextCtrl(id=wxID_input_w, name=u'input_w', parent=self.panel1, pos=wx.Point(col2 + 55, 80), size=wx.Size(medium_box_dimensions), style=0, value=u'0') ## Height self.text_h = wx.StaticText(id=wxID_text_h, label=u'Height:', name='text_h', parent=self.panel1, pos=wx.Point(col2, 105), size=wx.Size(medium_box_dimensions), style=wx.ALIGN_LEFT) self.input_h = wx.TextCtrl(id=wxID_input_h, name=u'input_h', parent=self.panel1, pos=wx.Point(col2 + 55, 105), size=wx.Size(medium_box_dimensions), style=0, value=u'0') ## ROI rectangle stays same dimensions but can be redrawn. Not critical to keep self.checkBox_fixed_ROI = wx.CheckBox(id=wxID_check_box_ROI, label=u'Fixed ROI Size?', name=u'checkBox_fixed_ROI', parent=self.panel1, pos=wx.Point(col2, 145), size=wx.Size(250, 22), style=0) self.checkBox_fixed_ROI.SetValue(False) ###### ## Detection parameters self.text_step_3 = wx.StaticText( id=wxID_text_step_3, label=u'Step 3: Specify spot parameters', name='text_step_3', parent=self.panel1, pos=wx.Point(col3, 10), size=wx.Size(100, 22), style=wx.ALIGN_LEFT) ## Expected spot diameter self.text_diameter = wx.StaticText(id=wxID_text_diameter, label=u'Diameter:', name='text_diameter', parent=self.panel1, pos=wx.Point(col3, 30), size=wx.Size(medium_box_dimensions), style=0) self.input_diameter = wx.TextCtrl(id=wxID_input_diameter, name=u'input_diameter', parent=self.panel1, pos=wx.Point(col3 + 100, 30), size=wx.Size(medium_box_dimensions), style=0, value=u'7') ## Maximum spot diameter self.text_maxsize = wx.StaticText(id=wxID_text_maxsize, label=u'MaxDiameter:', name='text_maxsize', parent=self.panel1, pos=wx.Point(col3, 55), size=wx.Size(medium_box_dimensions), style=0) self.input_maxsize = wx.TextCtrl(id=wxID_input_maxsize, name=u'input_maxsize', parent=self.panel1, pos=wx.Point(col3 + 100, 55), size=wx.Size(medium_box_dimensions), style=0, value=u'11') ## Minimum spot 'mass' self.text_minmass = wx.StaticText(id=wxID_text_minmass, label=u'MinMass:', name='text_minmass', parent=self.panel1, pos=wx.Point(col3, 80), size=wx.Size(medium_box_dimensions), style=0) self.input_minmass = wx.TextCtrl(id=wxID_input_minmass, name=u'input_minmass', parent=self.panel1, pos=wx.Point(col3 + 100, 80), size=wx.Size(medium_box_dimensions), style=0, value=u'100') ## Spot threshold self.text_threshold = wx.StaticText( id=wxID_text_threshold, label=u'Threshold:', name='text_threshold', parent=self.panel1, pos=wx.Point(col3, 105), size=wx.Size(medium_box_dimensions), style=0) self.input_threshold = wx.TextCtrl(id=wxID_input_threshold, name=u'input_threshold', parent=self.panel1, pos=wx.Point(col3 + 100, 105), size=wx.Size(medium_box_dimensions), style=0, value=u'"auto"') ## Eccentricity range self.text_ecc = wx.StaticText(id=wxID_text_ecc, label=u'Ecc/circularity:', name='text_ecc', parent=self.panel1, pos=wx.Point(col3, 130), size=wx.Size(medium_box_dimensions), style=0) self.input_ecc_low = wx.TextCtrl(id=wxID_input_ecc_low, name=u'input_ecc_low', parent=self.panel1, pos=wx.Point(col3 + 100, 130), size=wx.Size(small_box_dimensions), style=0, value=u'0') self.input_ecc_high = wx.TextCtrl(id=wxID_input_ecc_high, name=u'input_ecc_high', parent=self.panel1, pos=wx.Point(col3 + 140, 130), size=wx.Size(small_box_dimensions), style=0, value=u'0') #### Step 4 arguments ## Check frames self.text_step_4 = wx.StaticText( id=wxID_text_step_4, label=u'Step 4: Additional parameters', name='text_step_4', parent=self.panel1, pos=wx.Point(col4, 10), size=wx.Size(100, 22), style=wx.ALIGN_LEFT) ## Background frames self.text_background_frames = wx.StaticText( id=wxID_text_background_frames, label=u'Background frames:', name='text_background_frames', parent=self.panel1, pos=wx.Point(col4, 30), size=wx.Size(medium_box_dimensions), style=0) self.input_blank_0 = wx.TextCtrl(id=wxID_input_blank_0, name=u'input_blank_0', parent=self.panel1, pos=wx.Point(col4 + 130, 30), size=wx.Size(small_box_dimensions), style=0, value=u'0') self.input_blank_n = wx.TextCtrl(id=wxID_input_blank_n, name=u'input_blank_n', parent=self.panel1, pos=wx.Point(col4 + 170, 30), size=wx.Size(small_box_dimensions), style=0, value=u'0') ## crop frames self.text_crop_frames = wx.StaticText( id=wxID_text_crop_frames, label=u'Crop frames:', name='text_crop_frames', parent=self.panel1, pos=wx.Point(col4, 55), size=wx.Size(medium_box_dimensions), style=0) self.input_crop_0 = wx.TextCtrl(id=wxID_input_crop_0, name=u'input_crop_0', parent=self.panel1, pos=wx.Point(col4 + 130, 55), size=wx.Size(small_box_dimensions), style=0, value=u'0') self.input_crop_n = wx.TextCtrl(id=wxID_input_crop_n, name=u'input_crop_n', parent=self.panel1, pos=wx.Point(col4 + 170, 55), size=wx.Size(small_box_dimensions), style=0, value=u'0') ## Check frames self.text_check_frames = wx.StaticText(id=wxID_text_check_frames, label=u'Check frame:', name='text_check_frames', parent=self.panel1, pos=wx.Point(col4, 80), size=wx.Size(115, 17), style=0) self.input_check_frame = wx.TextCtrl( id=wxID_input_check_frame, name=u'input_check_frame', parent=self.panel1, pos=wx.Point(col4 + 130, 80), size=wx.Size(small_box_dimensions), style=0, value=u'0') ## Vials self.text_vials = wx.StaticText(id=wxID_text_vials, label=u'Number of vials:', name='text_vials', parent=self.panel1, pos=wx.Point(col4, 105), size=wx.Size(133, 22), style=0) self.input_vials = wx.TextCtrl(id=wxID_input_vials, name=u'input_vials', parent=self.panel1, pos=wx.Point(col4 + 130, 105), size=wx.Size(small_box_dimensions), style=0, value=u'1') ## Window size self.text_window = wx.StaticText(id=wxID_text_window, label=u'Window size:', name='text_window', parent=self.panel1, pos=wx.Point(col4, 130), size=wx.Size(133, 22), style=0) self.input_window = wx.TextCtrl(id=wxID_input_window, name=u'input_window', parent=self.panel1, pos=wx.Point(col4 + 130, 130), size=wx.Size(small_box_dimensions), style=0, value='1') ## Edge trim self.input_checkBox_trim_outliers = wx.CheckBox( id=wxID_check_box_outlier, label=u'Trim outliers? (TB LR)', name=u'checkBox_outlier', parent=self.panel1, pos=wx.Point(col4, 155), size=wx.Size(250, 22), style=0) self.input_checkBox_trim_outliers.SetValue(False) self.input_outlier_TB = wx.TextCtrl(id=wxID_outlier_TB, name=u'input_outlier_TB', parent=self.panel1, pos=wx.Point(col4 + 130, 155), size=wx.Size(small_box_dimensions), style=0, value=u'1') self.input_outlier_LR = wx.TextCtrl(id=wxID_outlier_LR, name=u'input_outlier_LR', parent=self.panel1, pos=wx.Point(col4 + 170, 155), size=wx.Size(small_box_dimensions), style=0, value=u'3') self.text_step_5 = wx.StaticText(id=wxID_text_step_5, label=u'Step 5: Naming parameters', name='text_step_5', parent=self.panel1, pos=wx.Point(col5, 10), size=wx.Size(100, 22), style=wx.ALIGN_LEFT) ## Naming convention self.text_naming_convention = wx.StaticText( id=wxID_text_naming_convention, label=u"Naming pattern:", name='text_naming_convention', parent=self.panel1, pos=wx.Point(col5, 30), size=wx.Size(medium_box_dimensions), style=0) self.input_naming_convention = wx.TextCtrl( id=wxID_input_naming_convention, name=u'input_naming_convention', parent=self.panel1, pos=wx.Point(col5, 50), size=wx.Size(large_box_dimensions), style=0, value='') ## Variables self.text_vial_id_vars = wx.StaticText(id=wxID_text_vial_id_vars, label=u'Vial_ID variables:', name='text_vial_id_vars', parent=self.panel1, pos=wx.Point(col5, 80), size=wx.Size(133, 22), style=0) self.input_vial_id_vars = wx.TextCtrl( id=wxID_input_vial_id_vars, name=u'input_vial_id_vars', parent=self.panel1, pos=wx.Point(col5 + 130, 80), size=wx.Size(small_box_dimensions), style=0, value=u'2') self.text_path_project = wx.StaticText( id=wxID_text_path_project, label=u"Project path:", name='text_path_project', parent=self.panel1, pos=wx.Point(col4 - 45, 180), size=wx.Size(medium_box_dimensions), style=0) self.input_path_project = wx.TextCtrl(id=wxID_input_path_project, name=u'input_path_project', parent=self.panel1, pos=wx.Point(col4 + 40, 180), size=wx.Size(350, 22), style=0, value='') ## Bottom panels self.text_video_path = wx.StaticText(id=wxID_video_path, label='Video Path', name='text_video_path', parent=self.panel1, pos=wx.Point(10, 205), size=wx.Size(930, 22), style=0) self.text_video_path.SetBackgroundColour(wx.Colour(241, 241, 241)) self.button_test_parameters = wx.Button(id=wxID_test_parameters, label=u'Test parameters', name=u'button_test_parameters', parent=self.panel1, pos=wx.Point(col1, 180), size=wx.Size(140, 22), style=wx.ALIGN_CENTER) self.button_test_parameters.Bind(wx.EVT_BUTTON, self.OnButton_testParButton, id=wxID_test_parameters) self.button_reload_video = wx.Button(id=wxID_reload_video, label=u'Reload video', name=u'button_reload_video', parent=self.panel1, pos=wx.Point(col1 + 160 * 1, 180), size=wx.Size(140, 22), style=wx.ALIGN_CENTER) self.button_reload_video.Bind(wx.EVT_BUTTON, self.OnButton_LoadVideo, id=wxID_reload_video) self.button_store_parameters = wx.Button( id=wxID_store_parameters, label=u'Save configuration', name=u'button_store_parameters', parent=self.panel1, pos=wx.Point(col1 + 160 * 2, 180), size=wx.Size(140, 22), style=wx.ALIGN_CENTER) self.button_store_parameters.Bind(wx.EVT_BUTTON, self.OnButton_strParButton, id=wxID_store_parameters) ## Text box at the bottom self.status_bar = wx.StatusBar(id=wxID_status_bar, name='status_bar', parent=self, style=0) self.initialize_sizers() return
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 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)
def handle_event(): global command global command_meta global main_pic global history global patch global patches global click_handlers global G if command == "horizontal_line" or command == "vertical_line": h, w = main_pic.shape[:2] if patch is not None: w1, h1 = patch.get_xy() if command == "horizontal_line": line = Line(0, int(h1), w, int(h1), int(patch.get_height()), magenta) else: line = Line(int(w1), 0, int(w1), h, int(patch.get_width()), magenta) main_pic = draw_line_on_picture(main_pic, line) patch = None else: if command == "horizontal_line": patch = Rectangle((0, 0), w, 1, edgecolor='magenta', alpha=1) else: patch = Rectangle((0, 0), 1, h, edgecolor='magenta', alpha=1) if command == "needle" or command == "angle_needle": G["needle"]["active"] = True just_added_patch = False if "pt1" in G["needle"] and "pt2" in G["needle"]: if patch is None: print "Drawing needle patch" pt1 = G["needle"]["pt1"] pt2 = G["needle"]["pt2"] if command == "needle": patch = Rectangle((pt1[0], pt1[1]), abs(pt2[0] - pt1[0]), abs(pt2[1] - pt1[1]), edgecolor='magenta', alpha=1, facecolor='magenta') else: patch = Polygon(np.array([pt1, pt2, p(pt1), p(pt2)]), closed=False, edgecolor='magenta', alpha=1, facecolor='magenta') angle = get_angle(pt1, pt2) print("Angle :{}".format(angle)) # how to add text? just_added_patch = True if patch is not None and not just_added_patch: if isinstance(patch, Polygon): patches.append(patch) patch = None else: print "finalize" w1, h1 = patch.get_xy() w = patch.get_width() h = patch.get_height() if w > h: print("horizontal patch") line = Line(int(w1), int(h1), int(w1 + w), int(h1), 3, magenta) else: line = Line(int(w1), int(h1), int(w1), int(h1 + h), 3, magenta) main_pic = draw_line_on_picture(main_pic, line) G["needle"] = {} if command == "divide": divide(command_meta.xdata, command_meta.ydata) if command == "brighten": main_pic = do_brighten(main_pic) if command == "mirror": main_pic = np.fliplr(main_pic) if command == "zoom": click_handlers = not click_handlers if command == "darken": main_pic = do_darken(main_pic) if command == "edge": main_pic = edge_detect(main_pic) if command == "resize_patch": if patch is not None: h = patch.get_height() w = patch.get_width() patch.set_width(int(w * 0.9)) patch.set_height(int(h * 0.9)) if command == "crop": if patch is not None: # apply patch # crop main_pic h = patch.get_height() w = patch.get_width() w1, h1 = patch.get_xy() main_pic = main_pic[slice(h1, h1 + h), slice(w1, w1 + w), slice(None)] patch = None else: # create patch # TODO: can read this from settings :)) portrait_ratio = 14.8 / 20.8 if orientation == "portrait": w_to_h = portrait_ratio else: w_to_h = 1.0 / portrait_ratio shape = main_pic.shape border = 15 hp = shape[0] - border wp = shape[1] - border if w_to_h * hp > wp: tw = wp th = wp / w_to_h else: th = hp tw = w_to_h * hp print th, tw patch = Rectangle((0, 0), tw, th, edgecolor='magenta', alpha=1, facecolor='none') if command == "undo": print "Undoing" print len(history) if len(history) >= 2: main_pic, cmd = history[-2] print cmd history = history[:-1] if command != "undo": history.append((np.copy(main_pic), command)) if command not in [ "crop", "horizontal_line", "vertical_line", "needle", "angle_needle", "resize_patch" ]: patch = None command = None command_meta = None plot(patch=patch, click_handlers=click_handlers) if command is not None: handle_event()
def handle_event(): global command global command_meta global main_pic global history global patch global patches global click_handlers global G if command=="horizontal_line" or command=="vertical_line": h,w = main_pic.shape[:2] if patch is not None: w1,h1 = patch.get_xy() if command=="horizontal_line": line = Line(0,int(h1),w,int(h1), int(patch.get_height()), magenta) else: line = Line(int(w1),0,int(w1),h, int(patch.get_width()), magenta) main_pic = draw_line_on_picture(main_pic, line) patch=None else: if command=="horizontal_line": patch = Rectangle((0,0), w, 1, edgecolor='magenta', alpha=1) else: patch = Rectangle((0,0), 1, h, edgecolor='magenta', alpha=1) if command=="needle" or command=="angle_needle": G["needle"]["active"] = True just_added_patch = False if "pt1" in G["needle"] and "pt2" in G["needle"]: if patch is None: print "Drawing needle patch" pt1 = G["needle"]["pt1"] pt2 = G["needle"]["pt2"] if command=="needle": patch = Rectangle((pt1[0], pt1[1]), abs(pt2[0]-pt1[0]), abs(pt2[1]-pt1[1]), edgecolor='magenta', alpha=1, facecolor='magenta') else: patch = Polygon(np.array([pt1, pt2, p(pt1), p(pt2)]), closed=False, edgecolor='magenta', alpha=1, facecolor='magenta') angle = get_angle(pt1, pt2) print ("Angle :{}".format(angle)) # how to add text? just_added_patch = True if patch is not None and not just_added_patch: if isinstance(patch, Polygon): patches.append(patch) patch=None else: print "finalize" w1,h1 = patch.get_xy() w = patch.get_width() h = patch.get_height() if w>h: print("horizontal patch") line = Line(int(w1),int(h1),int(w1+w),int(h1), 3, magenta) else: line = Line(int(w1),int(h1),int(w1),int(h1+h), 3, magenta) main_pic = draw_line_on_picture(main_pic, line) G["needle"] = {} if command == "divide": divide(command_meta.xdata, command_meta.ydata) if command == "brighten": main_pic = do_brighten(main_pic) if command == "mirror": main_pic = np.fliplr(main_pic) if command == "zoom": click_handlers = not click_handlers if command == "darken": main_pic = do_darken(main_pic) if command == "edge": main_pic = edge_detect(main_pic) if command == "resize_patch": if patch is not None: h = patch.get_height() w = patch.get_width() patch.set_width(int(w * 0.9)) patch.set_height(int(h * 0.9)) if command == "crop": if patch is not None: # apply patch # crop main_pic h = patch.get_height() w = patch.get_width() w1,h1 = patch.get_xy() main_pic = main_pic[slice(h1,h1+h),slice(w1,w1+w),slice(None)] patch=None else: # create patch # TODO: can read this from settings :)) portrait_ratio = 14.8/20.8 if orientation=="portrait": w_to_h = portrait_ratio else: w_to_h = 1.0/portrait_ratio shape = main_pic.shape border = 15 hp = shape[0] - border wp = shape[1] - border if w_to_h * hp >wp: tw = wp th = wp / w_to_h else: th = hp tw = w_to_h * hp print th,tw patch = Rectangle((0,0), tw, th, edgecolor='magenta', alpha=1, facecolor='none') if command == "undo": print "Undoing" print len(history) if len(history)>=2: main_pic,cmd = history[-2] print cmd history = history[:-1] if command!="undo": history.append((np.copy(main_pic),command)) if command not in ["crop","horizontal_line","vertical_line","needle","angle_needle","resize_patch"]: patch = None command = None command_meta = None plot(patch=patch, click_handlers=click_handlers) if command is not None: handle_event()
class PixelInteractor(QObject): epsilon = 10 showverts = True mySignal = pyqtSignal(str) modSignal = pyqtSignal(str) def __init__(self,ax,corner,width,angle=0.): super().__init__() from matplotlib.patches import Rectangle from matplotlib.lines import Line2D # from matplotlib.artist import Artist # To avoid crashing with maximum recursion depth exceeded import sys sys.setrecursionlimit(10000) # 10000 is 10x the default value self.type = 'Pixel' height = width self.ax = ax self.angle = angle self.width = width self.height = width # print('corner is ', corner) self.rect = Rectangle(corner,width,height,edgecolor='Lime',facecolor='none',angle=angle,fill=False,animated=True) self.ax.add_patch(self.rect) self.canvas = self.rect.figure.canvas x,y = self.compute_markers() self.line = Line2D(x, y, marker='s', linestyle=None, linewidth=0., markerfacecolor='g', animated=True) self.ax.add_line(self.line) self.cid = self.rect.add_callback(self.rectangle_changed) self._ind = None # the active point self.connect() self.aperture = self.rect self.press = None self.lock = None def compute_markers(self): # theta0 = self.rect.angle / 180.*np.pi w0 = self.rect.get_width() # h0 = self.rect.get_height() x0,y0 = self.rect.get_xy() angle0 = self.rect.angle x = [x0+w0/np.sqrt(2.)*np.sin((45.-angle0)*np.pi/180.)] y = [y0+w0/np.sqrt(2.)*np.cos((45.-angle0)*np.pi/180.)] self.xy = [(x,y)] return x, y def connect(self): self.cid_draw = self.canvas.mpl_connect('draw_event', self.draw_callback) self.cid_press = self.canvas.mpl_connect('button_press_event', self.button_press_callback) self.cid_release = self.canvas.mpl_connect('button_release_event', self.button_release_callback) self.cid_motion = self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) self.cid_key = self.canvas.mpl_connect('key_press_event', self.key_press_callback) self.canvas.draw_idle() def disconnect(self): self.canvas.mpl_disconnect(self.cid_draw) self.canvas.mpl_disconnect(self.cid_press) self.canvas.mpl_disconnect(self.cid_release) self.canvas.mpl_disconnect(self.cid_motion) self.canvas.mpl_disconnect(self.cid_key) self.rect.remove() self.line.remove() self.canvas.draw_idle() self.aperture = None def draw_callback(self, event): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self.rect) self.ax.draw_artist(self.line) def rectangle_changed(self, rect): 'this method is called whenever the polygon object is called' # only copy the artist props to the line (except visibility) vis = self.line.get_visible() Artist.update_from(self.line, rect) self.line.set_visible(vis) def get_ind_under_point(self, event): 'get the index of the point if within epsilon tolerance' x, y = self.xy[0] d = np.hypot(x - event.xdata, y - event.ydata) if d >= self.epsilon: ind = None else: ind = 0 return ind def button_press_callback(self, event): 'whenever a mouse button is pressed' if not self.showverts: return if event.inaxes is None: return if event.button != 1: return self._ind = self.get_ind_under_point(event) x0, y0 = self.rect.get_xy() w0, h0 = self.rect.get_width(), self.rect.get_height() theta0 = self.rect.angle/180*np.pi self.press = x0, y0, w0, h0, theta0, event.xdata, event.ydata self.xy0 = self.xy self.lock = "pressed" def key_press_callback(self, event): 'whenever a key is pressed' if not event.inaxes: return if event.key == 't': self.showverts = not self.showverts self.line.set_visible(self.showverts) if not self.showverts: self._ind = None elif event.key == 'd': self.mySignal.emit('rectangle deleted') self.canvas.draw_idle() def button_release_callback(self, event): 'whenever a mouse button is released' if not self.showverts: return if event.button != 1: return self._ind = None self.press = None self.lock = "released" self.background = None # To get other aperture redrawn self.canvas.draw_idle() def motion_notify_callback(self, event): 'on mouse movement' if not self.showverts: return if self._ind is None: return if event.inaxes is None: return if event.button != 1: return x0, y0, w0, h0, theta0, xpress, ypress = self.press self.dx = event.xdata - xpress self.dy = event.ydata - ypress self.update_rectangle() # Redraw rectangle and points self.canvas.restore_region(self.background) self.ax.draw_artist(self.rect) self.ax.draw_artist(self.line) self.canvas.update() self.canvas.flush_events() # alternative (slower) # self.canvas.draw_idle() # Notify callback self.modSignal.emit('rectangle modified') def update_rectangle(self): x0, y0, w0, h0, theta0, xpress, ypress = self.press dx, dy = self.dx, self.dy if self.lock == "pressed": self.lock = "move" elif self.lock == "move": if x0+dx < 0: xn = x0 dx = 0 else: xn = x0+dx if y0+dy < 0: yn = y0 dy = 0 else: yn = y0+dy self.rect.set_xy((xn,yn)) # update line self.xy = [(i+dx,j+dy) for (i,j) in self.xy0] # Redefine line self.line.set_data(zip(*self.xy)) self.updateMarkers() def updateMarkers(self): # update points x,y = self.compute_markers() self.line.set_data(x,y)
class Obstacle(Element): def __str__(self): pars = self.type, self.x_0, self.y_0, abs(self.x_1 - self.x_0), abs( self.y_1 - self.x_1), self._id, self._label fmt = "%s at (%g, %g) with width %g and height %g, ID: %s, Label: %s" return fmt % pars def __init__(self, x0, y0): Element.__init__(self) self.type = "Obstacle" self.x_0 = float(x0 if not Mode.snap_to_grid else round(x0)) self.y_0 = float(y0 if not Mode.snap_to_grid else round(y0)) self.x_1 = None self.y_1 = None self.drawing_started = False self.finalized = False self.rect = Rectangle((0, 0), 1, 1, zorder=2, picker=5) Graph.obstacles.append(self) def setID(self, ID=None): if ID == None: ## The ID of the obstacle simply is its index in the obstacles array self._id = "obst_" + str(getHighestIndex(Graph.obstacles) + 1).zfill(MAX_OBST) else: self._id = ID ## The label is initially assigned equal to the ID self.setLabel(self._id) def setLabel(self, label, drawNow=False, addGraphLabel=False): self._label = label if addGraphLabel: if self.matplotlib_label == None: x = self.x_0 + self.matplotlib_element.get_width() / 2 y = self.y_0 + self.matplotlib_element.get_height() / 2 self.matplotlib_label = self.axes.text( x, y, label, color='black', fontsize=FONTSIZE, bbox={ 'facecolor': 'white', 'edgecolor': 'none', 'alpha': 0.9, 'pad': 0.2 }, horizontalalignment='center', verticalalignment='center', zorder=2) else: self.matplotlib_label.set_text(label) if drawNow: self.canvas.draw() def addDrawing(self, axes, canvas, x1, y1, drawNow=False): self.axes = axes self.canvas = canvas if not self.drawing_started: self.axes.add_patch(self.rect) self.drawing_started = True if not self.finalized: x_1 = float(x1 if not Mode.snap_to_grid else round(x1)) y_1 = float(y1 if not Mode.snap_to_grid else round(y1)) self.rect.set_width(x_1 - self.x_0) self.rect.set_height(y_1 - self.y_0) self.rect.set_xy((self.x_0, self.y_0)) if drawNow: self.canvas.draw() def finalizeDrawing(self, x1, y1): self.x_1 = self.rect.get_width() + self.x_0 self.y_1 = self.rect.get_height() + self.y_0 self.matplotlib_element = self.rect def delete(self, now=True): ## Delete graphic elements # - Label if not self.matplotlib_label == None: self.matplotlib_label.remove() # - Obstacle if not self.matplotlib_element == None: self.matplotlib_element.remove() # Finally update the canvas if now: self.canvas.draw() ## Remove array elements Graph.obstacles.remove(self)
class usb1Windows(QWidget): def __del__(self): if hasattr(self, "camera"): self.camera.release() # 释放资源 def init_fun(self): self.window = Ui_Form() self.window.setupUi(self) self.timer = QTimer() # 定义一个定时器对象 self.timer.timeout.connect(self.timer_fun) #计时结束调用方法 # 1. open usb and show self.window.pushButton_2.clicked.connect(self.timer_start) # 2. catch one picture self.window.pushButton.clicked.connect(self.catch_picture) self.window.comboBox.currentIndexChanged.connect( self.set_width_and_height) self.window.checkBox.clicked.connect(self.get_faces_flag_fun) self.window.pushButton_5.clicked.connect(self.preview_picture) self.window.pushButton_4.clicked.connect(self.save_picture) self.window.pic_figure.canvas.mpl_connect('button_press_event', self.on_press) self.window.pic_figure.canvas.mpl_connect('button_release_event', self.on_release) self.getface_flag = False fm = open("./identiffun/faces.conf", 'r') self.names = fm.read().split(";") fm.close() self.my_get_face = Get_Faces(self.names) def on_press(self, event): self.on_x0 = event.xdata self.on_y0 = event.ydata if not hasattr(self, "rectload"): self.rectload = Rectangle((0, 0), 0, 0, linestyle='solid', fill=False, edgecolor='red') self.window.pic_figaxes.add_patch(self.rectload) def on_release(self, event): self.on_x1 = event.xdata self.on_y1 = event.ydata x_start = int(min(self.on_x0, self.on_x1)) x_end = int(max(self.on_x0, self.on_x1)) y_start = int(min(self.on_y0, self.on_y1)) y_end = int(max(self.on_y0, self.on_y1)) self.rectload.set_xy((x_start, y_start)) self.rectload.set_height(y_end - y_start + 1) self.rectload.set_width(x_end - x_start + 1) self.window.pic_figaxes.figure.canvas.draw() def save_picture(self): if hasattr(self, 'preview_res'): tmp_save_picture = self.preview_res else: if hasattr(self, 'raw_frame'): tmp_save_picture = self.raw_frame else: return # no pic cv2.imwrite("./image/save.jpg", tmp_save_picture) if hasattr(self, "rectload"): x, y = self.rectload.get_xy() w = self.rectload.get_width() h = self.rectload.get_height() cv2.imwrite("./image/ret.jpg", tmp_save_picture[y:y + h, x:x + w]) # filename, filetype = QFileDialog.getSaveFileName(self, "save", "jpg Files(*.jpg)::All Files(*)") # if filename: # cv2.imwrite(filename, tmp_save_picture) def preview_picture(self): if hasattr(self, 'raw_frame'): width = self.window.spinBox.value() height = self.window.spinBox_2.value() # self.raw_frame.reszie((width, height)) self.preview_res = cv2.resize(self.raw_frame, (width, height), interpolation=cv2.INTER_CUBIC) self.showimg2figaxes2(self.preview_res) def get_faces_flag_fun(self): if self.window.checkBox.isChecked(): self.getface_flag = True else: self.getface_flag = False # print(self.getface_flag) def set_width_and_height(self): # print(self.window.comboBox.currentText()) width, height = self.window.comboBox.currentText().split('*') if hasattr(self, "camera"): self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, int(width)) self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, int(height)) def catch_picture(self): if hasattr(self, "camera") and self.camera.isOpened(): ret, frame = self.camera.read() if ret: self.raw_frame = copy.deepcopy(frame) if hasattr(self, 'preview_res'): del self.preview_res self.showimg2figaxes2(frame) else: pass # get faild def timer_fun(self): ret, frame = self.camera.read() if ret: self.showimg2figaxes(frame) else: self.timer.stop() def timer_start(self): if hasattr(self, "camera"): if not self.camera.isOpened(): self.camera.open(0) # self.camera = cv2.VideoCapture(0) else: self.camera = cv2.VideoCapture(0) if self.camera.isOpened(): pass else: self.camera.open(0) # get width = self.camera.get(cv2.CAP_PROP_FRAME_WIDTH) print(width) height = self.camera.get(cv2.CAP_PROP_FRAME_HEIGHT) print(int(height)) self.window.comboBox.setCurrentText("%d*%d" % (int(width), int(height))) fps = self.camera.get(cv2.CAP_PROP_FPS) if fps == float('inf'): pass else: print(fps) brightness = self.camera.get(cv2.CAP_PROP_BRIGHTNESS) if brightness == float('inf'): self.window.doubleSpinBox_2.setValue(0.0) else: self.window.doubleSpinBox_2.setValue(brightness) contrast = self.camera.get(cv2.CAP_PROP_CONTRAST) if contrast == float('inf'): self.window.doubleSpinBox.setValue(0.0) else: self.window.doubleSpinBox.setValue(contrast) hue = self.camera.get(cv2.CAP_PROP_HUE) if hue == float('inf'): self.window.doubleSpinBox_3.setValue(0.0) else: self.window.doubleSpinBox_3.setValue(hue) exposure = self.camera.get(cv2.CAP_PROP_EXPOSURE) if exposure == float('inf'): self.window.doubleSpinBox_4.setValue(0.0) else: self.window.doubleSpinBox_4.setValue(exposure) # inf saturation = self.camera.get(cv2.CAP_PROP_SATURATION) if saturation == float('inf'): self.window.doubleSpinBox_5.setValue(0.0) else: self.window.doubleSpinBox_5.setValue(saturation) # inf self.timer.start(101) #设置计时间隔并启动 def showimg2figaxes2(self, frame): b, g, r = cv2.split(frame) imgret = cv2.merge([r, g, b]) if hasattr(self, "rectload"): self.rectload.remove() del self.rectload self.window.pic_figaxes.clear() self.window.pic_figaxes.imshow(imgret) self.window.pic_figure.canvas.draw() def showimg2figaxes(self, img): if self.getface_flag: tmp_img = self.my_get_face.get_face_fun(img) else: tmp_img = img b, g, r = cv2.split(tmp_img) imgret = cv2.merge([r, g, b]) # 这个就是前面说书的,OpenCV和matplotlib显示不一样,需要转换 self.window.video_figaxes.clear() self.window.video_figaxes.imshow(imgret) self.window.video_figure.canvas.draw()
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 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()
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 RectangleInteractor(QObject): epsilon = 5 showverts = True mySignal = pyqtSignal(str) modSignal = pyqtSignal(str) def __init__(self,ax,corner,width,height=None,angle=0.): super().__init__() from matplotlib.patches import Rectangle from matplotlib.lines import Line2D # from matplotlib.artist import Artist # To avoid crashing with maximum recursion depth exceeded import sys sys.setrecursionlimit(10000) # 10000 is 10x the default value if height is None: self.type = 'Square' height = width else: self.type = 'Rectangle' self.ax = ax self.angle = angle/180.*np.pi self.width = width self.height = height self.rect = Rectangle(corner,width,height,edgecolor='Lime',facecolor='none',angle=angle,fill=False,animated=True) self.ax.add_patch(self.rect) self.canvas = self.rect.figure.canvas x,y = self.compute_markers() self.line = Line2D(x, y, marker='o', linestyle=None, linewidth=0., markerfacecolor='g', animated=True) self.ax.add_line(self.line) self.cid = self.rect.add_callback(self.rectangle_changed) self._ind = None # the active point self.connect() self.aperture = self.rect self.press = None self.lock = None def compute_markers(self): theta0 = self.rect.angle / 180.*np.pi w0 = self.rect.get_width() h0 = self.rect.get_height() x0,y0 = self.rect.get_xy() c, s = np.cos(-theta0), np.sin(-theta0) R = np.matrix('{} {}; {} {}'.format(c, s, -s, c)) x = [0.5*w0, w0, 0.5*w0] y = [0.5*h0, 0.5*h0, h0] self.xy = [] x_ = [] y_ = [] for dx,dy in zip(x,y): (dx_,dy_), = np.array(np.dot(R,np.array([dx,dy]))) self.xy.append((dx_+x0,dy_+y0)) x_.append(dx_+x0) y_.append(dy_+y0) return x_,y_ def connect(self): self.cid_draw = self.canvas.mpl_connect('draw_event', self.draw_callback) self.cid_press = self.canvas.mpl_connect('button_press_event', self.button_press_callback) self.cid_release = self.canvas.mpl_connect('button_release_event', self.button_release_callback) self.cid_motion = self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) self.cid_key = self.canvas.mpl_connect('key_press_event', self.key_press_callback) self.canvas.draw_idle() def disconnect(self): self.canvas.mpl_disconnect(self.cid_draw) self.canvas.mpl_disconnect(self.cid_press) self.canvas.mpl_disconnect(self.cid_release) self.canvas.mpl_disconnect(self.cid_motion) self.canvas.mpl_disconnect(self.cid_key) self.rect.remove() self.line.remove() self.canvas.draw_idle() self.aperture = None def draw_callback(self, event): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self.rect) self.ax.draw_artist(self.line) def rectangle_changed(self, rect): 'this method is called whenever the polygon object is called' # only copy the artist props to the line (except visibility) vis = self.line.get_visible() Artist.update_from(self.line, rect) self.line.set_visible(vis) def get_ind_under_point(self, event): 'get the index of the point if within epsilon tolerance' x, y = zip(*self.xy) d = np.hypot(x - event.xdata, y - event.ydata) indseq, = np.nonzero(d == d.min()) ind = indseq[0] if d[ind] >= self.epsilon: ind = None return ind def button_press_callback(self, event): 'whenever a mouse button is pressed' if not self.showverts: return if event.inaxes is None: return if event.button != 1: return self._ind = self.get_ind_under_point(event) x0, y0 = self.rect.get_xy() w0, h0 = self.rect.get_width(), self.rect.get_height() theta0 = self.rect.angle/180*np.pi self.press = x0, y0, w0, h0, theta0, event.xdata, event.ydata self.xy0 = self.xy self.lock = "pressed" def key_press_callback(self, event): 'whenever a key is pressed' if not event.inaxes: return if event.key == 't': self.showverts = not self.showverts self.line.set_visible(self.showverts) if not self.showverts: self._ind = None elif event.key == 'd': #self.disconnect() #self.rect = None #self.line = None self.mySignal.emit('rectangle deleted') self.canvas.draw_idle() def button_release_callback(self, event): 'whenever a mouse button is released' if not self.showverts: return if event.button != 1: return self._ind = None self.press = None self.lock = "released" self.background = None # To get other aperture redrawn self.canvas.draw_idle() def motion_notify_callback(self, event): 'on mouse movement' if not self.showverts: return if self._ind is None: return if event.inaxes is None: return if event.button != 1: return x0, y0, w0, h0, theta0, xpress, ypress = self.press self.dx = event.xdata - xpress self.dy = event.ydata - ypress self.update_rectangle() # Redraw rectangle and points self.canvas.restore_region(self.background) self.ax.draw_artist(self.rect) self.ax.draw_artist(self.line) self.canvas.update() self.canvas.flush_events() # Notify callback self.modSignal.emit('rectangle modified') def update_rectangle(self): x0, y0, w0, h0, theta0, xpress, ypress = self.press dx, dy = self.dx, self.dy if self.lock == "pressed": if self._ind == 0: self.lock = "move" else: self.lock = "resizerotate" elif self.lock == "move": if x0+dx < 0: xn = x0 dx = 0 else: xn = x0+dx if y0+dy < 0: yn = y0 dy = 0 else: yn = y0+dy self.rect.set_xy((xn,yn)) # update line self.xy = [(i+dx,j+dy) for (i,j) in self.xy0] # Redefine line self.line.set_data(zip(*self.xy)) # otherwise rotate and resize elif self.lock == 'resizerotate': xc,yc = self.xy0[0] # center is conserved in the markers dtheta = np.arctan2(ypress+dy-yc,xpress+dx-xc)-np.arctan2(ypress-yc,xpress-xc) theta_ = (theta0+dtheta) * 180./np.pi c, s = np.cos(theta0), np.sin(theta0) R = np.matrix('{} {}; {} {}'.format(c, s, -s, c)) (dx_,dy_), = np.array(np.dot(R,np.array([dx,dy]))) # Avoid to pass through the center if self._ind == 1: w_ = w0+2*dx_ if (w0+2*dx_) > 0 else w0 if self.type == 'Square': h_ = w_ else: h_ = h0 elif self._ind == 2: h_ = h0+2*dy_ if (h0+2*dy_) > 0 else h0 if self.type == 'Square': w_ = h_ else: w_ = w0 # update rectangle self.rect.set_width(w_) self.rect.set_height(h_) self.rect.angle = theta_ # update markers self.updateMarkers() def updateMarkers(self): # update points x,y = self.compute_markers() self.line.set_data(x,y)
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 MplWidget(QtWidgets.QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.scroll = QtWidgets.QScrollArea(self) self.scroll.setParent(None) #self.fig =Figure(tight_layout=True) self.fig = Figure() left = 0.0 bottom = 0.0 width = 1 height = 1 self.fig.add_axes([left, bottom, width, height]) self.canvas = FigureCanvas(self.fig) self.fig.set_facecolor([0.23, 0.23, 0.23, 0.5]) self.canvas.axes = self.canvas.figure.gca() #self.canvas.figure.tight_layout(pad=0) self.vertical_layout = QVBoxLayout() self.vertical_layout.addWidget(self.canvas) self.mpl_toolbar = my_toolbar(self.canvas, self) self.mpl_toolbar.setParentClass(self) self.mpl_toolbar.setMinimumWidth(100) self.mpl_toolbar.setFixedHeight(26) self.mpl_toolbar.setStyleSheet( "QToolBar { opacity: 1;border: 0px; background-color: rgb(133, 196, 65); border-bottom: 1px solid #19232D;padding: 2px; font-weight: bold;spacing: 2px; } " ) self.mpl_toolbar.setObjectName("myToolBar") #self.canvas.mpl_connect("resize_event", self.resize) self.vertical_layout.addWidget(self.mpl_toolbar) self.setLayout(self.vertical_layout) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) self.rect = Rectangle((0, 0), 1, 1) self.updateSecondImage = None self.patchesTotal = 0 self.typeOfAnnotation = "autoDetcted" self.frameAtString = "Frame 0" self.currentSelectedOption = None self.AllBoxListDictionary = { "eraseBox": [], "oneWormLive": [], "multiWormLive": [], "oneWormDead": [], "multiWormDead": [], "miscBoxes": [], "autoDetcted": [] } self.eraseBoxXYValues = self.AllBoxListDictionary["eraseBox"] self.addBoxXYValues = self.AllBoxListDictionary["miscBoxes"] self.oneWormLiveBoxXYValues = self.AllBoxListDictionary["oneWormLive"] self.multiWormLiveBoxXYValues = self.AllBoxListDictionary[ "multiWormLive"] self.oneWormDeadBoxXYValues = self.AllBoxListDictionary["oneWormDead"] self.multiWormDeadBoxXYValues = self.AllBoxListDictionary[ "multiWormDead"] self.autoDetectedBoxXYValues = self.AllBoxListDictionary["autoDetcted"] self.tempList = [] def resetAllBoxListDictionary(self): self.AllBoxListDictionary = { "eraseBox": [], "oneWormLive": [], "multiWormLive": [], "oneWormDead": [], "multiWormDead": [], "miscBoxes": [], "autoDetcted": [] } def updateAllBoxListDictionary(self): self.AllBoxListDictionary["eraseBox"] = self.eraseBoxXYValues self.AllBoxListDictionary["miscBoxes"] = self.addBoxXYValues self.AllBoxListDictionary["oneWormLive"] = self.oneWormLiveBoxXYValues self.AllBoxListDictionary[ "multiWormLive"] = self.multiWormLiveBoxXYValues self.AllBoxListDictionary["oneWormDead"] = self.oneWormDeadBoxXYValues self.AllBoxListDictionary[ "multiWormDead"] = self.multiWormDeadBoxXYValues self.AllBoxListDictionary["autoDetcted"] = self.autoDetectedBoxXYValues def updateAllListFromAllBoxListDictionary(self): self.eraseBoxXYValues = self.AllBoxListDictionary["eraseBox"] self.addBoxXYValues = self.AllBoxListDictionary["miscBoxes"] self.oneWormLiveBoxXYValues = self.AllBoxListDictionary["oneWormLive"] self.multiWormLiveBoxXYValues = self.AllBoxListDictionary[ "multiWormLive"] self.oneWormDeadBoxXYValues = self.AllBoxListDictionary["oneWormDead"] self.multiWormDeadBoxXYValues = self.AllBoxListDictionary[ "multiWormDead"] self.autoDetectedBoxXYValues = self.AllBoxListDictionary["autoDetcted"] def setFrameAtString(self, text): self.frameAtString = text def getFrameAtString(self): return self.frameAtString def getCurrentSelectedOption(self): return self.currentSelectedOption def setCurrentSelectedOption(self, option): self.currentSelectedOption = option def setDarkTheme(self): self.mpl_toolbar.setStyleSheet( "QToolBar#myToolBar{ border: 0px; background-color: rgb(133, 0,s 65); border-bottom: 1px solid #19232D;padding: 2px; font-weight: bold;spacing: 2px; } " ) self.fig.set_facecolor([0.23, 0.23, 0.23, 0.5]) #self.fig.set_facecolor('grey') self.canvas.draw() def setGreenTheme(self): self.mpl_toolbar.setStyleSheet( "QToolBar { border: 0px; background-color: rgb(133, 196, 65); border-bottom: 1px solid #19232D;padding: 2px; font-weight: bold;spacing: 2px; } " ) self.fig.set_facecolor('grey') self.canvas.draw() def setTypeOfAnnotation(self, text): self.typeOfAnnotation = text def restrictCanvasMinimumSize(self, size): self.canvas.setMinimumSize(size) def unmountWidgetAndClear(self): self.vertical_layout.removeWidget(self.canvas) self.vertical_layout.removeWidget(self.scroll) self.scroll.setParent(None) self.canvas.setParent(None) sip.delete(self.scroll) del self.canvas self.scroll = None self.canvas = None self.canvas = FigureCanvas(Figure()) self.canvas.axes = self.canvas.figure.gca() #self.canvas.figure.tight_layout() self.scroll = QtWidgets.QScrollArea(self) self.scroll.setWidgetResizable(True) def connectClickListnerToCurrentImageForCrop(self, givenController, updateSecondImage=None, listOfControllers=None, keyForController=None): self.cid1 = self.canvas.mpl_connect("button_press_event", self.on_press_for_crop) self.cid2 = self.canvas.mpl_connect("motion_notify_event", self.onmove_for_crop) self.cid3 = self.canvas.mpl_connect("button_release_event", self.on_release_for_crop) self.givenControllerObject = givenController self.updateSecondImage = updateSecondImage self.pressevent = None self.listOfControllers = listOfControllers self.keyForController = keyForController def on_press_for_crop(self, event): if (self.mpl_toolbar.mode): return try: self.rect.remove() except: pass self.addedPatch = None self.x0 = event.xdata self.y0 = event.ydata self.rect = Rectangle((self.x0, self.y0), 1, 1) self.rect._alpha = 0.5 self.rect._linewidth = 2 self.rect.set_color("C2") self.rect.set self.pressevent = 1 self.addedPatch = self.canvas.axes.add_patch(self.rect) def on_release_for_crop(self, event): if (self.mpl_toolbar.mode): return self.pressevent = None minMaxVertices = [ int(np.ceil(min(self.x0, self.x1))), int(np.ceil(min(self.y0, self.y1))), int(np.round(max(self.x0, self.x1))), int(np.round(max(self.y0, self.y1))), ] self.givenControllerObject.updateManualCropCoordinates(minMaxVertices) image = self.givenControllerObject.showManualCropImage() self.canvas.axes.clear() self.canvas.axes.axis("off") self.canvas.axes.imshow(image) self.canvas.draw() if self.updateSecondImage is not None: self.updateSecondImage.canvas.axes.clear() self.updateSecondImage.canvas.axes.axis("off") self.updateSecondImage.canvas.axes.imshow( self.givenControllerObject.getCroppedImage(0)) self.updateSecondImage.canvas.draw() self.listOfControllers[ self.keyForController] = self.givenControllerObject def onmove_for_crop(self, event): if self.pressevent is None: 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.canvas.draw() def disconnectClickListnerFromCurrentImageForCrop(self): try: self.canvas.mpl_disconnect(self.cid1) self.canvas.mpl_disconnect(self.cid2) self.canvas.mpl_disconnect(self.cid3) self.updateSecondImage = None except: pass def getCurrentScrollParam(self): self.currentVerticalSliderValue = self.scroll.verticalScrollBar( ).value() self.currentHorizontalSliderValue = self.scroll.horizontalScrollBar( ).value() def resetCurrentScrollParam(self): self.scroll.verticalScrollBar().setValue( self.currentVerticalSliderValue) self.scroll.horizontalScrollBar().setValue( self.currentHorizontalSliderValue) def resize(self, event): # on resize reposition the navigation toolbar to (0,0) of the axes. x, y = self.fig.axes[0].transAxes.transform((0, 0)) figw, figh = self.fig.get_size_inches() ynew = figh * self.fig.dpi - y - self.mpl_toolbar.frameGeometry( ).height() self.mpl_toolbar.move(x, ynew) def connectClickListnerToCurrentImageForAnnotate(self, givenController, updateSecondImage=None): self.cid4 = self.canvas.mpl_connect("button_press_event", self.on_press_for_annotate) self.cid7 = self.canvas.mpl_connect('pick_event', self.onpick) #self.cid7 = self.canvas.mpl_connect('button_press_event', self.right_click_press_for_annotate) self.givenControllerObject = givenController self.updateSecondImage = updateSecondImage self.pressevent = None def autoAnnotateOnOverlay(self, autoDetectedObjects): for index, row in autoDetectedObjects.iterrows(): print(row.bbox3) #if self.pressevent is None: # return #self.x1 = event.xdata #self.y1 = event.ydata self.rect.set_width(row.bbox3 - row.bbox1) self.rect.set_height(row.bbox2 - row.bbox0) self.rect.set_xy((row.bbox1, row.bbox0)) self.canvas.draw() self.rect = Rectangle((row.bbox1, row.bbox0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) self.patchesTotal = self.patchesTotal + 1 if [row.bbox1, row.bbox0, row.bbox3, row.bbox2] not in self.autoDetectedBoxXYValues: self.autoDetectedBoxXYValues.append( [row.bbox1, row.bbox0, row.bbox3, row.bbox2]) # Update latest values self.updateAllBoxListDictionary() #print(self.typeOfAnnotation) '''if self.typeOfAnnotation == "eraseBox": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation not in ["eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead"]: if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1])''' #self.canvas.draw() #return(self.canvas) def on_press_for_annotate(self, event): # try: # self.rect.remove() # except: # pass if (self.mpl_toolbar.mode): return if event.button == 1: self.cid5 = self.canvas.mpl_connect("motion_notify_event", self.onmove_for_annotate) self.cid6 = self.canvas.mpl_connect("button_release_event", self.on_release_for_annotate) self.x0 = event.xdata self.y0 = event.ydata self.rect = Rectangle((self.x0, self.y0), 1, 1, picker=True) self.rect._alpha = 1 if self.typeOfAnnotation not in [ "eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead" ]: self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "autoDetcted": self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "eraseBox": self.rect._edgecolor = (0, 0, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "oneWormLive": self.rect._edgecolor = (0, 0, 1, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "multiWormLive": self.rect._edgecolor = (1, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "oneWormDead": self.rect._edgecolor = (1, 0, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "multiWormDead": self.rect._edgecolor = (1, 1, 1, 1) self.rect._facecolor = (0, 0, 0, 0) self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) self.patchesTotal = self.patchesTotal + 1 def on_release_for_annotate(self, event): if (self.mpl_toolbar.mode): return if event.button == 1: self.canvas.mpl_disconnect(self.cid5) if (self.rect.get_height() == 1) and (self.rect.get_width() == 1): self.rect.remove() self.pressevent = None self.canvas.mpl_disconnect(self.cid6) if self.typeOfAnnotation == "eraseBox": #print(self.typeOfAnnotation) self.eraseBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation not in [ "eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead" ]: #print(self.typeOfAnnotation) self.addBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "oneWormLive": self.oneWormLiveBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "multiWormLive": self.multiWormLiveBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "oneWormDead": self.oneWormDeadBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "multiWormDead": self.multiWormDeadBoxXYValues.append(self.tempList[-1]) self.tempList = [] # updateAllBoxListDictionary(self) self.updateAllBoxListDictionary() # self.givenControllerObject.updateManualCropCoordinates(minMaxVertices) # image = self.givenControllerObject.showManualCropImage() # self.canvas.axes.clear() # self.canvas.axes.axis("off") # self.canvas.axes.imshow(image) # self.canvas.draw() # if self.updateSecondImage is not None: # self.updateSecondImage.canvas.axes.clear() # self.updateSecondImage.canvas.axes.axis("off") # self.updateSecondImage.canvas.axes.imshow(self.givenControllerObject.getCroppedImage(0)) # self.updateSecondImage.canvas.draw() def onmove_for_annotate(self, event): if self.pressevent is None: 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)) #print(self.typeOfAnnotation) if self.typeOfAnnotation == "eraseBox": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation not in [ "eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead" ]: if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) self.canvas.draw() def getEraseBoxXYValues(self): return (self.eraseBoxXYValues) def getAutoDetctedBoxXYValues(self): return (self.autoDetectedBoxXYValues) def getAddBoxXYValues(self): return (self.addBoxXYValues) def getOneWormLiveBoxXYValues(self): return (self.oneWormLiveBoxXYValues) def getMultiWormLiveBoxXYValues(self): return (self.multiWormLiveBoxXYValues) def getOneWormDeadBoxXYValues(self): return (self.oneWormDeadBoxXYValues) def getMultiWormDeadBoxXYValues(self): return (self.multiWormDeadBoxXYValues) def resetEraseBoxXYValues(self): self.eraseBoxXYValues = [] def resetAutoDetctedBoxXYValues(self): self.autoDetectedBoxXYValues = [] def resetAddBoxXYValues(self): self.addBoxXYValues = [] def resetOneWormLiveBoxXYValues(self): self.oneWormLiveBoxXYValues = [] def resetMultiWormLiveBoxXYValues(self): self.multiWormLiveBoxXYValues = [] def resetOneWormDeadBoxXYValues(self): self.oneWormDeadBoxXYValues = [] def resetMultiWormDeadBoxXYValues(self): self.multiWormDeadBoxXYValues = [] def disconnectClickListnerFromCurrentImageForAnnotate(self): try: self.canvas.mpl_disconnect(self.cid4) self.canvas.mpl_disconnect(self.cid7) self.updateSecondImage = None except: pass def onpick(self, event): #if event.button == 3: #"3" is the right button # print "you click the right button" # print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%( # event.button, event.x, event.y, event.xdata, event.ydata) #Get the coordinates of the mouse click #I create the action if (self.mpl_toolbar.mode): return if event.mouseevent.button == 3: self.objectPicked = event.artist noteAction_1 = QtWidgets.QAction('Delete Box', self) noteAction_2 = QtWidgets.QAction('Classify', self) #noteAction_5 = QtWidgets.QAction('Add Once',self) #noteAction_2 = QtWidgets.QAction('Add Through',self) #noteAction_3 = QtWidgets.QAction('Mask Here',self) #noteAction_4 = QtWidgets.QAction('Mask Through',self) #noteAction_6 = QtWidgets.QAction('Live here',self) #noteAction_7 = QtWidgets.QAction('Live all',self) #noteAction_8 = QtWidgets.QAction('Dead here',self) #noteAction_9 = QtWidgets.QAction('Dead all',self) #I create the context menu self.popMenu = QtWidgets.QMenu(self) self.popMenu.addAction(noteAction_1) self.popMenu.addAction(noteAction_2) # self.popMenu.addAction(noteAction_2) # self.popMenu.addAction(noteAction_3) # self.popMenu.addAction(noteAction_4) # self.popMenu.addAction(noteAction_5) # self.popMenu.addAction(noteAction_6) # self.popMenu.addAction(noteAction_7) # self.popMenu.addAction(noteAction_8) # self.popMenu.addAction(noteAction_9) cursor = QtGui.QCursor() #self.connect(self.figure_canvas, SIGNAL("clicked()"), self.context_menu) #self.popMenu.exec_(self.mapToGlobal(event.globalPos())) noteAction_1.triggered.connect(lambda: self.removeThisArea(1)) noteAction_2.triggered.connect( lambda: self.classifyAsCurrentSelection(1)) # noteAction_2.triggered.connect(lambda :self.removeThisArea(2)) # noteAction_3.triggered.connect(lambda :self.removeThisArea(3)) # noteAction_4.triggered.connect(lambda :self.removeThisArea(4)) # noteAction_5.triggered.connect(lambda :self.removeThisArea(5)) # noteAction_6.triggered.connect(lambda :self.removeThisArea(5)) # noteAction_7.triggered.connect(lambda :self.removeThisArea(2)) # noteAction_8.triggered.connect(lambda :self.removeThisArea(3)) # noteAction_9.triggered.connect(lambda :self.removeThisArea(4)) self.popMenu.popup(cursor.pos()) else: return def right_click_press_for_annotate(self, event): if (self.mpl_toolbar.mode): return if event.button == 3: #"3" is the right button # print "you click the right button" # print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%( # event.button, event.x, event.y, event.xdata, event.ydata) #Get the coordinates of the mouse click #I create the action noteAction_1 = QtWidgets.QAction('Remove', self) noteAction_2 = QtWidgets.QAction('Add', self) #I create the context menu self.popMenu = QtWidgets.QMenu(self) self.popMenu.addAction(noteAction_1) self.popMenu.addAction(noteAction_2) cursor = QtGui.QCursor() #self.connect(self.figure_canvas, SIGNAL("clicked()"), self.context_menu) #self.popMenu.exec_(self.mapToGlobal(event.globalPos())) noteAction_1.triggered.connect( lambda eventData=object: self.removeThisArea(eventData)) noteAction_2.triggered.connect( lambda eventData=object: self.classifyAsCurrentSelection( eventData)) self.popMenu.popup(cursor.pos()) def classifyAsCurrentSelection(self, caseNumber): # Get all the list values for this frame self.updateAllListFromAllBoxListDictionary() print("INSIDE classifyAsCurrentSelection") try: if caseNumber == 1: # green delete print(type(self.objectPicked)) X0 = self.objectPicked.get_xy()[0] Y0 = self.objectPicked.get_xy()[1] X1 = X0 + self.objectPicked.get_width() Y1 = Y0 + self.objectPicked.get_height() selectedBoxCoords = [X0, Y0, X1, Y1] if self.currentSelectedOption == "eraseBox": #self.autoDetectedBoxXYValues.remove(selectedBoxCoords) #self.eraseBoxXYValues.append(selectedBoxCoords) print("Use Delte Option! Right Click -> Delete Box") if self.currentSelectedOption == "autoDetcted": #self.autoDetectedBoxXYValues.remove(selectedBoxCoords) #self.addBoxXYValues.append(selectedBoxCoords) print("Already Selected!") if self.currentSelectedOption not in [ "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead", "autoDetcted" ]: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.addBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) if self.currentSelectedOption == "oneWormLive" and selectedBoxCoords not in self.oneWormLiveBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.oneWormLiveBoxXYValues.append(selectedBoxCoords) self.canvas.draw() self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (0, 0, 1, 1) self.rect._facecolor = (0, 0, 0, 0) self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) self.canvas.draw() if self.currentSelectedOption == "multiWormLive" and selectedBoxCoords not in self.multiWormLiveBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.multiWormLiveBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (1, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) if self.currentSelectedOption == "oneWormDead" and selectedBoxCoords not in self.oneWormDeadBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.oneWormDeadBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (1, 0, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) if self.currentSelectedOption == "multiWormDead" and selectedBoxCoords not in self.multiWormDeadBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.multiWormDeadBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (1, 1, 1, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) except: print("Delete and Redraw!") # updateAllBoxListDictionary(self) self.updateAllBoxListDictionary() def removeThisArea(self, caseNumber): # Get all the list values for this frame self.updateAllListFromAllBoxListDictionary() if caseNumber == 1: # green delete print(type(self.objectPicked)) X0 = self.objectPicked.get_xy()[0] Y0 = self.objectPicked.get_xy()[1] X1 = X0 + self.objectPicked.get_width() Y1 = Y0 + self.objectPicked.get_height() removeBoxCoords = [X0, Y0, X1, Y1] #print(removeBoxCoords) self.objectPicked.remove() self.patchesTotal = self.patchesTotal - 1 try: if removeBoxCoords in self.eraseBoxXYValues: self.eraseBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.addBoxXYValues: self.addBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.oneWormLiveBoxXYValues: #print(self.oneWormLiveBoxXYValues) self.oneWormLiveBoxXYValues.remove(removeBoxCoords) #print(self.oneWormLiveBoxXYValues) if removeBoxCoords in self.multiWormLiveBoxXYValues: self.multiWormLiveBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.oneWormDeadBoxXYValues: self.oneWormDeadBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.multiWormDeadBoxXYValues: self.multiWormDeadBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.autoDetectedBoxXYValues: print(len(self.autoDetectedBoxXYValues)) self.autoDetectedBoxXYValues.remove(removeBoxCoords) print(len(self.autoDetectedBoxXYValues)) except: pass # elif caseNumber == 2: # orange add all # self.objectPicked._facecolor = (1.0, 0.64, 0.0,0.5) # self.objectPicked._alpha = 0.5 # self.objectPicked.addName ="addAll" # elif caseNumber == 3: # black # self.objectPicked._facecolor = (0,0, 0, 0.8) # self.objectPicked._alpha = 0.8 # self.objectPicked.addName ="eraseBox" # elif caseNumber == 4: # self.objectPicked._facecolor = ( 0, 0, 0, 0.2) # self.objectPicked._alpha = 0.2 # self.objectPicked.addName ="deleteAll" # elif caseNumber == 5: # self.objectPicked.set_color("C2") # self._edgecolor = (0, 0, 0, 0) # self.objectPicked.addName ="addBox" self.canvas.draw() #print(len(self.canvas.axes.patches)) #self.canvas.draw() #self.on_release_for_annotate(None) def initializeAnnotationDictionary(self): self.currentAnnotationFrame = None self.annotationRecordDictionary = {} def updateAnnotationDictionary(self): # When you move away from current Frame call this previousFrame = self.currentAnnotationFrame if previousFrame is not None: self.annotationRecordDictionary[str( previousFrame)] = self.canvas.axes.patches def getAnnotationDictionary(self): return self.annotationRecordDictionary def applyAnnotationDictionary(self, frameNumber): self.currentAnnotationFrame = frameNumber self.canvas.axes.patches = [] if str(frameNumber) in self.annotationRecordDictionary.keys(): for patch in self.annotationRecordDictionary[str(frameNumber)]: self.canvas.axes.add_patch(patch) def setAnnotationDictionary(self): pass
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 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)
class viewer(graphplot): ''' Do: viewer = viewer(window) viewer.connect() To instantiate and use ''' def __init__(self, nodes_obj, window=None, figsize=None, idxlist=[], usekm=False): # window are the [xmin,xmax,ymin,ymax] dimensions of the viewer self.coordunits = "coords" if usekm: self.coordunits = "coords_km" # Create nodedict if nodes is a dataframe if "DataFrame" in str(type(nodes_obj)): nodedict = nodes_obj.to_dict("index") else: nodedict = nodes_obj self.figsize = (9, 9) if not figsize else figsize self.fig_main = plt.figure(figsize=self.figsize) self.grid = plt.GridSpec(2, 2, hspace=0.1, wspace=0.1, width_ratios=[2.2, 1], height_ratios=[0.8, 1]) self.axs = [self.fig_main.add_subplot(self.grid[:, 0])] self.axs.append(self.fig_main.add_subplot(self.grid[0, 1])) # Initialize parent graph object super().__init__(nodedict, idxlist, (self.figsize[0] * 0.75, self.figsize[1] * 0.75), self.fig_main, self.axs[0], usekm=usekm) self.axs[0].set_aspect('equal', adjustable='box', anchor="NW") # Draw main graph self.drawgraph() # Setup viewer if not window: # window = [self.xlims[0]+0.4*self.graphwidth, # self.xlims[0]+0.6*self.graphwidth, # self.ylims[0]+0.4*self.graphheight, # self.ylims[0]+0.6*self.graphheight] dw = 1. / long2km if usekm: dw = 1. window = [ self.xlims[0] + 0.5 * self.graphwidth - dw, self.xlims[0] + 0.5 * self.graphwidth + dw, self.ylims[0] + 0.5 * self.graphheight - dw, self.ylims[0] + 0.5 * self.graphheight + dw ] xmin, xmax, ymin, ymax = window[0], window[1], window[2], window[3] self.window = Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, fill=False, linewidth=1.6, edgecolor="orangered") self.axs[0].add_patch(self.window) # Create inset axis xmin, ymin, xmax, ymax = self.get_rect_coords(self.window) self.axs[1].set_ylim(ymin, ymax) self.axs[1].set_xlim(xmin, xmax) self.axs[1].set_aspect('equal', adjustable='box', anchor="NE") self.axs[1].set_xticks([]) self.axs[1].set_yticks([]) self.press = None self.updateinset() def updateinset(self): # Remove any artists if present self.axs[1].clear() # Collect which nodes are included insetnodes = {} xmin, ymin, xmax, ymax = self.get_rect_coords(self.window) for key, node in self.nodes.items(): x, y = node[self.coordunits] if x > xmin and x < xmax and y > ymin and y < ymax: insetnodes.update({key: node}) self.plotnode(self.axs[1], x, y) edges = node["nbrs"] for e in edges: if e in self.idxlist: nbr = self.nodes[e][self.coordunits] self.plotLine(self.axs[1], x, y, nbr[0], nbr[1]) self.axs[1].set_ylim(ymin, ymax) self.axs[1].set_xlim(xmin, xmax) #self.axs[1].set_aspect('equal', adjustable='box',anchor="NE") self.axs[1].set_xticks([]) self.axs[1].set_yticks([]) # pickle.dump(self., file('myplot.pickle', 'w')) def connect(self): 'connect to all the events we need' self.cidpress = self.window.figure.canvas.mpl_connect( 'button_press_event', self.on_press) self.cidrelease = self.window.figure.canvas.mpl_connect( 'button_release_event', self.on_release) self.cidmotion = self.window.figure.canvas.mpl_connect( 'motion_notify_event', self.on_motion) def on_press(self, event): 'on button press we will see if the mouse is over us and store some data' if event.inaxes != self.window.axes: return contains, attrd = self.window.contains(event) self.window.set_x(event.xdata - self.window.get_width() * 0.5) self.window.set_y(event.ydata - self.window.get_height() * 0.5) if True: # Update attributes for motion print('event contains', self.window.xy) x0, y0 = self.window.xy self.press = x0, y0, event.xdata, event.ydata def on_motion(self, event): 'on motion we will move the window if the mouse is over us' if self.press is None: return if event.inaxes != self.window.axes: return x0, y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress #print('x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f' % # (x0, xpress, event.xdata, dx, x0+dx)) self.window.set_x(x0 + dx) self.window.set_y(y0 + dy) self.window.figure.canvas.draw() def on_release(self, event): 'on release we reset the press data' self.press = None self.updateinset() self.window.figure.canvas.draw() def disconnect(self): 'disconnect all the stored connection ids' self.window.figure.canvas.mpl_disconnect(self.cidpress) self.window.figure.canvas.mpl_disconnect(self.cidrelease) self.window.figure.canvas.mpl_disconnect(self.cidmotion)
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()
class LensGUI: def __init__(self,parent): self.root = Tk.Tk() self.parent = parent self.img = self.parent.img self.color = self.parent.color self.mover = None f1 = Figure((12.06,12.06)) a1 = f1.add_axes([0,101./201,100./201,100./201]) self.img1 = a1.imshow(self.img,origin='bottom',interpolation='nearest') a1.set_xticks([]) a1.set_yticks([]) xlim = a1.get_xlim() ylim = a1.get_ylim() a2 = f1.add_axes([101./201,101./201,100./201,100./201]) self.img2 = a2.imshow(self.img,origin='bottom',interpolation='nearest') a2.set_xlim(xlim) a2.set_ylim(ylim) a2.set_xticks([]) a2.set_yticks([]) a3 = f1.add_axes([0.,0.,100./201,100./201]) self.img3 = a3.imshow(self.img*0+1,origin='bottom',interpolation='nearest') a3.set_xlim(xlim) a3.set_ylim(ylim) a3.set_xticks([]) a3.set_yticks([]) a4 = f1.add_axes([101./201,0.,100./201,100./201]) a4.imshow(self.parent.b*0) a4.cla() a4.set_xlim(xlim) a4.set_ylim(ylim) a4.set_xticks([]) a4.set_yticks([]) canvas = FigureCanvasTkAgg(f1,master=self.root) canvas.show() canvas.get_tk_widget().pack(side=Tk.TOP,fill=Tk.BOTH,expand=1) toolbar = NavigationToolbar2TkAgg(canvas,self.root ) toolbar.update() canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) bFrame = Tk.Frame(self.root) bFrame.pack(side=Tk.TOP,fill=Tk.BOTH,expand=1) self.f1 = f1 self.a1 = a1 self.a2 = a2 self.a3 = a3 self.a4 = a4 self.bFrame = bFrame self.canvas = canvas self.toolbar = toolbar self.rubberBox = None self.addButtons() def addButtons(self): self.activeButton = None self.bAGtext = Tk.StringVar() self.bAGtext.set('Add Galaxy') self.buttonAG = Tk.Button(self.toolbar,textvariable=self.bAGtext,command=self.parent.addGal,width=10) self.buttonAG.pack(side=Tk.LEFT) self.bALtext = Tk.StringVar() self.bALtext.set('Add Lens') self.buttonAL = Tk.Button(self.toolbar,textvariable=self.bALtext,command=self.parent.addLens,width=10) self.buttonAL.pack(side=Tk.LEFT) self.bAStext = Tk.StringVar() self.bAStext.set('Add Source') self.buttonAS = Tk.Button(self.toolbar,textvariable=self.bAStext,command=self.parent.addSrc,width=10) self.buttonAS.pack(side=Tk.LEFT) self.buttonAS.configure(state='disabled') self.buttonFit = Tk.Button(self.toolbar,text='Fit Light',command=self.parent.fitLight,width=10) self.buttonFit.pack(side=Tk.LEFT) #self.buttonFit.configure(state='disabled') self.bOpttext = Tk.StringVar() self.bOpttext.set('Optimize') self.buttonOptimize = Tk.Button(self.toolbar,textvariable=self.bOpttext,command=self.parent.optimize,width=10) self.buttonOptimize.pack(side=Tk.LEFT) #self.buttonOptimize.configure(state='disabled') self.buttonSave = Tk.Button(self.bFrame,text='Save',command=self.parent.saveState,width=10) self.buttonSave.pack(side=Tk.LEFT) self.buttonLoad = Tk.Button(self.bFrame,text='Load',command=self.parent.loadState,width=10) self.buttonLoad.pack(side=Tk.LEFT) self.bAMtext = Tk.StringVar() self.bAMtext.set('Add Mask') self.buttonMask = Tk.Button(self.bFrame,textvariable=self.bAMtext,command=self.addMask,width=10) self.buttonMask.pack(side=Tk.LEFT) def deactivateButtons(self): if self.toolbar.mode!='': self.toolbar.zoom() self.toolbar.pan() self.toolbar.pan() if self.activeButton==self.buttonAG: self.bAGtext.set('Add Galaxy') self.canvas.mpl_disconnect(self.pressid) elif self.activeButton==self.buttonAL: self.bALtext.set('Add Lens') self.canvas.mpl_disconnect(self.pressid) elif self.activeButton==self.buttonAS: self.bAStext.set('Add Source') self.canvas.mpl_disconnect(self.pressid) elif self.activeButton==self.buttonMask: self.bAMtext.set('Add Mask') self.canvas.mpl_disconnect(self.pressid) self.canvas.mpl_disconnect(self.moveid) self.canvas.mpl_disconnect(self.releaseid) self.pressid = None self.releaseid = None self.activeButton = None def addMask(self,loaded=False): from matplotlib.patches import Rectangle if loaded and self.parent.mask is not None: import numpy y,x = numpy.where(self.parent.mask==1) x0,x1,y0,y1 = x.min(),x.max(),y.min(),y.max() self.rubberBox = Rectangle((x0,y0),x1-x0,y1-y0,fc='none',ec='w') self.a1.add_patch(self.rubberBox) self.canvas.draw() return if self.activeButton==self.buttonMask: self.deactivateButtons() return self.deactivateButtons() self.xmask = None def onPress(event): axes = event.inaxes if axes==self.a1: self.xmask = event.xdata self.ymask = event.ydata if self.rubberBox is not None: self.rubberBox.remove() self.rubberBox = None def onMove(event): if self.xmask is None: return axes = event.inaxes if axes==self.a1: x,y = event.xdata,event.ydata dx = x-self.xmask dy = y-self.ymask if self.rubberBox is None: self.rubberBox = Rectangle((self.xmask,self.ymask), dx,dy,fc='none',ec='w') self.a1.add_patch(self.rubberBox) else: self.rubberBox.set_height(dy) self.rubberBox.set_width(dx) self.canvas.draw() def onRelease(event): dy = int(self.rubberBox.get_height()) dx = int(self.rubberBox.get_width()) x0,y0 = int(self.xmask),int(self.ymask) x1,y1 = x0+dx,y0+dy self.parent.mask = self.parent.imgs[0]*0 self.parent.mask[y0:y1,x0:x1] = 1 self.parent.mask = self.parent.mask==1 self.deactivateButtons() self.pressid = self.canvas.mpl_connect('button_press_event',onPress) self.moveid = self.canvas.mpl_connect('motion_notify_event',onMove) self.releaseid = self.canvas.mpl_connect('button_release_event',onRelease) self.bAMtext.set('Cancel') self.activeButton = self.buttonMask def showResid(self): if self.parent.models is None: self.a2.imshow(self.parent.img,origin='bottom', interpolation='nearest') self.a3.cla() self.a3.set_xticks([]) self.a3.set_yticks([]) self.canvas.show() return models = self.parent.models imgs = self.parent.imgs nimgs = self.parent.nimgs if self.color is not None: if nimgs==2: b = imgs[0]-models[0] r = imgs[1]-models[1] g = (b+r)/2. resid = self.color.colorize(b,g,r) b = models[0] r = models[1] g = (b+r)/2. model = self.color.colorize(b,g,r,newI=True) else: b = imgs[0]-models[0] g = imgs[1]-models[1] r = imgs[2]-models[2] resid = self.color.colorize(b,g,r) b = models[0] g = models[1] r = models[2] model = self.color.colorize(b,g,r,newI=True) else: resid = imgs[0]-models[0] model = models[0] self.img3.set_clim([0.,model.max()]) #self.a2.imshow(resid,origin='bottom',interpolation='nearest') #self.a3.imshow(model,origin='bottom',interpolation='nearest') self.img2.set_data(resid) self.img3.set_data(model) self.canvas.draw() def redrawSymbols(self): import objectMover if self.mover is not None: self.mover.remove() self.mover = objectMover.ObjMover(self.parent,self.a4,self.canvas)