Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion PYME/IO/tabular.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def _dep_name(*args, **kwargs):

return _dec

class TabularBase(object):
class _TabularBase(object):
_image_bounds = False

def toDataFrame(self, keys=None):
Expand Down Expand Up @@ -241,6 +241,46 @@ def image_bounds(self):
self._image_bounds = self._calc_image_bounds()

return self._image_bounds

@property
def channel_names(self):
return []

class _Channel(_TabularBase):
def __init__(self, table, channel_name, idx):
self._table = table
self._channel_names = [channel_name,]
self._idx = idx

def __getitem__(self, keys):
key, sl = self._getKeySlice(keys)

return self._table[key][self._idx][sl]

def keys(self):
return list(self._table.keys())

class TabularBase(_TabularBase):
"""Channel aware version of tabular base"""
_channel_column = 'channel_id'

def get_channel_ds(self, channel_name):
chan_column = getattr(self, '_channel_column', None)
if chan_column is None or (not chan_column in self.keys()) or len(self.channel_names) <1:
raise RuntimeError('Data set has no channels')

try:
ch_id = self.channel_names.index(channel_name)
except ValueError:
raise RuntimeError('Data does not have a "$s" channel' % channel_name)

return _Channel(self, channel_name, self[chan_column] == ch_id)

@property
def channel_names(self):
return [k[2:] for k in self.keys() if k.startswith('p_')]




# Data sources (File IO, or adapters to other data formats - e.g. recarrays
Expand Down
36 changes: 36 additions & 0 deletions PYME/recipes/localisations.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ class ProcessColour(ModuleBase):

ratios_from_metadata = Bool(True)

threshold_p_dye = Float(0.1)
threshold_p_other = Float(0.1)
threshold_p_background = Float(0.1)

def _get_dye_ratios_from_metadata(self, mdh):
from PYME.LMVis import dyeRatios

Expand Down Expand Up @@ -390,6 +394,23 @@ def _get_dye_ratios_from_metadata(self, mdh):
# #cached_output.mdh = output.mdh
# namespace[self.output] = cached_output

def _index(self, data, channel):
p_dye = data['p_%s' % channel]
p_other = 0 * p_dye
p_tot = self.threshold_p_background * data['ColourNorm']

for structure in self.species_ratios.keys():
p_struct = data['p_%s' % structure]
p_tot += p_struct

if not structure == channel:
p_other = np.maximum(p_other, p_struct)

p_dye /= p_tot
p_other /= p_tot

return (p_dye > self.threshold_p_dye) * (p_other < self.threshold_p_other)

def run(self, input):
mdh = input.mdh

Expand All @@ -411,21 +432,36 @@ def run(self, input):
if not ratio is None:
output.setMapping('p_%s' % structure,
'exp(-(%f - gFrac)**2/(2*error_gFrac**2))/(error_gFrac*sqrt(2*numpy.pi))' % ratio)

# Create a chanel ID column
chanID = -1*np.ones_like(output['gFrac'], dtype='i4')

for i, structure in enumerate(self.species_ratios.keys()):
chanID[self._index(output, structure)] = i

output.addColumn('channel_id', chanID)
else:
if 'probe' in output.keys():
#non-ratiometric (i.e. sequential) colour
#color channel is given in 'probe' column
output.setMapping('ColourNorm', '1.0 + 0*probe')
output.setMapping('channel_id', 'probe')

for i in range(int(output['probe'].min()), int(output['probe'].max() + 1)):
output.setMapping('p_chan%d' % i, '1.0*(probe == %d)' % i)

nSeqCols = mdh.getOrDefault('Protocol.NumberSequentialColors', 1)
if nSeqCols > 1:
t = output['t']
chanID = -1*np.ones_like(output['gFrac'], dtype='i4')
for i in range(nSeqCols):
output.setMapping('ColourNorm', '1.0 + 0*t')
cr = mdh['Protocol.ColorRange%d' % i]
chanID[(t >= cr[0])*(t < cr[1])] = i
output.setMapping('p_chan%d' % i, '(t>= %d)*(t<%d)' % cr)

output.addColumn('channel_id', chanID)


cached_output = tabular.CachingResultsFilter(output)
#cached_output.mdh = output.mdh
Expand Down
25 changes: 24 additions & 1 deletion PYME/recipes/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,37 @@ def __init__(self, msg, recipe=None):
def __reduce__(self):
return self.__class__, self.args

class NamespaceDict(dict):
def __getitem__(self, key):
parts = key.split('.')

if len(parts) == 2:
key, channel = parts

return super().__getitem__(key).get_channel_ds(channel)
else:
return super().__getitem__(key)

def __setitem__(self, key, value):
if '.' in key:
raise KeyError('Invalid key %s - namespace keys must not include "."' % key)

return super().__setitem__(key, value)

# def __getattr__(self, key):
# try:
# return self.__getitem__(key)
# except KeyError:
# raise AttributeError('No attribute %s' % key)

class Recipe(HasTraits):
modules = List()
execute_on_invalidation = Bool(False)

def __init__(self, *args, **kwargs):
HasTraits.__init__(self, *args, **kwargs)

self.namespace = {}
self.namespace = NamespaceDict() #{}

# we open hdf files and don't necessarily read their contents into memory - these need to be closed when we
# either delete the recipe, or clear the namespace
Expand Down