Source code for Cell

# CellTracking.py
# By MW, Jun 2013
# GPLv3+
#
# Class managing the display of a cell instance
import logging, cv2, numpy, gtk, imp, sys
sys.path.append('./bin/Features/')
import Features_gtk

Slider = imp.load_source("Slider", './bin/Players/Slider.py')

[docs]class GtkCell : """A generic class for handling cell creation (get polygon as successive clicks)""" def __init__(self, cells, delete_f, export_f, state) : self.delete_this_cell = delete_f# Forwarded function from the cells.gtk class self.export_progress_bar = export_f # Forwarded function from the cells.gtk class, allowing generic creation of an export window and progress bar. self.state = state self.cells = cells self.eps = 7 # A distance in pixels self.points = [] self.closed = False self.type = 'pol' self.cell = None self.panel = None self.selected = False self.r_panel = None self.l_panel = None #self.r_panel_inside = None self.radiobutton = None # The button on the cell l_panel self.radiobutton_none = None # The widget to select to uselect all self.visible = True self.selected_handle = None self.move_ref_with_cell = True self.reference_size = None self.deleted = False #self.features_gtk = Features_gtk.Features_gtk(cell.features, state)
[docs] def set_cell(self, cell) : """Set the cell (no gtk) when loading a savefile""" self.cell = cell self.closed = True self.features_gtk = Features_gtk.Features_gtk(cell.features, self.state)
[docs] def get_selected(self) : return self.selected
[docs] def set_r_panel(self, panel) : self.r_panel = panel
[docs] def set_selected(self, sel) : logging.debug("cell %s %s", self.get_name(), ['unselected', 'selected'][sel]) self.selected = sel self.radiobutton.set_active(sel) self.radiobutton.set_sensitive(self.visible) if not self.visible and sel: self.radiobutton_none.set_active(True)
[docs] def get_name(self) : """Return the name of the cell""" if self.closed : return self.cell.get_name() else : logging.warning("Trying to get the name of a temporary cell") return "TMP cell"
[docs] def rb_toggled(self, active) : self.selected = active if active : if self.r_panel.get_child() != None : self.r_panel.remove(self.r_panel.get_child()) mainbox = gtk.VBox() name = gtk.Label() name.set_markup("<big>Cell : %s</big>" % self.get_name()) mainbox.pack_start(name, fill=False, expand=False) # == Cell parameters parameters_frame = gtk.Frame("Cell") parameters_box = gtk.VBox() delete_b = gtk.Button("Delete cell") delete_b.connect("clicked", self.delete_cell_evt) parameters_box.pack_start(delete_b, fill=False, expand=False) parameters_frame.add(parameters_box) mainbox.pack_start(parameters_frame, fill=False, expand=False) # == Slider parameters slider_frame = gtk.Frame("Cell begin & end") slider_box = gtk.VBox() slider = Slider.Slider((0, self.state.get_frame_nb()-1)) slider.init_values(self.cell.get_bounds()) begin_b = gtk.Button("Set begin frame") end_b = gtk.Button("Set end frame") begin_b.connect('clicked', self.begend_evt, slider, 'begin') end_b.connect('clicked', self.begend_evt, slider, 'end') begend_box = gtk.HBox() begend_box.pack_start(begin_b) begend_box.pack_start(end_b) slider_p = slider.get_panel() slider_box.pack_start(begend_box, fill=True) slider_box.pack_start(slider_p, fill=True) slider_frame.add(slider_box) mainbox.pack_start(slider_frame, fill=False, expand=False) # == Reference frame reference_frame = gtk.Frame("Reference") reference_box = gtk.VBox() move_ref_with_cell_cb=gtk.CheckButton("Move reference with cell") reference_box.pack_start(move_ref_with_cell_cb, fill=False, expand=False) move_ref_with_cell_cb.connect("toggled", self.move_refwcell_evt) move_ref_with_cell_cb.set_active(True) bottom_box = gtk.HBox() refsize_l = gtk.Label("Reference size") refsize = self.reference_size if refsize == None : refsize = self.state.get_subclass('player').reference_size adj = gtk.Adjustment(refsize, 2, 100, 1, 5, 0) refsize_sb = gtk.SpinButton(adj) refsize_sb.connect('value-changed', self.refsize_sb_evt) reset_ref_b = gtk.Button("Reset reference") reset_ref_b.connect('clicked', self.reset_ref_evt) bottom_box.pack_start(refsize_l, fill=False, expand=False) bottom_box.pack_start(refsize_sb, fill=False, expand=False) bottom_box.pack_start(reset_ref_b, fill=False, expand=False) reference_box.pack_start(bottom_box, fill=False, expand=False) reference_frame.add(reference_box) mainbox.pack_start(reference_frame, expand=False, fill=False) # == Slider multi slidermulti_frame = gtk.Frame("Features") #f = Features_gtk.Features_gtk(self.cell.get_features(), self.state) f = self.features_gtk f.set_cell(self.cell) slidermulti_frame.add(f.get_panel((0, self.state.get_frame_nb()-1))) mainbox.pack_start(slidermulti_frame, expand=False, fill=False) # == Exports exports_frame = gtk.Frame('Export') export_box = gtk.HBox() quant_b = gtk.Button('Quantification') quant_b.set_tooltip_text("Export the cell quantification in CSV format") quant_b.connect('clicked', self.quant_evt) tiff_b = gtk.Button('TIFF series') tiff_b.set_tooltip_text("Exports the cell as a tiff series. Still EXPERIMENTAL") tiff_b.connect('clicked', self.tiff_evt) export_box.pack_start(quant_b, expand=False, fill=False) export_box.pack_start(tiff_b, expand=False, fill=False) exports_frame.add(export_box) mainbox.pack_start(exports_frame, expand=False, fill=False) mainbox.pack_start(gtk.Label(''), fill=True, expand=True) mainbox.show_all() #self.r_panel_inside = mainbox self.r_panel.add_with_viewport(mainbox) # == Bindings slider.connect('slider-changed', self.slider_changed_evt, f)
[docs] def get_panel(self, rb, rb_none) : """Return the panel to manage a cell rb is the radiobutton instance determining which cell is selected""" # Create expander and header self.panel = gtk.Expander(label="Cell: "+self.get_name()) self.radiobutton = rb self.radiobutton_none = rb header_cb = gtk.CheckButton("Show") header_cb.connect('clicked', self.show_evt) header_cb.set_active(bool(self.visible)) box = gtk.HBox() box.pack_start(header_cb, fill=False, expand=False) box.pack_start(gtk.VSeparator(), expand=False, fill=False) box.pack_start(self.panel, fill=False, expand=False) box.show_all() # Fill panel l = gtk.Label('Not implemented yer!') l.show() self.panel.add(l) return box
[docs] def show_evt(self, evt) : self.visible = evt.get_active() self.set_selected(evt.get_active())
[docs] def delete_cell_evt(self, evt) : """Function calling a forwarded function from Cells.gtk""" self.delete()
[docs] def delete(self) : self.delete_this_cell(self.get_name()) # (cells.gtk.delete_cell) self.cells.delete_cell(self.get_name()) self.visble = False
[docs] def unclick(self) : """Function called when the user click is released""" self.selected_handle = None
[docs] def click(self, tup) : """Function that receive a click from the viewer. This function returns True if the click lies inside the cell (core or reference polygon) In theory, the coordinates already have been converted""" if self.visible : i = self.state.get_subclass('player').get_current_index() in_c = self.cell.is_point_in_cell(tup, i) in_r = self.cell.is_point_in_ref(tup, i) if self.selected_handle == None : c_c = self.cell.is_point_on_cell_handle(tup, i) c_r = self.cell.is_point_on_ref_handle(tup, i) if c_c != None : self.selected_handle = ('cell', c_c) elif c_r != None : self.selected_handle = ('ref', c_r) if in_c or in_r or (self.selected_handle != None): # Select cell logging.debug("%s selected", str(self.get_name())) self.set_selected(True) return True else : #unselect cell logging.debug("%s unselected", str(self.get_name())) self.set_selected(False) return False return False
[docs] def move(self, tup) : """Function called when the cell is selected and the user tries to drag'n'drop it""" #print 'moving point %s' % self.selected_handle if self.selected_handle != None : i = self.state.get_subclass('player').get_current_index() if self.selected_handle[0] == 'cell' : tr = self.cell.cell_pol.move_point(self.selected_handle[1], tup,i) if self.move_ref_with_cell : self.cell.cell_ref.translate(tr, i) elif self.selected_handle[0] == 'ref' : tr = self.cell.cell_ref.move_point(self.selected_handle[1], tup,i) if self.move_ref_with_cell : self.cell.cell_pol.translate(tr, i) #if self.move_ref_with_cell : # refsize = self.state.get_subclass('player').reference_size # self.cell.update_ref(i, refsize)
[docs] def get_type(self) : """Return the cell type ('pol' for instance)""" return self.type
[docs] def add_point(self, tup) : if not self.closed : if (len(self.points)>=1) and (self.dist(self.points[0], tup)<self.eps) : self.close() # Close the polygon return False # erase tmp_cell else : self.points.append(tup) print "point added: %s" % str(tup) else : logging.warning("Trying to add a point to a closed polygon")
[docs] def reset_polygon(self) : self.points = [] self.state.get_subclass('player').set_current_frame(None) # updating display
[docs] def close(self) : """Function that close the polygon and create a new cell instance instead""" n = self.cells.get_name() # Get an unused name self.cell = self.cells.new_cell(str(n)) i = self.state.get_subclass('player').get_current_index() refsize = self.state.get_subclass('player').reference_size self.cell.reset_polygon(self.points, i) self.cell.init_ref(refsize) self.closed = True self.refsize = refsize self.features_gtk = Features_gtk.Features_gtk(self.cell.features, self.state)
[docs] def new_cell(self, name=None) : """Function that initialize an empty cell .. warning : You should immediately add a polygon to the cell as it doesn't like empty timetracks much... """ if name == None : name = self.cells.get_name() self.cell = self.cells.new_cell(str(name)) self.closed = True
[docs] def init_ref(self, refsize) : self.refsize = refsize self.cell.init_ref(refsize)
[docs] def dist(self, p1, p2) : s = 0 for i in range(len(p1)) : s += (p1[i]-p2[i])**2 return s**0.5
[docs] def draw_polygon(self, im) : """Draw the polygon on the image :param im: the image to draw on. :param type: a numpy matrix (x*y*3) :returns: numpy matrix -- the output image""" if self.visible and not self.deleted : if self.closed : i = self.state.get_subclass('player').get_current_index() (beg, end) = self.cell.get_bounds() if (i>=beg) and (i<=end) : points = self.cell.cell_pol.get_polygon(i) points_ref = self.cell.cell_ref.get_polygon(i) else : points = [] points_ref = [] if self.selected : color = (255,0,0) color_ref = (128,0,0) else : color = (0,0,255) color_ref = (0,0,128) points_ref = numpy.array(points_ref)*self.state. get_subclass('player').get_current_zoom() else : points = self.points color = (255,0,0) points = numpy.array(points)*self.state.get_subclass('player') .get_current_zoom() if len(points)==1 : # Draw first point if necessary p = points[0] cv2.circle(im, (int(p[0]), int(p[1])), 3, color=color) # Draw polygon #if self.points != [] : # Not sure whether this is required... if points != [] : cv2.polylines(im, [numpy.array(points, dtype=numpy.int32)], self.closed, color=color) if self.closed : cv2.polylines(im, [numpy.array(points_ref, dtype=numpy.int32)], self.closed, color=color_ref) center_pol = numpy.array(points).mean(axis=0) cv2.circle(im,tuple(numpy.int_(center_pol)), 5, color=color) if not self.move_ref_with_cell : center_ref = numpy.array(points_ref).mean(axis=0)#+numpy.array([10,0]) cv2.circle(im,tuple(numpy.int_(center_ref)), 5, color=color_ref) for p in points : cv2.circle(im, (int(p[0]), int(p[1])), 3, color=color) for p in points_ref : cv2.circle(im, (int(p[0]), int(p[1])), 3, color=color_ref) coord = (int(points[0][0]), int(points[0][1])-3) cv2.putText(im, self.get_name(), coord, cv2.FONT_HERSHEY_PLAIN, fontScale=1,color=color) return im
[docs] def move_refwcell_evt(self, evt) : self.move_ref_with_cell = evt.get_active() self.cell.center_ref = ['mean', None][evt.get_active()]
[docs] def reset_ref_evt(self, evt) : self.cell.init_ref(self.reference_size)
[docs] def refsize_sb_evt(self, evt) : """Function called when the value of the spinbutton "Set reference size" is changed""" self.reference_size = evt.get_value() self.cell.init_ref(self.reference_size)
[docs] def begend_evt(self, evt, slider, begend) : """Receives signals from the "Set begin frame" and "Set end frame" buttons and forward them to : 1. The slider 2. The cell object""" # Update slider dict_convert ={'begin': 'left', 'end': 'right'} i = self.state.get_subclass('player').get_current_index() slider.set_value(dict_convert[begend], i) if begend == 'begin' : self.cell.set_begin(i) elif begend == 'end' : self.cell.set_end(i)
[docs] def slider_changed_evt(self, w, features_gtk) : b = w.get_cur_value() self.cell.set_begin(b[0]) self.cell.set_end(b[1]) features_gtk.update()
[docs] def tiff_evt(self, w) : """Binding called when clicking on the "Export as TIFF series" button Many parameters exist, but they cannot be tuned for the moment This is used as a way to experiment for the quantification export This function should call a function displaying a progress bar, and call a cell object returning an iterator on the number of frames, such as in: http://stackoverflow.com/questions/496814/progress-bar-not-updating-during-operation or: http://faq.pygtk.org/index.py?req=show&file=faq23.020.htp """ # Get path logging.info("Saving tiff series as...") dialog = gtk.FileChooserDialog("Select output folder", action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) resp = dialog.run() f = None if resp == gtk.RESPONSE_OK : f = dialog.get_filename() dialog.destroy() state_player = self.state.get_subclass('player') state_player.set_lock(True) ch = state_player.get_current_channel() #self.cell.export_as_tiff(f, ch) self.export_progress_bar([(self.cell, ch, f)], 'tiff') state_player.set_lock(False)
[docs] def quant_evt(self, w) : """Binding called when clicking on the "Export quantification" button""" # Get path logging.info("Saving quantification as...") dialog = gtk.FileChooserDialog("Select output folder", action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) resp = dialog.run() f = None if resp == gtk.RESPONSE_OK : f = dialog.get_filename() dialog.destroy() state_player = self.state.get_subclass('player') state_player.set_lock(True) ch = state_player.get_current_channel() ##self.cell.export_as_csv(f, ch) # DEBUG self.export_progress_bar([(self.cell, ch, f)], 'csv') state_player.set_lock(False)
[docs]class GtkRect(GtkCell) : """A generic class to handle polygons as untilted rectangles""" def __init__(self, cells, state) : self.state = state TmpCell.__init__(self, cells)
[docs]class GtkRect_ct(GtkRect) : def __init__(self, cells, state) : self.state = state TmpCell.__init__(self, cells)
[docs]class GtkRect_tp(GtkRect) : def __init__(self, cells, state) : self.state = state TmpCell.__init__(self, cells)