# 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)