class click_yrange: '''An interactive yrange selector. Given an axis and a starting y0 location, draw a full-width rectange that follows the mouise. Similar to click_window, but more appropriate for selecting out a y-range.''' def __init__(self, ax, y0): self.ax = ax self.y0 = y0 x0,x1 = ax.get_xbound() self.rect = Rectangle((x0,y0), width=(x1-x0), height=0, 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_height(event.ydata - self.y0) 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.y0, self.rect.get_y()+self.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_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 )
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()
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 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 CaseSelector(_my_SelectorWidget): """ Select a min/max range of the x or y axes for a matplotlib Axes. For the selector to remain responsive you much keep a reference to it. Example usage:: ax = subplot(111) ax.plot(x,y) def onselect(vmin, vmax): print vmin, vmax span = SpanSelector(ax, onselect, 'horizontal') *onmove_callback* is an optional callback that is called on mouse move within the span range """ def __init__(self, ax, onselect, onclick, minspan=0.1, nrect=10, update_on_ext_event=True, rectprops=None, stay_rectprops=None, lineprops=None, onmove_callback=None, button=None): """ Create a case selector in *ax*. When a selection is made, call *onselect* with:: onselect(vmin, vmax) and clear the span. *direction* must be 'horizontal' or 'vertical' If *minspan* is not *None*, ignore events smaller than *minspan* The span rectangle is drawn with *rectprops*; default:: rectprops = dict(facecolor='red', alpha=0.5) Set the visible attribute to *False* if you want to turn off the functionality of the span selector If *span_stays* is True, the span stays visble after making a valid selection. *button* is a list of integers indicating which mouse buttons should be used for selection. You can also specify a single integer if only a single button is desired. Default is *None*, which does not limit which button can be used. Note, typically: 1 = left mouse button 2 = center mouse button (scroll wheel) 3 = right mouse button """ _my_SelectorWidget.__init__(self, ax, onselect, update_on_ext_event, button=button) if rectprops is None: self.rectprops = dict(facecolor='#f5f5f5', alpha=0.3) else: self.rectprops = rectprops if lineprops is None: self.lineprops = dict(color='#e66101', lw=2) # bar color else: self.lineprops = lineprops if not isinstance(nrect, list): nrect = [nrect] if stay_rectprops is None: cc = ['#984ea3', '#ffff33', '#d8b365', '#5ab4ac'] # green yellow blue red color = [cc[i % len(cc)] for i in range(0, len(nrect))] self.stay_rectprops = [dict(facecolor=c, alpha=0.5) for c in color] else: assert (len(nrect) == len(stay_rectprops)) self.stay_rectprops = stay_rectprops self.pressv = None self.onclick = onclick self.onmove_callback = onmove_callback self.minspan = minspan # 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, nrect) def new_axes(self, ax, nrect): 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() # span trans = blended_transform_factory(self.ax.transData, self.ax.transAxes) w, h = 0, 1 self.rect = Rectangle((0, 0), w, h, transform=trans, visible=False, animated=True, **self.rectprops) self.ax.add_patch(self.rect) self.artists = [self.rect] # stay rect self.stay_rects = [] for set in range(0, len(nrect)): self.stay_rects.append([]) for n in range(0, nrect[set]): stay_rect = Rectangle((0, 0), w, h, transform=trans, visible=False, animated=True, **self.stay_rectprops[set]) self.ax.add_patch(stay_rect) self.stay_rects[set].append(stay_rect) self.artists.extend(self.stay_rects[set]) # bar self.bar = ax.axvline(0, w, h, visible=False, **self.lineprops) self.artists.append(self.bar) def set_bar_position(self, x): self.bar.set_xdata(x) self.bar.set_visible(True) def set_stay_rects_x_bounds(self, xarr, set=0): for n, stay_rect in enumerate(self.stay_rects[set]): try: xmin, xmax = xarr[n] except IndexError: stay_rect.set_visible(False) else: stay_rect.set_x(xmin) stay_rect.set_y(self.rect.get_y()) stay_rect.set_width(abs(xmax - xmin)) stay_rect.set_height(self.rect.get_height()) stay_rect.set_visible(True) def set_stay_rect_visible(self, b=True, set=0): for stay_rect in self.stay_rects[set]: stay_rect.set_visible(b) 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""" xdata, ydata = self._get_data(event) self.pressv = xdata return False def _release(self, event): """on button release event""" if self.pressv is None: return self.buttonDown = False self.rect.set_visible(False) vmin = self.pressv xdata, ydata = self._get_data(event) vmax = xdata or self.prev[0] if vmin > vmax: vmin, vmax = vmax, vmin span = vmax - vmin if span < self.minspan and event.button == 3: # right click to remove span self.onclick(vmin) return elif span > self.minspan and event.button == 1: self.onselect(vmin, vmax) self.pressv = None return False elif span > self.minspan and event.button == 3: self.onselect(vmin, vmax, True) self.pressv = None return False def _onmove(self, event): self.rect.set_visible(self.visible) """on motion notify event""" if self.pressv is None: return x, y = self._get_data(event) if x is None: return self.prev = x, y v = x minv, maxv = v, self.pressv if minv > maxv: minv, maxv = maxv, minv self.rect.set_x(minv) self.rect.set_width(maxv - minv) if self.onmove_callback is not None: vmin = self.pressv xdata, ydata = self._get_data(event) vmax = xdata or self.prev[0] if vmin > vmax: vmin, vmax = vmax, vmin self.onmove_callback(vmin, vmax) if not self.update_on_ext_event: self.update() return False
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)
class ZoneInteret: """ Cette classe permet de gérer les informations sur la zone d'interet Une zone interet est un cadre d'image sur l'image entière. On se concentre sur cette zone d'interet pour faire le traitement. C'est une manière de réduire le bruit sur le résultat de traitement @version 3.0 """ @staticmethod def verifier_presence_fichier_ini(): """ Vérifier si les fichiers de zone d'interet sont déjà présents dans le dossier :param: :returns: 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 :returns: """ #si le fichier ./zi/param.ini existe if os.path.isfile('./zi/param.ini'): try: #suppression de ces documents os.remove("./zi/param.ini") os.remove("./zi/image_modele.png") os.remove("./zi/image_zone_interet.png") #on informe l'utilisateur du succes de l'operation QMessageBox.information( window, "Information", "Supprimer la Zone d'intérêt avec succès", QMessageBox.Ok) except OSError: #on informe l'utilisateur de l'echec de l'operation QMessageBox.warning( window, "Erreur", "Impossible de supprimer les fichiers dans le repertoire /zi", QMessageBox.Ok) else: # si le fichier ./zi/param.ini n'existe pas QMessageBox.warning( window, "Erreur", "Impossible de trouver les fichiers dans le repertoire /zi", QMessageBox.Ok) def __init__(self, video): """ Initialise les variables necessaires à l'affichage de l'image et aux evenements :param video: la vidéo à traiter :returns: """ self.flag = False self.get_one_image_from_video(video) # On se sert de l'image extraite precedemment self.img = mpimg.imread('./zi/image_modele.png') # On initialise le titre de la fenetre 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 :returns: """ #coordonnees x de la zone cliquee self.x0 = event.xdata #coordonnees y de la zone cliquee self.y0 = event.ydata def on_mouseclick_release(self, event): """ Click gauche relâché -> dessin du rectangle :param event: évènement de souris :returns: """ #obtention des autres coordonnees pour dessiner le rectangle 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: """ #touche enter appuyée if event.key == 'enter': self.flag = True # on ecrit dans le fichier 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) # Enregistrement zone interet plt.title("Zone interet") plt.savefig("./zi/image_zone_interet.png") plt.close() def show_window(self): """ Pour afficher la fenetre qui est utilisee pour choisir une zone interet :param: :returns: """ plt.title( "Selectionnez la zone d'interet avec la souris. Appuyez sur entrer pour valider." ) # Affichage de la fenetre plt.show() def get_one_image_from_video(self, video): """ Extrait une image de la vidéo selectionnée Cette image est utilisée pour choisir une zone interet :param video: la vidéo choisie :return: """ video_capture = cv2.VideoCapture(video) nb_frame = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) video_capture.set(cv2.CAP_PROP_FRAME_COUNT, int(nb_frame - 1)) success, self.image = video_capture.read() #sauvegarder l'image cv2.imwrite("zi/image_modele.png", self.image)
class 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 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 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
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()