def execute(self, slot, subindex, roi, result): clipped_block_rois = getIntersectingRois( self.Input.meta.shape, self.BlockShape.value, (roi.start, roi.stop), True ) if self._always_request_full_blocks: full_block_rois = getIntersectingRois( self.Input.meta.shape, self.BlockShape.value, (roi.start, roi.stop), False ) else: full_block_rois = clipped_block_rois pool = RequestPool() for full_block_roi, clipped_block_roi in zip(full_block_rois, clipped_block_rois): full_block_roi = numpy.asarray(full_block_roi) clipped_block_roi = numpy.asarray(clipped_block_roi) req = self.Input(*full_block_roi) output_roi = numpy.asarray(clipped_block_roi) - roi.start if (full_block_roi == clipped_block_roi).all(): req.writeInto(result[roiToSlice(*output_roi)]) else: roi_within_block = clipped_block_roi - full_block_roi[0] def copy_request_result(output_roi, roi_within_block, request_result): self.Output.stype.copy_data( result[roiToSlice(*output_roi)], request_result[roiToSlice(*roi_within_block)] ) req.notify_finished(partial(copy_request_result, output_roi, roi_within_block)) pool.add(req) del req pool.wait()
def _executeOutput(self, roi, destination): assert len(roi.stop) == len(self.Input.meta.shape), "roi: {} has the wrong number of dimensions for Input shape: {}".format( roi, self.Input.meta.shape ) assert numpy.less_equal(roi.stop, self.Input.meta.shape).all(), "roi: {} is out-of-bounds for Input shape: {}".format( roi, self.Input.meta.shape ) block_starts = getIntersectingBlocks( self._blockshape, (roi.start, roi.stop) ) block_starts = map( tuple, block_starts ) # Ensure all block cache files are up-to-date reqPool = RequestPool() # (Do the work in parallel.) for block_start in block_starts: entire_block_roi = getBlockBounds( self.Input.meta.shape, self._blockshape, block_start ) f = partial( self._ensureCached, entire_block_roi) reqPool.add( Request(f) ) logger.debug( "Waiting for {} blocks...".format( len(block_starts) ) ) reqPool.wait() # Copy data from each block # (Parallelism not needed here: h5py will serialize these requests anyway) logger.debug( "Copying data from {} blocks...".format( len(block_starts) ) ) for block_start in block_starts: entire_block_roi = getBlockBounds( self.Input.meta.shape, self._blockshape, block_start ) # This block's portion of the roi intersecting_roi = getIntersection( (roi.start, roi.stop), entire_block_roi ) # Compute slicing within destination array and slicing within this block destination_relative_intersection = numpy.subtract(intersecting_roi, roi.start) block_relative_intersection = numpy.subtract(intersecting_roi, block_start) # Copy from block to destination dataset = self._getBlockDataset( entire_block_roi ) destination[ roiToSlice(*destination_relative_intersection) ] = dataset[ roiToSlice( *block_relative_intersection ) ] return destination
def predict_probabilities(self, X): logger.debug( "Predicting with parallel vigra RF" ) X = numpy.asarray(X, dtype=numpy.float32) assert X.ndim == 2 if self._feature_names is not None: # For some reason, vigra doesn't seem to check this for us... assert X.shape[1] == len(self._feature_names), \ "Feature count ({}) doesn't match the training feature count ({}).\n"\ "Expected features: {}".format( X.shape[1], len(self._feature_names), self._feature_names ) # As each forest completes, aggregate results in a shared array. # (Must put in a list so we can update it in this closure.) total_predictions = [None] prediction_lock = RequestLock() def update_predictions(forest, forest_predictions): forest_predictions *= forest.treeCount() with prediction_lock: if total_predictions[0] is None: total_predictions[0] = forest_predictions else: total_predictions[0] += forest_predictions # Create a request for each forest pool = RequestPool() for forest in self._forests: req = Request( partial( forest.predictProbabilities, X ) ) req.notify_finished( partial(update_predictions, forest) ) pool.add( req ) del req pool.wait() total_predictions[0] /= self._num_trees return total_predictions[0]
def predict_probabilities(self, X): logger.debug( "Predicting with parallel vigra RF" ) X = numpy.asarray(X, dtype=numpy.float32) # As each forest completes, aggregate results in a shared array. # (Must put in a list so we can update it in this closure.) total_predictions = [None] prediction_lock = RequestLock() def update_predictions(forest, forest_predictions): forest_predictions *= forest.treeCount() with prediction_lock: if total_predictions[0] is None: total_predictions[0] = forest_predictions else: total_predictions[0] += forest_predictions # Create a request for each forest pool = RequestPool() for forest in self._forests: req = Request( partial( forest.predictProbabilities, X ) ) req.notify_finished( partial(update_predictions, forest) ) pool.add( req ) del req pool.wait() total_predictions[0] /= self._num_trees return total_predictions[0]
def _label(self, roi, result): result = vigra.taggedView(result, axistags=self.Output.meta.axistags) # get the background values bg = self.Background[...].wait() bg = vigra.taggedView(bg, axistags=self.Background.meta.axistags) bg = bg.withAxes(*'ct') assert np.all(self.Background.meta.shape[3:] == self.Input.meta.shape[3:]),\ "Shape of background values incompatible to shape of Input" # do labeling in parallel over channels and time slices pool = RequestPool() start = np.asarray(roi.start, dtype=np.int) stop = np.asarray(roi.stop, dtype=np.int) for ti, t in enumerate(range(roi.start[4], roi.stop[4])): start[4], stop[4] = t, t+1 for ci, c in enumerate(range(roi.start[3], roi.stop[3])): start[3], stop[3] = c, c+1 newRoi = SubRegion(self.Output, start=tuple(start), stop=tuple(stop)) resView = result[..., ci, ti].withAxes(*'xyz') req = Request(partial(self._label3d, newRoi, bg[c, t], resView)) pool.add(req) logger.debug( "{}: Computing connected components for ROI {} ...".format( self.name, roi)) pool.wait() pool.clean() logger.debug("{}: Connected components computed.".format( self.name))
def create_and_train(self, X, y): logger.debug( "Training parallel vigra RF" ) # Save for future reference known_labels = numpy.unique(y) X = numpy.asarray(X, numpy.float32) y = numpy.asarray(y, numpy.uint32) if y.ndim == 1: y = y[:, numpy.newaxis] assert X.ndim == 2 assert len(X) == len(y) # Create N forests forests = [] for _ in range(self._num_forests): forest = vigra.learning.RandomForest(self._trees_per_forest, **self._kwargs) forests.append( forest ) # Train them all in parallel pool = RequestPool() for forest in forests: pool.add( Request( partial(forest.learnRF, X, y) ) ) pool.wait() return ParallelVigraRfLazyflowClassifier( forests, known_labels )
def execute(self, slot, subindex, roi, result): assert slot == self._ReorderedOutput pool = RequestPool() t_ind = 0 for t in range(roi.start[0], roi.stop[0]): c_ind = 0 for c in range(roi.start[-1], roi.stop[-1]): newroi = roi.copy() newroi.start[0] = t newroi.stop[0] = t+1 newroi.start[-1] = c newroi.stop[-1] = c+1 req = self._op.Output.get(newroi) resView = result[t_ind:t_ind+1, ..., c_ind:c_ind+1] req.writeInto(resView) pool.add(req) c_ind += 1 t_ind += 1 pool.wait() pool.clean()
def execute_tasks( tasks ): """ Executes the given list of tasks (functions) in the lazyflow threadpool. """ pool = RequestPool() for task in tasks: pool.add( Request(task) ) pool.wait()
def _resolveMergers(self, hypothesesGraph, model): ''' run merger resolution on the hypotheses graph which contains the current solution ''' logger.info("Resolving mergers.") parameters = self.Parameters.value withTracklets = parameters['withTracklets'] originalGraph = hypothesesGraph.referenceTraxelGraph if withTracklets else hypothesesGraph resolvedMergersDict = {} # Enable full graph computation for animal tracking workflow withFullGraph = False if 'withAnimalTracking' in parameters and parameters['withAnimalTracking']: # TODO: Setting this parameter outside of the track() function (on AnimalConservationTrackingWorkflow) is not desirable withFullGraph = True logger.info("Computing full graph on merger resolver (Only enabled on animal tracking workflow)") mergerResolver = IlastikMergerResolver(originalGraph, pluginPaths=self.pluginPaths, withFullGraph=withFullGraph) # Check if graph contains mergers, otherwise skip merger resolving if not mergerResolver.mergerNum: logger.info("Graph contains no mergers. Skipping merger resolving.") else: # Fit and refine merger nodes using a GMM # It has to be done per time-step in order to aviod loading the whole video on RAM traxelIdPerTimestepToUniqueIdMap, uuidToTraxelMap = getMappingsBetweenUUIDsAndTraxels(model) timesteps = [int(t) for t in traxelIdPerTimestepToUniqueIdMap.keys()] timesteps.sort() timeIndex = self.LabelImage.meta.axistags.index('t') for timestep in timesteps: roi = [slice(None) for i in range(len(self.LabelImage.meta.shape))] roi[timeIndex] = slice(timestep, timestep+1) roi = tuple(roi) labelImage = self.LabelImage[roi].wait() # Get coordinates for object IDs in label image. Used by GMM merger fit. objectIds = vigra.analysis.unique(labelImage[0,...,0]) maxObjectId = max(objectIds) coordinatesForIds = {} pool = RequestPool() for objectId in objectIds: pool.add(Request(partial(mergerResolver.getCoordinatesForObjectId, coordinatesForIds, labelImage[0, ..., 0], timestep, objectId))) # Run requests to get object ID coordinates pool.wait() # Fit mergers and store fit info in nodes if coordinatesForIds: mergerResolver.fitAndRefineNodesForTimestep(coordinatesForIds, maxObjectId, timestep) # Compute object features, re-run flow solver, update model and result, and get merger dictionary resolvedMergersDict = mergerResolver.run() return resolvedMergersDict
def read(self, view_roi, result_out): """ roi: (start, stop) tuples, ordered according to description.output_axes roi should be relative to the view """ output_axes = self.description.output_axes roi_transposed = zip(*view_roi) roi_dict = dict( zip(output_axes, roi_transposed) ) view_roi = zip( *(roi_dict['z'], roi_dict['y'], roi_dict['x']) ) # First, normalize roi and result to zyx order result_out = vigra.taggedView(result_out, output_axes) result_out = result_out.withAxes(*'zyx') assert numpy.array(view_roi).shape == (2,3), "Invalid roi for 3D volume: {}".format( view_roi ) view_roi = numpy.array(view_roi) assert (result_out.shape == (view_roi[1] - view_roi[0])).all() # User gave roi according to the view output. # Now offset it find global roi. roi = view_roi + self.description.view_origin_zyx tile_blockshape = (1,) + tuple(self.description.tile_shape_2d_yx) tile_starts = getIntersectingBlocks( tile_blockshape, roi ) pool = RequestPool() for tile_start in tile_starts: tile_roi_in = getBlockBounds( self.description.bounds_zyx, tile_blockshape, tile_start ) tile_roi_in = numpy.array(tile_roi_in) # This tile's portion of the roi intersecting_roi = getIntersection( roi, tile_roi_in ) intersecting_roi = numpy.array( intersecting_roi ) # Compute slicing within destination array and slicing within this tile destination_relative_intersection = numpy.subtract(intersecting_roi, roi[0]) tile_relative_intersection = intersecting_roi - tile_roi_in[0] # Get a view to the output slice result_region = result_out[roiToSlice(*destination_relative_intersection)] rest_args = self._get_rest_args(tile_blockshape, tile_roi_in) if self.description.tile_url_format.startswith('http'): retrieval_fn = partial( self._retrieve_remote_tile, rest_args, tile_relative_intersection, result_region ) else: retrieval_fn = partial( self._retrieve_local_tile, rest_args, tile_relative_intersection, result_region ) PARALLEL_REQ = True if PARALLEL_REQ: pool.add( Request( retrieval_fn ) ) else: # execute serially (leave the pool empty) retrieval_fn() if PARALLEL_REQ: with Timer() as timer: pool.wait() logger.info("Loading {} tiles took a total of {}".format( len(tile_starts), timer.seconds() ))
def execute(self, slot, subindex, rroi, result): key = roiToSlice(rroi.start,rroi.stop) cnt = 0 written = 0 start, stop = roi.sliceToRoi(key, self.outputs["Output"].meta.shape) assert (stop<=self.outputs["Output"].meta.shape).all() #axisindex = self.inputs["AxisIndex"].value flag = self.inputs["AxisFlag"].value axisindex = self.outputs["Output"].meta.axistags.index(flag) #ugly-ugly-ugly oldkey = list(key) oldkey.pop(axisindex) #print "STACKER: ", flag, axisindex #print "requesting an outslot from stacker:", key, result.shape #print "input slots total: ", len(self.inputs['Images']) requests = [] pool = RequestPool() for i, inSlot in enumerate(self.inputs['Images']): req = None inTagKeys = [ax.key for ax in inSlot.meta.axistags] if flag in inTagKeys: slices = inSlot.meta.shape[axisindex] if cnt + slices >= start[axisindex] and start[axisindex]-cnt<slices and start[axisindex]+written<stop[axisindex]: begin = 0 if cnt < start[axisindex]: begin = start[axisindex] - cnt end = slices if cnt + end > stop[axisindex]: end -= cnt + end - stop[axisindex] key_ = copy.copy(oldkey) key_.insert(axisindex, slice(begin, end, None)) reskey = [slice(None, None, None) for x in range(len(result.shape))] reskey[axisindex] = slice(written, written+end-begin, None) req = inSlot[tuple(key_)].writeInto(result[tuple(reskey)]) written += end - begin cnt += slices else: if cnt>=start[axisindex] and start[axisindex] + written < stop[axisindex]: #print "key: ", key, "reskey: ", reskey, "oldkey: ", oldkey #print "result: ", result.shape, "inslot:", inSlot.meta.shape reskey = [slice(None, None, None) for s in oldkey] reskey.insert(axisindex, written) destArea = result[tuple(reskey)] req = inSlot[tuple(oldkey)].writeInto(destArea) written += 1 cnt += 1 if req is not None: pool.add(req) pool.wait() pool.clean()
def _waitForBlocks(self, block_starts): """ Make sure that all blocks in the given list of blocks are present in the cache before returning. (Blocks that are not yet present will be requested from our Input slot.) """ reqPool = RequestPool() # (Do the work in parallel.) for block_start in block_starts: entire_block_roi = getBlockBounds( self.Output.meta.shape, self._blockshape, block_start ) f = partial( self._ensureCached, entire_block_roi) reqPool.add( Request(f) ) logger.debug( "Waiting for {} blocks...".format( len(block_starts) ) ) reqPool.wait()
def testBasic(self): graph = Graph() opDataProvider = OpArrayPiperWithAccessCount(graph=graph) opCache = OpUnblockedArrayCache(graph=graph) data = np.random.random((100, 100, 100)).astype(np.float32) opDataProvider.Input.setValue(vigra.taggedView(data, "zyx")) opCache.Input.connect(opDataProvider.Output) assert opCache.CleanBlocks.value == [] roi = ((30, 30, 30), (50, 50, 50)) cache_data = opCache.Output(*roi).wait() assert (cache_data == data[roiToSlice(*roi)]).all() assert opDataProvider.accessCount == 1 assert opCache.CleanBlocks.value == [roiToSlice(*roi)] # Request the same data a second time. # Access count should not change. cache_data = opCache.Output(*roi).wait() assert (cache_data == data[roiToSlice(*roi)]).all() assert opDataProvider.accessCount == 1 assert opCache.CleanBlocks.value == [roiToSlice(*roi)] # Now invalidate a part of the data # The cache will discard it, so the access count should increase. opDataProvider.Input.setDirty((30, 30, 30), (31, 31, 31)) assert opCache.CleanBlocks.value == [] cache_data = opCache.Output(*roi).wait() assert (cache_data == data[roiToSlice(*roi)]).all() assert opDataProvider.accessCount == 2 # Repeat this next part just for safety for _ in range(10): # Make sure the cache is empty opDataProvider.Input.setDirty((30, 30, 30), (31, 31, 31)) opDataProvider.accessCount = 0 # Create many requests for the same data. # Upstream data should only be accessed ONCE. pool = RequestPool() for _ in range(10): pool.add(opCache.Output(*roi)) pool.wait() assert opDataProvider.accessCount == 1 # Also, make sure requests for INNER rois of stored blocks are also serviced from memory opDataProvider.accessCount = 0 inner_roi = ((35, 35, 35), (45, 45, 45)) cache_data = opCache.Output(*inner_roi).wait() assert (cache_data == data[roiToSlice(*inner_roi)]).all() assert opDataProvider.accessCount == 0 assert opCache.CleanBlocks.value == [roiToSlice(*roi)]
def export(self, filename, hypothesesGraph, pluginExportContext): """Export the tracking solution stored in the hypotheses graph as a sequence of H5 files, one per frame, containing the label image of that frame and which objects were part of a move or a division. :param filename: string of the FOLDER where to save the result :param hypothesesGraph: hytra.core.hypothesesgraph.HypothesesGraph filled with a solution :param pluginExportContext: instance of ilastik.plugins.PluginExportContext containing: labelImageSlot (required here) as well as objectFeaturesSlot, rawImageSlot, additionalPluginArgumentsSlot :returns: True on success, False otherwise """ labelImageSlot = pluginExportContext.labelImageSlot traxelIdPerTimestepToUniqueIdMap, uuidToTraxelMap = hypothesesGraph.getMappingsBetweenUUIDsAndTraxels() timesteps = [t for t in traxelIdPerTimestepToUniqueIdMap.keys()] result = hypothesesGraph.getSolutionDictionary() mergers, detections, links, divisions = getMergersDetectionsLinksDivisions(result, uuidToTraxelMap) # group by timestep for event creation mergersPerTimestep = getMergersPerTimestep(mergers, timesteps) linksPerTimestep = getLinksPerTimestep(links, timesteps) detectionsPerTimestep = getDetectionsPerTimestep(detections, timesteps) divisionsPerTimestep = getDivisionsPerTimestep(divisions, linksPerTimestep, timesteps) # save to disk in parallel pool = RequestPool() timeIndex = labelImageSlot.meta.axistags.index('t') if not os.path.exists(filename): os.makedirs(filename) for timestep in traxelIdPerTimestepToUniqueIdMap.keys(): # extract current frame lable image roi = [slice(None) for i in range(len(labelImageSlot.meta.shape))] roi[timeIndex] = slice(int(timestep), int(timestep)+1) roi = tuple(roi) labelImage = labelImageSlot[roi].wait() fn = os.path.join(filename, "{0:05d}.h5".format(int(timestep))) pool.add(Request(partial(writeEvents, int(timestep), linksPerTimestep[timestep], divisionsPerTimestep[timestep], mergersPerTimestep[timestep], detectionsPerTimestep[timestep], fn, labelImage))) pool.wait() return True
def _execute_Output(self, slot, subindex, roi, result): """ Overridden from OpUnblockedArrayCache """ def copy_block(full_block_roi, clipped_block_roi): full_block_roi = numpy.asarray(full_block_roi) clipped_block_roi = numpy.asarray(clipped_block_roi) output_roi = numpy.asarray(clipped_block_roi) - roi.start block_roi = self._get_containing_block_roi(clipped_block_roi) # Skip cache and copy full block directly if self.BypassModeEnabled.value: full_block_data = self.Output.stype.allocateDestination(SubRegion(self.Output, *full_block_roi)) self.Input(*full_block_roi).writeInto(full_block_data).block() roi_within_block = clipped_block_roi - full_block_roi[0] self.Output.stype.copy_data( result[roiToSlice(*output_roi)], full_block_data[roiToSlice(*roi_within_block)] ) # If data data exists already or we can just fetch it without needing extra scratch space, # just call the base class elif block_roi is not None or (full_block_roi == clipped_block_roi).all(): self._execute_Output_impl(clipped_block_roi, result[roiToSlice(*output_roi)]) elif self.Input.meta.dontcache: # Data isn't in the cache, but we don't need it in the cache anyway. self.Input(*clipped_block_roi).writeInto(result[roiToSlice(*output_roi)]).block() else: # Data doesn't exist yet in the cache. # Request the full block, but then discard the parts we don't need. # (We use allocateDestination() here to support MaskedArray types.) # TODO: We should probably just get rid of MaskedArray support altogether... full_block_data = self.Output.stype.allocateDestination(SubRegion(self.Output, *full_block_roi)) self._execute_Output_impl(full_block_roi, full_block_data) roi_within_block = clipped_block_roi - full_block_roi[0] self.Output.stype.copy_data( result[roiToSlice(*output_roi)], full_block_data[roiToSlice(*roi_within_block)] ) clipped_block_rois = getIntersectingRois(self.Input.meta.shape, self._blockshape, (roi.start, roi.stop), True) full_block_rois = getIntersectingRois(self.Input.meta.shape, self._blockshape, (roi.start, roi.stop), False) pool = RequestPool() for full_block_roi, clipped_block_roi in zip(full_block_rois, clipped_block_rois): req = Request(partial(copy_block, full_block_roi, clipped_block_roi)) pool.add(req) pool.wait()
def execute(self, slot, subindex, roi, result): assert len(roi.start) == len(roi.stop) == len(self.Output.meta.shape) assert slot == self.Output t_ind = self.RawVolume.meta.axistags.index('t') assert t_ind < len(self.RawVolume.meta.shape) def compute_features_for_time_slice(res_t_ind, t): axes4d = [k for k in self.RawVolume.meta.getTaggedShape().keys() if k in 'xyzc'] # Process entire spatial volume s = [slice(None)] * len(self.RawVolume.meta.shape) s[t_ind] = slice(t, t+1) s = tuple(s) # Request in parallel raw_req = self.RawVolume[s] raw_req.submit() label_req = self.LabelVolume[s] label_req.submit() if self.Atlas.ready(): atlasVolume = self.Atlas[s].wait() atlasVolume = vigra.taggedView(atlasVolume, axistags=self.Atlas.meta.axistags) atlasVolume = atlasVolume.withAxes(*axes4d) else: atlasVolume = None # Get results rawVolume = raw_req.wait() labelVolume = label_req.wait() rawVolume = vigra.taggedView(rawVolume, axistags=self.RawVolume.meta.axistags) labelVolume = vigra.taggedView(labelVolume, axistags=self.LabelVolume.meta.axistags) # Convert to 4D (preserve axis order) rawVolume = rawVolume.withAxes(*axes4d) labelVolume = labelVolume.withAxes(*axes4d) acc = self._extract(rawVolume, labelVolume, atlasVolume) # Copy into the result result[res_t_ind] = acc # loop over requested time slices pool = RequestPool() for res_t_ind, t in enumerate(range(roi.start[t_ind], roi.stop[t_ind])): pool.add( Request( partial(compute_features_for_time_slice, res_t_ind, t) ) ) pool.wait() return result
def execute(self, slot, subindex, roi, result): featList = [] labelsList = [] for i in range(len(self.Labels)): feats = self.Features[i]([]).wait() # TODO: we should be able to use self.Labels[i].value, # but the current implementation of Slot.value() does not # do the right thing. labels = self.Labels[i]([]).wait() featstmp, labelstmp = make_feature_array(feats, labels) featList.append(featstmp) labelsList.append(labelstmp) featMatrix = _concatenate(featList, axis=0) labelsMatrix = _concatenate(labelsList, axis=0) print "training on matrix:", featMatrix.shape, featMatrix.dtype if len(featMatrix) == 0 or len(labelsMatrix) == 0: result[:] = None return oob = [0] * self.ForestCount.value try: # Ensure there are no NaNs in the feature matrix # TODO: There should probably be a better way to fix this... featMatrix = numpy.asarray(featMatrix, dtype=numpy.float32) nanFeatMatrix = numpy.isnan(featMatrix) if nanFeatMatrix.any(): warnings.warn("Feature matrix has NaN values! Replacing with 0.0...") featMatrix[numpy.where(nanFeatMatrix)] = 0.0 # train and store forests in parallel pool = RequestPool() for i in range(self.ForestCount.value): def train_and_store(number): result[number] = vigra.learning.RandomForest(self._tree_count) oob[number] = result[number].learnRF(featMatrix, numpy.asarray(labelsMatrix, dtype=numpy.uint32)) print "intermediate oob:", oob[number] req = Request( partial(train_and_store, i) ) pool.add( req ) pool.wait() pool.clean() except: print ("couldn't learn classifier") raise oob_total = numpy.mean(oob) print "training finished, out of bag error:", oob_total return result
def predict(cls, X, method='classic'): """ predict if the histograms in X correspond to missing regions do this for subsets of X in parallel """ if cls._manager is None: cls._manager = SVMManager() # svm input has to be (nSamples, nFeatures) -> for us: (nSampels = len(X), nFeatures = # of histogrambins ) X_reshaped = np.zeros((len(X), len(X[0]))) for i in range(len(X)): X_reshaped[i, :] = X[i] n_bins = len(X[0]) if method == 'classic' or not have_sklearn: logger.warning("no real svm used! -> PseudoSVC") svm = PseudoSVC() else: # load samples for histograms of labeled regions try: svm = cls._manager.get(n_bins) except SVMManager.NotTrainedError: # fail gracefully if not trained => responsibility of user! svm = PseudoSVC() y = np.zeros((len(X),)) * np.nan pool = RequestPool() # chunk up all samples from X into chunks that will be predicted in parallel chunk_size = 1000 # FIXME magic number?? n_chunks = len(X)/chunk_size + (1 if len(X) % chunk_size > 0 else 0) s = [slice(k * chunk_size, min((k + 1) * chunk_size, len(X))) for k in range(n_chunks)] def partFun(i): y[s[i]] = svm.predict(X_reshaped[s[i], :]) for i in range(n_chunks): req = Request(partial(partFun, i)) pool.add(req) pool.wait() pool.clean() return np.asarray(y)
def predict(cls, X, method='classic'): ''' predict if the histograms in X correspond to missing regions do this for subsets of X in parallel ''' if cls._manager is None: cls._manager = SVMManager() assert len(X.shape) == 2, \ "Prediction data must have shape (nSamples, nHistogramBins)." nBins = X.shape[1] if method == 'classic' or not havesklearn: svm = PseudoSVC() else: try: svm = cls._manager.get(nBins) except SVMManager.NotTrainedError: # fail gracefully if not trained => responsibility of user! svm = PseudoSVC() y = np.zeros((len(X),))*np.nan pool = RequestPool() chunkSize = 1000 # FIXME magic number?? nChunks = len(X)//chunkSize + (1 if len(X) % chunkSize > 0 else 0) s = [slice(k*chunkSize, min((k+1)*chunkSize, len(X))) for k in range(nChunks)] def partFun(i): y[s[i]] = svm.predict(X[s[i]]) for i in range(nChunks): req = Request(partial(partFun, i)) pool.add(req) pool.wait() pool.clean() # not neccessary #assert not np.any(np.isnan(y)) return np.asarray(y)
def _train_forests(forests, X, y): """ Train all RFs (in parallel), and return the oobs. """ oobs = [None] * len(forests) def store_oob_results(i, oob): oobs[i] = oob with Timer() as train_timer: pool = RequestPool() for i, forest in enumerate(forests): req = Request( partial(forest.learnRF, X, y) ) # save the oob results req.notify_finished( partial( store_oob_results, i ) ) pool.add( req ) pool.wait() logger.info("Training took, {} seconds".format( train_timer.seconds() ) ) return oobs
def predict(cls, X, method="classic"): """ predict if the histograms in X correspond to missing regions do this for subsets of X in parallel """ if cls._manager is None: cls._manager = SVMManager() assert len(X.shape) == 2, "Prediction data must have shape (nSamples, nHistogramBins)." nBins = X.shape[1] if method == "classic" or not havesklearn: svm = PseudoSVC() else: try: svm = cls._manager.get(nBins) except SVMManager.NotTrainedError: # fail gracefully if not trained => responsibility of user! svm = PseudoSVC() y = np.zeros((len(X),)) * np.nan pool = RequestPool() chunkSize = 1000 # FIXME magic number?? nChunks = len(X) // chunkSize + (1 if len(X) % chunkSize > 0 else 0) s = [slice(k * chunkSize, min((k + 1) * chunkSize, len(X))) for k in range(nChunks)] def partFun(i): y[s[i]] = svm.predict(X[s[i]]) for i in range(nChunks): req = Request(partial(partFun, i)) pool.add(req) pool.wait() pool.clean() # not neccessary # assert not np.any(np.isnan(y)) return np.asarray(y)
def execute(self, slot, subindex, roi, result): assert slot == self.ConcatenatedOutput self.progressSignal(0.0) num_dirty_slots = len(self._dirty_slots) subtask_progress = {} progress_lock = RequestLock() def forward_progress_updates(feature_slot, progress): with progress_lock: subtask_progress[feature_slot] = progress total_progress = 0.95 * sum(subtask_progress.values()) / num_dirty_slots self.progressSignal(total_progress) logger.debug( "Updating features for {} dirty images out of {}" "".format(len(self._dirty_slots), len(self.FeatureMatrices)) ) pool = RequestPool() subresults = [] for feature_slot, progress_slot in zip(self.FeatureMatrices, self.ProgressSignals): subresults.append([None]) req = feature_slot[:] req.writeInto(subresults[-1]) # Only use progress for slots that were dirty. # The others are going to be really fast. if feature_slot in self._dirty_slots: sub_progress_signal = progress_slot.value sub_progress_signal.subscribe(partial(forward_progress_updates, feature_slot)) pool.add(req) pool.wait() # Reset dirty slots self._dirty_slots = set() # Since the subresults are returned in 'value' slots, # we have to unpack them from their single-element lists. subresult_list = list(itertools.chain(*subresults)) total_matrix = numpy.concatenate(subresult_list, axis=0) self.progressSignal(100.0) result[0] = total_matrix
def execute(self, slot, subindex, roi, result): assert len(roi.start) == len(roi.stop) == len(self.Output.meta.shape) assert slot == self.Output t_ind = self.RawVolume.meta.axistags.index('t') assert t_ind < len(self.RawVolume.meta.shape) def compute_features_for_time_slice(res_t_ind, t): # Process entire spatial volume s = [slice(None) for i in range(len(self.RawVolume.meta.shape))] s[t_ind] = slice(t, t+1) s = tuple(s) # Request in parallel raw_req = self.RawVolume[s] label_req = self.LabelVolume[s] raw_req.submit() label_req.submit() # Get results rawVolume = raw_req.wait() labelVolume = label_req.wait() rawVolume = vigra.taggedView(rawVolume, axistags=self.RawVolume.meta.axistags) labelVolume = vigra.taggedView(labelVolume, axistags=self.LabelVolume.meta.axistags) # Convert to 4D (preserve axis order) axes4d = self.RawVolume.meta.getTaggedShape().keys() axes4d = filter(lambda k: k in 'xyzc', axes4d) rawVolume = rawVolume.withAxes(*axes4d) labelVolume = labelVolume.withAxes(*axes4d) acc = self._extract(rawVolume, labelVolume) # Copy into the result result[res_t_ind] = acc # loop over requested time slices pool = RequestPool() for res_t_ind, t in enumerate(xrange(roi.start[t_ind], roi.stop[t_ind])): pool.add( Request( partial(compute_features_for_time_slice, res_t_ind, t) ) ) pool.wait() return result
def create_and_train(self, X, y, feature_names=None): # Distribute trees as evenly as possible tree_counts = numpy.array([self._num_trees // self._num_forests] * self._num_forests) tree_counts[:self._num_trees % self._num_forests] += 1 assert tree_counts.sum() == self._num_trees tree_counts = map(int, tree_counts) tree_counts[:] = (tree_count for tree_count in tree_counts if tree_count != 0) logger.debug("Training parallel vigra RF") # Save for future reference known_labels = numpy.unique(y) X = numpy.asarray(X, numpy.float32) y = numpy.asarray(y, numpy.uint32) if y.ndim == 1: y = y[:, numpy.newaxis] assert X.ndim == 2 assert len(X) == len(y) # Create N forests forests = [] for tree_count in tree_counts: forests.append( vigra.learning.RandomForest(tree_count, **self._kwargs)) # Train them all in parallel oobs = [None] * len(forests) pool = RequestPool() for i, forest in enumerate(forests): req = Request(partial(forest.learnRF, X, y)) # save the oobs req.notify_finished(partial(oobs.__setitem__, i)) pool.add(req) with Timer() as timer: pool.wait() logger.info("Training completed in {} seconds. Average OOB: {}".format( timer.seconds(), numpy.average(oobs))) return ParallelVigraRfLazyflowClassifier(forests, oobs, known_labels, feature_names)
def execute(self, slot, subindex, roi, result): def compute_for_channel(output_channel, input_channel): input_roi = numpy.array((roi.start, roi.stop)) input_roi[:, -1] = (input_channel, input_channel + 1) input_req = self.Input(*input_roi) # If possible, use the result array itself as a scratch area if self.Input.meta.dtype == result.dtype: input_req.writeInto(result[..., output_channel : output_channel + 1]) input_data = input_req.wait() input_data = input_data.astype(numpy.float32, order="C", copy=False) input_data = input_data[..., 0] # drop channel axis result[..., output_channel] = computeIntegralImage(input_data) pool = RequestPool() for output_channel, input_channel in enumerate(range(roi.start[-1], roi.stop[-1])): pool.add(Request(partial(compute_for_channel, output_channel, input_channel))) pool.wait()
def _executePredictionImage(self, slot, roi, destination): roi_one_channel = numpy.array((roi.start, roi.stop)) roi_one_channel[..., -1] = (0, 1) # Determine intersecting blocks block_shape = self._getFullShape(self.BlockShape3dDict.value) block_starts = getIntersectingBlocks(block_shape, roi_one_channel) block_starts = map(tuple, block_starts) # Ensure that block pipelines exist (create first if necessary) for block_start in block_starts: self._ensurePipelineExists(block_start) # Retrieve result from each block, and write into the appropriate region of the destination pool = RequestPool() for block_start in block_starts: opBlockPipeline = self._blockPipelines[block_start] block_roi = opBlockPipeline.block_roi block_intersection = getIntersection(block_roi, roi_one_channel) block_relative_intersection = numpy.subtract( block_intersection, block_roi[0]) destination_relative_intersection = numpy.subtract( block_intersection, roi_one_channel[0]) block_slot = opBlockPipeline.PredictionImage if slot == self.ProbabilityChannelImage: block_slot = opBlockPipeline.ProbabilityChannelImage # Add channels back to roi # request all channels block_relative_intersection[..., -1] = ( 0, opBlockPipeline.ProbabilityChannelImage.meta.shape[-1]) # But only write the ones that were specified in the original roi destination_relative_intersection[..., -1] = (roi.start[-1], roi.stop[-1]) # Request the data destination_slice = roiToSlice(*destination_relative_intersection) req = block_slot(*block_relative_intersection) req.writeInto(destination[destination_slice]) pool.add(req) pool.wait() return destination
def execute(self, slot, subindex, roi, result): assert slot == self.LabelAndFeatureMatrix self.progressSignal(0.0) # Technically, this could result in strange progress reporting if execute() # is called by multiple threads in parallel. # This could be fixed with some fancier progress state, but # (1) We don't expect that to by typical, and # (2) progress reporting is merely informational. num_dirty_blocks = len(self._dirty_blocks) def update_progress(result): remaining_dirty = len(self._dirty_blocks) percent_complete = 95.0 * (num_dirty_blocks - remaining_dirty) / num_dirty_blocks self.progressSignal(percent_complete) # Update all dirty blocks in the cache logger.debug("Updating {} dirty blocks".format(num_dirty_blocks)) pool = RequestPool() for block_start in self._dirty_blocks: req = Request(partial(self._update_block, block_start)) req.notify_finished(update_progress) pool.add(req) pool.wait() # Concatenate the all blockwise results if self._blockwise_feature_matrices: total_feature_matrix = numpy.concatenate( self._blockwise_feature_matrices.values(), axis=0) else: # No label points at all. # Return an empty label&feature matrix (of the correct shape) num_feature_channels = self.FeatureImage.meta.shape[-1] total_feature_matrix = numpy.ndarray(shape=(0, 1 + num_feature_channels), dtype=numpy.float) self.progressSignal(100.0) logger.debug("After update, there are {} clean blocks".format( len(self._blockwise_feature_matrices))) result[0] = total_feature_matrix
def execute(self, slot, subindex, roi, result): def compute_for_channel(output_channel, input_channel): input_roi = numpy.array( (roi.start, roi.stop) ) input_roi[:,-1] = (input_channel, input_channel+1) input_req = self.Input(*input_roi) # If possible, use the result array itself as a scratch area if self.Input.meta.dtype == result.dtype: input_req.writeInto( result[...,output_channel:output_channel+1] ) input_data = input_req.wait() input_data = input_data.astype(numpy.float32, order='C', copy=False) input_data = input_data[...,0] # drop channel axis result[..., output_channel] = computeIntegralImage(input_data) pool = RequestPool() for output_channel, input_channel in enumerate(range(roi.start[-1], roi.stop[-1])): pool.add( Request( partial( compute_for_channel, output_channel, input_channel ) ) ) pool.wait()
def _train_forests_with_feature_importance(forests, X, y, feature_names, export_path=None): """ Train all RFs (in parallel) and compute feature importances while doing so. The importances table will be logged as INFO, and also exported to a file if export_path is given. Returns: oobs and importances """ oobs = [None] * len(forests) importances = [None] * len(forests) def store_training_results(i, training_results): oob, importance_results = training_results oobs[i] = oob importances[i] = importance_results with Timer() as train_timer: pool = RequestPool() for i, forest in enumerate(forests): req = Request(partial(forest.learnRFWithFeatureSelection, X, y)) # save the training results req.notify_finished(partial(store_training_results, i)) pool.add(req) pool.wait() logger.info("Training took, {} seconds".format(train_timer.seconds())) # Forests may have different numbers of trees, # so take a weighted average of their importances tree_counts = [f.treeCount() for f in forests] weights = numpy.array(tree_counts).astype(float) weights /= weights.sum() named_importances = collections.OrderedDict( list(zip(feature_names, numpy.average(importances, weights=weights, axis=0))) ) importance_table = generate_importance_table(named_importances, sort="overall", export_path=export_path) logger.info("Feature importance measurements during training: \n{}".format(importance_table)) return oobs, named_importances
def run(self): # # Load caches beforehand (To remove overhead of reading frames) # with Timer() as timerCaches: # rawVol = self.opCacheRaw.Output([]).wait() # binaryVol = self.opCacheBinary.Output([]).wait() # # print "Caches took {} secs".format(timerCaches.seconds()) # # del rawVol # del binaryVol # Profile object extraction simplified print("\nStarting object extraction simplified (single-thread, without cache)") with Timer() as timerObjectFeaturesSimp: featsObjectFeaturesSimp = self.opObjectFeaturesSimp.Features([]).wait() print("Simplified object extraction took: {} seconds".format(timerObjectFeaturesSimp.seconds())) # Profile object extraction optimized print("\nStarting object extraction (multi-thread, without cache)") with Timer() as timerObjectExtraction: featsObjectExtraction = self.opObjectExtraction.RegionFeatures([]).wait() print("Object extraction took: {} seconds".format(timerObjectExtraction.seconds())) # Profile for basic multi-threaded feature computation # just a multi-threaded loop that labels volumes and extract object features directly (No operators, no plugin system, no overhead, just a loop) featsBasicFeatureComp = dict.fromkeys( list(range(self.op5Raw.Output.meta.shape[0])), None) print("\nStarting basic multi-threaded feature computation") pool = RequestPool() for t in range(0, self.op5Raw.Output.meta.shape[0], 1): pool.add( Request( partial(self._computeObjectFeatures, t, featsBasicFeatureComp) ) ) with Timer() as timerBasicFeatureComp: pool.wait() print("Basic multi-threaded feature extraction took: {} seconds".format( timerBasicFeatureComp.seconds() ))
def execute(self, slot, subindex, roi, result): stacked_axisindex = self.Images[0].meta.getAxisKeys().index(self.AxisFlag.value) pool = RequestPool() for slot, (slot_output_start, slot_output_stop) in zip(self.Images, self.stacked_output_ranges): if roi.start[stacked_axisindex] >= slot_output_start and roi.stop[stacked_axisindex] <= slot_output_stop: output_roi = roi.copy() output_roi.start[stacked_axisindex] = max(slot_output_start, roi.start[stacked_axisindex]) output_roi.stop[stacked_axisindex] = min(slot_output_stop, roi.stop[stacked_axisindex]) request_roi = roi.copy() request_roi.start[stacked_axisindex] = output_roi.start[stacked_axisindex] - slot_output_start request_roi.stop[stacked_axisindex] = output_roi.stop[stacked_axisindex] - slot_output_start result_roi = roi.copy() result_roi.start = output_roi.start - roi.start result_roi.stop = output_roi.stop - roi.start req = slot(request_roi.start, request_roi.stop) req.writeInto( result[roiToSlice(result_roi.start, result_roi.stop)] ) pool.add( req ) pool.wait()
def create_and_train(self, X, y, feature_names=None): # Distribute trees as evenly as possible tree_counts = numpy.array( [self._num_trees // self._num_forests] * self._num_forests ) tree_counts[:self._num_trees % self._num_forests] += 1 assert tree_counts.sum() == self._num_trees tree_counts = map(int, tree_counts) tree_counts[:] = (tree_count for tree_count in tree_counts if tree_count != 0) logger.debug( "Training parallel vigra RF" ) # Save for future reference known_labels = numpy.unique(y) X = numpy.asarray(X, numpy.float32) y = numpy.asarray(y, numpy.uint32) if y.ndim == 1: y = y[:, numpy.newaxis] assert X.ndim == 2 assert len(X) == len(y) # Create N forests forests = [] for tree_count in tree_counts: forests.append( vigra.learning.RandomForest(tree_count, **self._kwargs) ) # HERE <--- this links to C++ library # Train them all in parallel oobs = [None] * len(forests) pool = RequestPool() for i, forest in enumerate(forests): req = Request( partial(forest.learnRF, X, y) ) # save the oobs req.notify_finished( partial( oobs.__setitem__, i ) ) pool.add( req ) with Timer() as timer: pool.wait() logger.info( "Training completed in {} seconds. Average OOB: {}".format( timer.seconds(), numpy.average(oobs) ) ) return ParallelVigraRfLazyflowClassifier( forests, oobs, known_labels, feature_names )
def _executePredictionImage(self, slot, roi, destination): roi_one_channel = numpy.array( (roi.start, roi.stop) ) roi_one_channel[...,-1] = (0,1) # Determine intersecting blocks block_shape = self._getFullShape( self.BlockShape3dDict.value ) block_starts = getIntersectingBlocks( block_shape, roi_one_channel ) block_starts = map( tuple, block_starts ) # Ensure that block pipelines exist (create first if necessary) for block_start in block_starts: self._ensurePipelineExists(block_start) # Retrieve result from each block, and write into the appropriate region of the destination pool = RequestPool() for block_start in block_starts: opBlockPipeline = self._blockPipelines[block_start] block_roi = opBlockPipeline.block_roi block_intersection = getIntersection( block_roi, roi_one_channel ) block_relative_intersection = numpy.subtract(block_intersection, block_roi[0]) destination_relative_intersection = numpy.subtract(block_intersection, roi_one_channel[0]) block_slot = opBlockPipeline.PredictionImage if slot == self.ProbabilityChannelImage: block_slot = opBlockPipeline.ProbabilityChannelImage # Add channels back to roi # request all channels block_relative_intersection[...,-1] = (0, opBlockPipeline.ProbabilityChannelImage.meta.shape[-1]) # But only write the ones that were specified in the original roi destination_relative_intersection[...,-1] = ( roi.start[-1], roi.stop[-1] ) # Request the data destination_slice = roiToSlice( *destination_relative_intersection ) req = block_slot( *block_relative_intersection ) req.writeInto( destination[destination_slice] ) pool.add( req ) pool.wait() return destination
def predict_probabilities(self, X): logger.debug( "Predicting with parallel vigra RF" ) X = numpy.asarray(X, dtype=numpy.float32) # Create a request for each forest reqs = [] for forest in self._forests: req = Request( partial( forest.predictProbabilities, X ) ) reqs.append( req ) # Execute all requests in a pool pool = RequestPool() for req in reqs: pool.add( req ) pool.wait() # Aggregate the results predictions = reqs[0].result for req in reqs[1:]: predictions += req.result predictions /= len(reqs) return predictions
def predict_probabilities(self, X): logger.debug("Predicting with parallel vigra RF") X = numpy.asarray(X, dtype=numpy.float32) # Create a request for each forest reqs = [] for forest in self._forests: req = Request(partial(forest.predictProbabilities, X)) reqs.append(req) # Execute all requests in a pool pool = RequestPool() for req in reqs: pool.add(req) pool.wait() # Aggregate the results predictions = self._forests[0].treeCount() * reqs[0].result for forest, req in zip(self._forests[1:], reqs[1:]): predictions += forest.treeCount() * req.result predictions /= self._num_trees return predictions
def predict_probabilities(self, X): logger.debug("Predicting with parallel vigra RF") X = numpy.asarray(X, dtype=numpy.float32) assert X.ndim == 2 if self._feature_names is not None: # For some reason, vigra doesn't seem to check this for us... assert X.shape[1] == len(self._feature_names), ( "Feature count ({}) doesn't match the training feature count ({}).\n" "Expected features: {}".format(X.shape[1], len(self._feature_names), self._feature_names)) # As each forest completes, aggregate results in a shared array. # (Must put in a list so we can update it in this closure.) total_predictions = [None] prediction_lock = RequestLock() def update_predictions(forest, forest_predictions): forest_predictions *= forest.treeCount() with prediction_lock: if total_predictions[0] is None: total_predictions[0] = forest_predictions else: total_predictions[0] += forest_predictions # Create a request for each forest pool = RequestPool() for forest in self._forests: req = Request(partial(forest.predictProbabilities, X)) req.notify_finished(partial(update_predictions, forest)) pool.add(req) del req pool.wait() total_predictions[0] /= self._num_trees return total_predictions[0]
def _label(self, roi, result): result = vigra.taggedView(result, axistags=self.Output.meta.axistags) # get the background values bg = self.Background[...].wait() bg = vigra.taggedView(bg, axistags=self.Background.meta.axistags) bg = bg.withAxes(*"ct") assert np.all( self.Background.meta.shape[0] == self.Input.meta.shape[0] ), "Shape of background values incompatible to shape of Input" assert np.all( self.Background.meta.shape[4] == self.Input.meta.shape[4] ), "Shape of background values incompatible to shape of Input" # do labeling in parallel over channels and time slices pool = RequestPool() start = np.asarray(roi.start, dtype=np.int) stop = np.asarray(roi.stop, dtype=np.int) for ti, t in enumerate(range(roi.start[0], roi.stop[0])): start[0], stop[0] = t, t + 1 for ci, c in enumerate(range(roi.start[4], roi.stop[4])): start[4], stop[4] = c, c + 1 newRoi = SubRegion(self.Output, start=tuple(start), stop=tuple(stop)) resView = result[ti, ..., ci].withAxes(*"xyz") req = Request(partial(self._label3d, newRoi, bg[c, t], resView)) pool.add(req) logger.debug( "{}: Computing connected components for ROI {} ...".format( self.name, roi)) pool.wait() pool.clean() logger.debug("{}: Connected components computed.".format(self.name))
def execute(self, slot, subindex, roi, result): assert slot == self.LabelAndFeatureMatrix self.progressSignal(0.0) # Technically, this could result in strange progress reporting if execute() # is called by multiple threads in parallel. # This could be fixed with some fancier progress state, but # (1) We don't expect that to by typical, and # (2) progress reporting is merely informational. num_dirty_blocks = len( self._dirty_blocks ) def update_progress( result ): remaining_dirty = len( self._dirty_blocks ) percent_complete = 95.0*(num_dirty_blocks - remaining_dirty)/num_dirty_blocks self.progressSignal( percent_complete ) # Update all dirty blocks in the cache logger.debug( "Updating {} dirty blocks".format(num_dirty_blocks) ) pool = RequestPool() for block_start in self._dirty_blocks: req = Request( partial(self._update_block, block_start ) ) req.notify_finished( update_progress ) pool.add( req ) pool.wait() # Concatenate the all blockwise results if self._blockwise_feature_matrices: total_feature_matrix = numpy.concatenate( self._blockwise_feature_matrices.values(), axis=0 ) else: # No label points at all. # Return an empty label&feature matrix (of the correct shape) num_feature_channels = self.FeatureImage.meta.shape[-1] total_feature_matrix = numpy.ndarray( shape=(0, 1 + num_feature_channels), dtype=numpy.float ) self.progressSignal(100.0) logger.debug( "After update, there are {} clean blocks".format( len(self._blockwise_feature_matrices) ) ) result[0] = total_feature_matrix
def export(self, filename, hypothesesGraph, pluginExportContext): """ Export the Multi-Worm-Tracker .summary and .blobs files. :param filename: string of the FILE where to save the result (different .xml files were) :param hypothesesGraph: hytra.core.hypothesesgraph.HypothesesGraph filled with a solution :param pluginExportContext: instance of ilastik.plugins.PluginExportContext containing: objectFeaturesSlot (required here), labelImageSlot (required here) as well as rawImageSlot, additionalPluginArgumentsSlot :returns: True on success, False otherwise """ # Get object features features = pluginExportContext.objectFeaturesSlot([]).wait() contoursDict = {} summaryDict = {} labelImageSlot = pluginExportContext.labelImageSlot tIndex = labelImageSlot.meta.axistags.index("t") tMax = labelImageSlot.meta.shape[tIndex] # Method to compute contours for single frame (called in parallel by a request parallel) def compute_dicts_for_frame(tIndex, t, labelImageSlot, hypothesesGraph, contoursDict): roi = [slice(None) for i in range(len(labelImageSlot.meta.shape))] roi[tIndex] = slice(t, t + 1) roi = tuple(roi) frame = labelImageSlot[roi].wait() frame = frame.squeeze() for idx in vigra.analysis.unique(frame): nodeId = (t, idx) if hypothesesGraph.hasNode( nodeId ) and "lineageId" in hypothesesGraph._graph.node[nodeId]: if t in summaryDict: summaryDict[t]["objectsNumber"] += 1 summaryDict[t]["validObjectsNumber"] += 1 summaryDict[t]["averageDuration"] = 0.0 summaryDict[t]["averageSpeed"] = 0.0 summaryDict[t]["averageAngularSpeed"] = 0.0 summaryDict[t]["averageLength"] += features[t][ "Standard Object Features"]["RegionRadii"][idx, 0] summaryDict[t]["averageRelativeLength"] = summaryDict[ t]["averageLength"] summaryDict[t]["averageWidth"] += features[t][ "Standard Object Features"]["RegionRadii"][idx, 1] summaryDict[t]["averageRelativeWidth"] = summaryDict[ t]["averageWidth"] summaryDict[t]["averageAspectRatio"] += old_div( features[t]["Standard Object Features"] ["RegionRadii"][idx, 0], features[t]["Standard Object Features"] ["RegionRadii"][idx, 1], ) summaryDict[t][ "averageRelativeAspectRatio"] = summaryDict[t][ "averageAspectRatio"] summaryDict[t]["endWiggle"] = 0.0 else: summaryDict[t] = {} summaryDict[t]["objectsNumber"] = 1 summaryDict[t]["validObjectsNumber"] = 1 summaryDict[t]["averageDuration"] = 0.0 summaryDict[t]["averageSpeed"] = 0.0 summaryDict[t]["averageAngularSpeed"] = 0.0 summaryDict[t]["averageLength"] = features[t][ "Standard Object Features"]["RegionRadii"][idx, 0] summaryDict[t]["averageRelativeLength"] = summaryDict[ t]["averageLength"] summaryDict[t]["averageWidth"] = features[t][ "Standard Object Features"]["RegionRadii"][idx, 1] summaryDict[t]["averageRelativeWidth"] = summaryDict[ t]["averageWidth"] summaryDict[t]["averageAspectRatio"] = old_div( features[t]["Standard Object Features"] ["RegionRadii"][idx, 0], features[t]["Standard Object Features"] ["RegionRadii"][idx, 1], ) summaryDict[t][ "averageRelativeAspectRatio"] = summaryDict[t][ "averageAspectRatio"] summaryDict[t]["endWiggle"] = 0.0 # Generate frame with single label idx frameSingleLabel = np.zeros(frame.shape).astype(np.uint8) frameSingleLabel[frame == idx] = 1 # Find contours using skimage marching squares contours = measure._find_contours.find_contours( frameSingleLabel, 0) # Save contours to dictionary if idx in contoursDict: contoursDict[idx][t] = contours[0] else: contoursDict[idx] = {t: contours[0]} # Compute the contours in parallel pool = RequestPool() for t in range(tMax): pool.add( Request( partial(compute_dicts_for_frame, tIndex, t, labelImageSlot, hypothesesGraph, contoursDict))) pool.wait() # Generate .summary file summaryFile = open(filename + ".summary", "w") for t in sorted(summaryDict): summaryDict[t]["averageLength"] /= summaryDict[t]["objectsNumber"] summaryDict[t]["averageRelativeLength"] = summaryDict[t][ "averageLength"] summaryDict[t]["averageWidth"] /= summaryDict[t]["objectsNumber"] summaryDict[t]["averageRelativeWidth"] = summaryDict[t][ "averageWidth"] summaryDict[t]["averageAspectRatio"] /= summaryDict[t][ "objectsNumber"] summaryDict[t]["averageRelativeAspectRatio"] = summaryDict[t][ "averageAspectRatio"] summaryFile.write( "{} {} {} {} {} {} {} {} {} {} {} {} {} {}\n".format( t + 1, t, summaryDict[t]["objectsNumber"], summaryDict[t]["validObjectsNumber"], summaryDict[t]["averageDuration"], summaryDict[t]["averageSpeed"], summaryDict[t]["averageAngularSpeed"], summaryDict[t]["averageLength"], summaryDict[t]["averageRelativeLength"], summaryDict[t]["averageWidth"], summaryDict[t]["averageRelativeWidth"], summaryDict[t]["averageAspectRatio"], summaryDict[t]["averageRelativeAspectRatio"], summaryDict[t]["endWiggle"], )) summaryFile.close() # Generate contour string and save .blobs file blobsFile = open(filename + ".blobs", "w") for idx in sorted(contoursDict): # blobsFile.write('%{}\n'.format(int(idx))) count = 0 for t in sorted(contoursDict[idx]): nodeId = (t, idx) if hypothesesGraph.hasNode( nodeId ) and "lineageId" in hypothesesGraph._graph.node[nodeId]: lineageId = hypothesesGraph._graph.node[nodeId][ "lineageId"] contour = contoursDict[idx][t] count += 1 time = t xCoord = features[t]["Standard Object Features"][ "RegionCenter"][idx, 0] yCoord = features[t]["Standard Object Features"][ "RegionCenter"][idx, 1] area = features[t]["Standard Object Features"]["Count"][ idx, 0] xMajorAxis = (features[t]["Standard Object Features"] ["RegionAxes"][idx, 0] * features[t]["Standard Object Features"] ["RegionRadii"][idx, 0]) yMajorAxis = (features[t]["Standard Object Features"] ["RegionAxes"][idx, 1] * features[t]["Standard Object Features"] ["RegionRadii"][idx, 0]) std = np.mean([ features[t]["Standard Object Features"]["RegionRadii"][ idx, 0], features[t]["Standard Object Features"]["RegionRadii"][ idx, 1], ]) length = features[t]["Standard Object Features"][ "RegionRadii"][idx, 0] width = features[t]["Standard Object Features"][ "RegionRadii"][idx, 1] xOffset = contour[0, 0] yOffset = contour[0, 1] # Generate contour string prevPoint = [] contourLength = 0 binaryString = "" pointsString = "" for i, point in enumerate(contour): if len(prevPoint) > 0: # 4-way connected points if point[0] - prevPoint[0] == -1 and point[ 1] - prevPoint[1] == 0: binaryString += "00" contourLength += 1 elif point[0] - prevPoint[0] == 1 and point[ 1] - prevPoint[1] == 0: binaryString += "01" contourLength += 1 elif point[0] - prevPoint[0] == 0 and point[ 1] - prevPoint[1] == -1: binaryString += "10" contourLength += 1 elif point[0] - prevPoint[0] == 0 and point[ 1] - prevPoint[1] == 1: binaryString += "11" contourLength += 1 # Diagonal points elif point[0] - prevPoint[0] == -1 and point[ 1] - prevPoint[1] == -1: binaryString += "00" binaryString += "10" contourLength += 2 elif point[0] - prevPoint[0] == -1 and point[ 1] - prevPoint[1] == 1: binaryString += "00" binaryString += "11" contourLength += 2 elif point[0] - prevPoint[0] == 1 and point[ 1] - prevPoint[1] == -1: binaryString += "01" binaryString += "10" contourLength += 2 elif point[0] - prevPoint[0] == 1 and point[ 1] - prevPoint[1] == 1: binaryString += "01" binaryString += "11" contourLength += 2 if len(binaryString) == 6: pointsString += chr( int(binaryString, 2) + ord("0")) binaryString = "" elif len(binaryString) > 6: diff = len(binaryString) - 6 pointsString += chr( int(binaryString[:-diff], 2) + ord("0")) binaryString = binaryString[-diff:] elif i >= contour.shape[0] - 1: if len(binaryString) == 2: binaryString += "00001" pointsString += chr( int(binaryString, 2) + ord("0")) binaryString = "" elif len(binaryString) == 4: binaryString += "00" pointsString += chr( int(binaryString, 2) + ord("0")) binaryString = "" prevPoint = point # Create contour string and append to file contourString = "{} {} {} {} {} {} {} {} {} {} %% {} {} {} ".format( count, time, xCoord, yCoord, area, xMajorAxis, yMajorAxis, std, length, width, xOffset, yOffset, contourLength + 1, ) contourString += pointsString if count == 1: blobsFile.write("% {}\n".format(int(idx))) blobsFile.write(contourString + "\n") blobsFile.close() return True
def compute_all_features(): # Compute features in parallel pool = RequestPool() for t in range(tMax): pool.add( Request( partial(compute_features_for_frame, tIndex, t, features) ) ) pool.wait()
def _execute_graphcut(self, roi, result): for i in (0, 4): assert roi.stop[i] - roi.start[i] == 1,\ "Invalid roi for graph-cut: {}".format(str(roi)) t = roi.start[0] c = roi.start[4] margin = self.Margin.value beta = self.Beta.value MAXBOXSIZE = 10000000 # FIXME justification?? ## request the bounding box coordinates ## # the trailing index brackets give us the dictionary (instead of an # array of size 1) feats = self.BoundingBoxes.get(roi).wait() mins = feats["Coord<Minimum>"] maxs = feats["Coord<Maximum>"] nobj = mins.shape[0] # these are indices, so they should have an index datatype mins = mins.astype(np.uint32) maxs = maxs.astype(np.uint32) ## request the prediction image ## pred = self.Prediction.get(roi).wait() pred = vigra.taggedView(pred, axistags=self.Prediction.meta.axistags) pred = pred.withAxes(*'xyz') ## request the connected components image ## cc = self.LabelImage.get(roi).wait() cc = vigra.taggedView(cc, axistags=self.LabelImage.meta.axistags) cc = cc.withAxes(*'xyz') # provide xyz view for the output (just need 8bit for segmentation resultXYZ = vigra.taggedView(np.zeros(cc.shape, dtype=np.uint8), axistags='xyz') def processSingleObject(i): logger.debug("processing object {}".format(i)) # maxs are inclusive, so we need to add 1 xmin = max(mins[i][0] - margin[0], 0) ymin = max(mins[i][1] - margin[1], 0) zmin = max(mins[i][2] - margin[2], 0) xmax = min(maxs[i][0] + margin[0] + 1, cc.shape[0]) ymax = min(maxs[i][1] + margin[1] + 1, cc.shape[1]) zmax = min(maxs[i][2] + margin[2] + 1, cc.shape[2]) ccbox = cc[xmin:xmax, ymin:ymax, zmin:zmax] resbox = resultXYZ[xmin:xmax, ymin:ymax, zmin:zmax] nVoxels = ccbox.size if nVoxels > MAXBOXSIZE: #problem too large to run graph cut, assign to seed logger.warn("Object {} too large for graph cut.".format(i)) resbox[ccbox == i] = 1 return probbox = pred[xmin:xmax, ymin:ymax, zmin:zmax] gcsegm = segmentGC(probbox, beta) gcsegm = vigra.taggedView(gcsegm, axistags='xyz') ccsegm = vigra.analysis.labelVolumeWithBackground( gcsegm.astype(np.uint8)) # Extended bboxes of different objects might overlap. # To avoid conflicting segmentations, we find all connected # components in the results and only take the one, which # overlaps with the object "core" or "seed", defined by the # pre-thresholding seed = ccbox == i filtered = seed * ccsegm passed = vigra.analysis.unique(filtered.astype(np.uint32)) assert len(passed.shape) == 1 if passed.size > 2: logger.warn("ambiguous label assignment for region {}".format( (xmin, xmax, ymin, ymax, zmin, zmax))) resbox[ccbox == i] = 1 elif passed.size <= 1: logger.warn("box {} segmented out with beta {}".format( i, beta)) else: # assign to the overlap region label = passed[1] # 0 is background resbox[ccsegm == label] = 1 pool = RequestPool() #FIXME make sure that the parallel computations fit into memory for i in range(1, nobj): req = Request(functools.partial(processSingleObject, i)) pool.add(req) logger.info("Processing {} objects ...".format(nobj - 1)) pool.wait() pool.clean() logger.info("object loop done") # prepare result resView = vigra.taggedView(result, axistags=self.Output.meta.axistags) resView = resView.withAxes(*'xyz') # some labels could have been removed => relabel vigra.analysis.labelVolumeWithBackground(resultXYZ, out=resView)
def _execute_graphcut(self, roi, result): for i in (0, 4): assert roi.stop[i] - roi.start[i] == 1,\ "Invalid roi for graph-cut: {}".format(str(roi)) t = roi.start[0] c = roi.start[4] margin = self.Margin.value beta = self.Beta.value MAXBOXSIZE = 10000000 # FIXME justification?? ## request the bounding box coordinates ## # the trailing index brackets give us the dictionary (instead of an # array of size 1) feats = self.BoundingBoxes.get(roi).wait() mins = feats["Coord<Minimum>"] maxs = feats["Coord<Maximum>"] nobj = mins.shape[0] # these are indices, so they should have an index datatype mins = mins.astype(np.uint32) maxs = maxs.astype(np.uint32) ## request the prediction image ## pred = self.Prediction.get(roi).wait() pred = vigra.taggedView(pred, axistags=self.Prediction.meta.axistags) pred = pred.withAxes(*'xyz') ## request the connected components image ## cc = self.LabelImage.get(roi).wait() cc = vigra.taggedView(cc, axistags=self.LabelImage.meta.axistags) cc = cc.withAxes(*'xyz') # provide xyz view for the output (just need 8bit for segmentation resultXYZ = vigra.taggedView(np.zeros(cc.shape, dtype=np.uint8), axistags='xyz') def processSingleObject(i): logger.debug("processing object {}".format(i)) # maxs are inclusive, so we need to add 1 xmin = max(mins[i][0]-margin[0], 0) ymin = max(mins[i][1]-margin[1], 0) zmin = max(mins[i][2]-margin[2], 0) xmax = min(maxs[i][0]+margin[0]+1, cc.shape[0]) ymax = min(maxs[i][1]+margin[1]+1, cc.shape[1]) zmax = min(maxs[i][2]+margin[2]+1, cc.shape[2]) ccbox = cc[xmin:xmax, ymin:ymax, zmin:zmax] resbox = resultXYZ[xmin:xmax, ymin:ymax, zmin:zmax] nVoxels = ccbox.size if nVoxels > MAXBOXSIZE: #problem too large to run graph cut, assign to seed logger.warn("Object {} too large for graph cut.".format(i)) resbox[ccbox == i] = 1 return probbox = pred[xmin:xmax, ymin:ymax, zmin:zmax] gcsegm = segmentGC(probbox, beta) gcsegm = vigra.taggedView(gcsegm, axistags='xyz') ccsegm = vigra.analysis.labelVolumeWithBackground( gcsegm.astype(np.uint8)) # Extended bboxes of different objects might overlap. # To avoid conflicting segmentations, we find all connected # components in the results and only take the one, which # overlaps with the object "core" or "seed", defined by the # pre-thresholding seed = ccbox == i filtered = seed*ccsegm passed = vigra.analysis.unique(filtered.astype(np.uint32)) assert len(passed.shape) == 1 if passed.size > 2: logger.warn("ambiguous label assignment for region {}".format( (xmin, xmax, ymin, ymax, zmin, zmax))) resbox[ccbox == i] = 1 elif passed.size <= 1: logger.warn( "box {} segmented out with beta {}".format(i, beta)) else: # assign to the overlap region label = passed[1] # 0 is background resbox[ccsegm == label] = 1 pool = RequestPool() #FIXME make sure that the parallel computations fit into memory for i in range(1, nobj): req = Request(functools.partial(processSingleObject, i)) pool.add(req) logger.info("Processing {} objects ...".format(nobj-1)) pool.wait() pool.clean() logger.info("object loop done") # prepare result resView = vigra.taggedView(result, axistags=self.Output.meta.axistags) resView = resView.withAxes(*'xyz') # some labels could have been removed => relabel vigra.analysis.labelVolumeWithBackground(resultXYZ, out=resView)
def export(self, filename, hypothesesGraph, pluginExportContext): """ Export the contours, head index, and corresponding IDs for all objects on the video. Each .contours file consists of a line for each object in the video. All the values are space separated: the first number is the time (frame number), followed by the ID, the head index, the number of xy pairs in the contour, and an array of xy pairs. :param filename: string of the FILE where to save the result (different .xml files were) :param hypothesesGraph: hytra.core.hypothesesgraph.HypothesesGraph filled with a solution :param pluginExportContext: instance of ilastik.plugins.PluginExportContext containing: labelImageSlot (required here), rawImageSlot (required here) as well as objectFeaturesSlot, additionalPluginArgumentsSlot :returns: True on success, False otherwise """ headIndsDict = {} contoursDict = {} labelImageSlot = pluginExportContext.labelImageSlot rawImageSlot = pluginExportContext.rawImageSlot cIndex = labelImageSlot.meta.axistags.index("c") tIndex = labelImageSlot.meta.axistags.index("t") tMax = labelImageSlot.meta.shape[tIndex] # Method to compute contours for single frame (called in parallel by a request parallel) def compute_contours_for_frame(tIndex, t, labelImageSlot, hypothesesGraph, contoursDict): roi = [slice(None) for i in range(len(labelImageSlot.meta.shape))] roi[tIndex] = slice(t, t + 1) roi = tuple(roi) rawFrame = rawImageSlot[roi].wait() frame = labelImageSlot[roi].wait() frame = frame.squeeze() for idx in vigra.analysis.unique(frame): nodeId = (t, idx) if hypothesesGraph.hasNode( nodeId ) and "lineageId" in hypothesesGraph._graph.node[nodeId]: # Generate frame with single label idx frameSingleLabel = np.zeros(frame.shape).astype(np.uint8) frameSingleLabel[frame == idx] = 1 # Find contours using skimage marching squares contours = measure._find_contours.find_contours( frameSingleLabel, 0) # Doing a very stupid slow convolution to sum the head probabilities for each xy point on the contour. headProbs = [] for coord in contours[0]: prob = 0 for i in range(-CONV_WINDOW_SIZE, CONV_WINDOW_SIZE + 1): for j in range(-CONV_WINDOW_SIZE, CONV_WINDOW_SIZE + 1): if (coord[0] + i >= 0 and coord[0] + i < rawFrame.shape[1] and coord[1] + j >= 0 and coord[1] + j < rawFrame.shape[2]): prob += rawFrame[0, coord[0] + i, coord[1] + j, 0, 2] headProbs.append(prob) headInd = np.argmax(headProbs) # Save contours and head index to dicts lineageId = hypothesesGraph._graph.node[nodeId][ "lineageId"] if t in contoursDict: contoursDict[t][lineageId] = contours[0] else: contoursDict[t] = {lineageId: contours[0]} if t in headIndsDict: headIndsDict[t][lineageId] = headInd else: headIndsDict[t] = {lineageId: headInd} # Compute the contours in parallel pool = RequestPool() for t in range(tMax): pool.add( Request( partial(compute_contours_for_frame, tIndex, t, labelImageSlot, hypothesesGraph, contoursDict))) pool.wait() # Save contours and head index in .contours file outlineFile = open(filename + ".contours", "w") for t in sorted(contoursDict): for id in contoursDict[t]: contour = contoursDict[t][id] # Frame number, id, head index, contour length, contour xy pairs contourString = str(t) + " " + str(id) + " " + str( headIndsDict[t][id]) + " " + str(len(contour)) + " " contourString += " ".join( str(contour[i, 1]) + " " + str(contour[i, 0]) for i in range(len(contour))) contourString += "\n" outlineFile.write(contourString) outlineFile.close() return True
def execute(self, slot, subindex, roi, result): assert slot in [ self.Predictions, self.Probabilities, self.CachedProbabilities, self.ProbabilityChannels, self.BadObjects ] times = roi._l if len(times) == 0: # we assume that 0-length requests are requesting everything times = range(self.Predictions.meta.shape[0]) if slot is self.CachedProbabilities: return { t: self.prob_cache[t] for t in times if t in self.prob_cache } forests = self.inputs["Classifier"][:].wait() if forests is None or forests[0] is None: # this happens if there was no data to train with return dict((t, numpy.array([])) for t in times) feats = {} prob_predictions = {} selected = self.SelectedFeatures([]).wait() # FIXME: self.prob_cache is shared, so we need to block. # However, this makes prediction single-threaded. self.lock.acquire() try: for t in times: if t in self.prob_cache: continue tmpfeats = self.Features([t]).wait() ftmatrix, _, col_names = make_feature_array(tmpfeats, selected) rows, cols = replace_missing(ftmatrix) self.bad_objects[t] = numpy.zeros((ftmatrix.shape[0], )) self.bad_objects[t][rows] = 1 feats[t] = ftmatrix prob_predictions[t] = [0] * len(forests) def predict_forest(_t, forest_index): # Note: We can't use RandomForest.predictLabels() here because we're training in parallel, # and we have to average the PROBABILITIES from all forests. # Averaging the label predictions from each forest is NOT equivalent. # For details please see wikipedia: # http://en.wikipedia.org/wiki/Electoral_College_%28United_States%29#Irrelevancy_of_national_popular_vote # (^-^) prob_predictions[_t][forest_index] = forests[ forest_index].predictProbabilities(feats[_t].astype( numpy.float32)) # predict the data with all the forests in parallel pool = RequestPool() for t in times: if t in self.prob_cache: continue for i, f in enumerate(forests): req = Request(partial(predict_forest, t, i)) pool.add(req) pool.wait() pool.clean() for t in times: if t not in self.prob_cache: # prob_predictions is a dict-of-lists-of-arrays, indexed as follows: # prob_predictions[t][forest_index][object_index, class_index] # Stack the forests together and average them. stacked_predictions = numpy.array(prob_predictions[t]) averaged_predictions = numpy.average(stacked_predictions, axis=0) assert averaged_predictions.shape[0] == len(feats[t]) self.prob_cache[t] = averaged_predictions self.prob_cache[t][ 0] = 0 # Background probability is always zero if slot == self.Probabilities: return {t: self.prob_cache[t] for t in times} elif slot == self.Predictions: # FIXME: Support SegmentationThreshold again... labels = dict() for t in times: prob_sum = numpy.sum(self.prob_cache[t], axis=1) labels[t] = 1 + numpy.argmax(self.prob_cache[t], axis=1) labels[t][0] = 0 # Background gets the zero label return labels elif slot == self.ProbabilityChannels: try: prob_single_channel = { t: self.prob_cache[t][:, subindex[0]] for t in times } except: # no probabilities available for this class; return zeros prob_single_channel = { t: numpy.zeros((self.prob_cache[t].shape[0], 1)) for t in times } return prob_single_channel elif slot == self.BadObjects: return {t: self.bad_objects[t] for t in times} else: assert False, "Unknown input slot" finally: self.lock.release()
def execute(self, slot, subindex, roi, result): featList = [] all_col_names = [] labelsList = [] # will be available at slot self.Warnings all_bad_objects = defaultdict(lambda: defaultdict(list)) all_bad_feats = set() selected = self.SelectedFeatures([]).wait() if len(selected) == 0: # no features - no predictions self.Classifier.setValue(None) return for i in range(len(self.Labels)): # FIXME: we should only compute the features if there are nonzero labels in this image feats = self.Features[i]([]).wait() # TODO: we should be able to use self.Labels[i].value, # but the current implementation of Slot.value() does not # do the right thing. labels = self.Labels[i]([]).wait() featstmp, row_names, col_names, labelstmp = make_feature_array( feats, selected, labels) if labelstmp.size == 0 or featstmp.size == 0: continue rows, cols = replace_missing(featstmp) featList.append(featstmp) all_col_names.append(tuple(col_names)) labelsList.append(labelstmp) for idx in rows: t, obj = row_names[idx] all_bad_objects[i][t].append(obj) for c in cols: all_bad_feats.add(col_names[c]) if len(labelsList) == 0: #no labels, return here self.Classifier.setValue(None) return self._warnBadObjects(all_bad_objects, all_bad_feats) if not len(set(all_col_names)) == 1: raise Exception( 'different time slices did not have same features.') featMatrix = _concatenate(featList, axis=0) labelsMatrix = _concatenate(labelsList, axis=0) logger.info("training on matrix of shape {}".format(featMatrix.shape)) if featMatrix.size == 0 or labelsMatrix.size == 0: result[:] = None return oob = [0] * self.ForestCount.value try: # train and store forests in parallel pool = RequestPool() for i in range(self.ForestCount.value): def train_and_store(number): result[number] = vigra.learning.RandomForest( self._tree_count) oob[number] = result[number].learnRF( featMatrix.astype(numpy.float32), numpy.asarray(labelsMatrix, dtype=numpy.uint32)) req = Request(partial(train_and_store, i)) pool.add(req) pool.wait() pool.clean() except: logger.warn("couldn't learn classifier") raise oob_total = numpy.mean(oob) logger.info( "training finished, out of bag error: {}".format(oob_total)) return result
def extractHistograms(volume, labels, patchSize=64, haloSize=0, nBins=30, intRange=(0, 255), appendPositions=False): ''' extracts histograms from 3d-volume - labels are 0 ignore 1 positive 2 negative - histogram extraction is attempted to be done in parallel - patches that intersect with the volume border are discarded - volume and labels must be 3d, and in order 'zyx' (if not VigraArrays) - returns: np.ndarray, shape: (nSamples,nBins+1), last column is the label ''' # progress reporter class, histogram extraction can take quite a long time class ProgressReporter(object): lock = None def __init__(self, nThreads): self.lock = ThreadLock() self.nThreads = nThreads self.status = np.zeros((nThreads,)) def report(self, index): self.lock.acquire() self.status[index] = 1 logger.debug("Finished threads: %d/%d." % (self.status.sum(), len(self.status))) self.lock.release() # sanity checks assert len(volume.shape) == 3, "Volume must be 3d data" assert volume.shape == labels.shape,\ "Volume and labels must have the same shape" try: volumeZYX = volume.withAxes(*'zyx') labelsZYX = labels.withAxes(*'zyx') except AttributeError: # can't blame me volumeZYX = volume labelsZYX = labels pass # compute actual patch size patchSize = patchSize + 2*haloSize # fill list of patch centers (VigraArray does not support bitwise_or) ind_z, ind_y, ind_x = np.where( (labelsZYX == 1).view(np.ndarray) | (labelsZYX == 2).view(np.ndarray)) index = np.arange(len(ind_z)) # prepare chunking of histogram centers chunkSize = 10000 # FIXME magic number?? nChunks = len(index)//chunkSize + (1 if len(index) % chunkSize > 0 else 0) sliceList = [slice(k*chunkSize, min((k+1)*chunkSize, len(index))) for k in range(nChunks)] histoList = [None]*nChunks # prepare subroutine for parallel extraction reporter = ProgressReporter(nChunks) #BEGIN subroutine def _extractHistogramsSub(itemList): xs = ind_x[itemList] ys = ind_y[itemList] zs = ind_z[itemList] ymin = ys - patchSize//2 ymax = ymin + patchSize xmin = xs - patchSize//2 xmax = xmin + patchSize validPatchIndices = np.where( np.all( (ymin >= 0, xmin >= 0, xmax <= volumeZYX.shape[2], ymax <= volumeZYX.shape[1]), axis=0))[0] if appendPositions: out = np.zeros((len(validPatchIndices), nBins+4)) else: out = np.zeros((len(validPatchIndices), nBins+1)) for k, patchInd in enumerate(validPatchIndices): x = xs[patchInd] y = ys[patchInd] z = zs[patchInd] vol = volumeZYX[z, ymin[patchInd]:ymax[patchInd], xmin[patchInd]:xmax[patchInd]] (out[k, :nBins], _) = np.histogram( vol, bins=nBins, range=intRange, density=True) out[k, nBins] = 1 if labelsZYX[z, y, x] == 1 else 0 if appendPositions: out[k, nBins+1:] = [z, y, x] return out def partFun(i): itemList = index[sliceList[i]] histos = _extractHistogramsSub(itemList) histoList[i] = histos reporter.report(i) #END subroutine # pool the extraction requests pool = RequestPool() for i in range(nChunks): req = Request(partial(partFun, i)) pool.add(req) pool.wait() pool.clean() return np.vstack(histoList)
def execute(self, slot, subindex, rroi, result): key = roiToSlice(rroi.start, rroi.stop) cnt = 0 written = 0 start, stop = roi.sliceToRoi(key, self.outputs["Output"].meta.shape) assert (stop <= self.outputs["Output"].meta.shape).all() #axisindex = self.inputs["AxisIndex"].value flag = self.inputs["AxisFlag"].value axisindex = self.outputs["Output"].meta.axistags.index(flag) #ugly-ugly-ugly oldkey = list(key) oldkey.pop(axisindex) #print "STACKER: ", flag, axisindex #print "requesting an outslot from stacker:", key, result.shape #print "input slots total: ", len(self.inputs['Images']) requests = [] pool = RequestPool() for i, inSlot in enumerate(self.inputs['Images']): req = None inTagKeys = [ax.key for ax in inSlot.meta.axistags] if flag in inTagKeys: slices = inSlot.meta.shape[axisindex] if cnt + slices >= start[axisindex] and start[ axisindex] - cnt < slices and start[ axisindex] + written < stop[axisindex]: begin = 0 if cnt < start[axisindex]: begin = start[axisindex] - cnt end = slices if cnt + end > stop[axisindex]: end -= cnt + end - stop[axisindex] key_ = copy.copy(oldkey) key_.insert(axisindex, slice(begin, end, None)) reskey = [ slice(None, None, None) for x in range(len(result.shape)) ] reskey[axisindex] = slice(written, written + end - begin, None) req = inSlot[tuple(key_)].writeInto(result[tuple(reskey)]) written += end - begin cnt += slices else: if cnt >= start[axisindex] and start[ axisindex] + written < stop[axisindex]: #print "key: ", key, "reskey: ", reskey, "oldkey: ", oldkey #print "result: ", result.shape, "inslot:", inSlot.meta.shape reskey = [slice(None, None, None) for s in oldkey] reskey.insert(axisindex, written) destArea = result[tuple(reskey)] req = inSlot[tuple(oldkey)].writeInto(destArea) written += 1 cnt += 1 if req is not None: pool.add(req) pool.wait() pool.clean()
def _executeOutput(self, slot, subindex, roi, result): t = time.time() key = roi.toSlice() shape = self.Output.meta.shape start, stop = sliceToRoi(key, shape) self._lock.acquire() ch = self._cacheHits ch += 1 self._cacheHits = ch self._running += 1 if self._cache is None: self._allocateCache() cacheView = self._cache[:] #prevent freeing of cache during running this function blockStart = (1.0 * start / self._blockShape).floor() blockStop = (1.0 * stop / self._blockShape).ceil() blockKey = roiToSlice(blockStart, blockStop) blockSet = self._blockState[blockKey] # this is a little optimization to shortcut # many lines of python code when all data is # is already in the cache: if numpy.logical_or(blockSet == OpArrayCache.CLEAN, blockSet == OpArrayCache.FIXED_DIRTY).all(): result[:] = self._cache[roiToSlice(start, stop)] self._running -= 1 self._updatePriority() cacheView = None self._lock.release() return inProcessQueries = numpy.unique( numpy.extract(blockSet == OpArrayCache.IN_PROCESS, self._blockQuery[blockKey])) cond = (blockSet == OpArrayCache.DIRTY) tileWeights = fastWhere(cond, 1, 128**3, numpy.uint32) trueDirtyIndices = numpy.nonzero(cond) tileArray = drtile.test_DRTILE(tileWeights, 128**3).swapaxes(0, 1) dirtyRois = [] half = tileArray.shape[0] / 2 dirtyPool = RequestPool() for i in range(tileArray.shape[1]): drStart3 = tileArray[:half, i] drStop3 = tileArray[half:, i] drStart2 = drStart3 + blockStart drStop2 = drStop3 + blockStart drStart = drStart2 * self._blockShape drStop = drStop2 * self._blockShape shape = self.Output.meta.shape drStop = numpy.minimum(drStop, shape) drStart = numpy.minimum(drStart, shape) key3 = roiToSlice(drStart3, drStop3) key2 = roiToSlice(drStart2, drStop2) key = roiToSlice(drStart, drStop) if not self._fixed: dirtyRois.append([drStart, drStop]) req = self.inputs["Input"][key].writeInto(self._cache[key]) req.uncancellable = True #FIXME dirtyPool.add(req) self._blockQuery[key2] = weakref.ref(req) #sanity check: if (self._blockState[key2] != OpArrayCache.DIRTY).any(): logger.warning("original condition" + str(cond)) logger.warning("original tilearray {} {}".format( tileArray, tileArray.shape)) logger.warning("original tileWeights {} {}".format( tileWeights, tileWeights.shape)) logger.warning("sub condition {}".format( self._blockState[key2] == OpArrayCache.DIRTY)) logger.warning("START={}, STOP={}".format( drStart2, drStop2)) import h5py with h5py.File("test.h5", "w") as f: f.create_dataset("data", data=tileWeights) logger.warning( "%r \n %r \n %r\n %r\n %r \n%r" % (key2, blockKey, self._blockState[key2], self._blockState[blockKey][trueDirtyIndices], self._blockState[blockKey], tileWeights)) assert False self._blockState[key2] = OpArrayCache.IN_PROCESS # indicate the inprocessing state, by setting array to 0 (i.e. IN_PROCESS) if not self._fixed: blockSet[:] = fastWhere(cond, OpArrayCache.IN_PROCESS, blockSet, numpy.uint8) else: # Someone asked for some dirty blocks while we were fixed. # Mark these blocks to be signaled as dirty when we become unfixed blockSet[:] = fastWhere(cond, OpArrayCache.FIXED_DIRTY, blockSet, numpy.uint8) self._has_fixed_dirty_blocks = True self._lock.release() temp = itertools.count(0) #wait for all requests to finish dirtyPool.wait() if len(dirtyPool) > 0: # Signal that something was updated. # Note that we don't need to do this for the 'in process' queries (below) # because they are already in the dirtyPool in some other thread self.Output._sig_value_changed() dirtyPool.clean() # indicate the finished inprocess state (i.e. CLEAN) if not self._fixed and temp.next() == 0: with self._lock: blockSet[:] = fastWhere(cond, OpArrayCache.CLEAN, blockSet, numpy.uint8) self._blockQuery[blockKey] = fastWhere( cond, None, self._blockQuery[blockKey], object) inProcessPool = RequestPool() #wait for all in process queries for req in inProcessQueries: req = req() # get original req object from weakref if req is not None: inProcessPool.add(req) inProcessPool.wait() inProcessPool.clean() # finally, store results in result area self._lock.acquire() if self._cache is not None: result[:] = self._cache[roiToSlice(start, stop)] else: self.inputs["Input"][roiToSlice(start, stop)].writeInto(result).wait() self._running -= 1 self._updatePriority() cacheView = None self._lock.release() self.logger.debug("read %s took %f sec." % (roi.pprint(), time.time() - t))
def execute(self, slot, subindex, roi, result): assert slot == self.LabelAndFeatureMatrix self.progressSignal(0.0) # Technically, this could result in strange progress reporting if execute() # is called by multiple threads in parallel. # This could be fixed with some fancier progress state, but # (1) We don't expect that to by typical, and # (2) progress reporting is merely informational. num_dirty_blocks = len(self._dirty_blocks) remaining_dirty = [num_dirty_blocks] def update_progress(result): remaining_dirty[0] -= 1 percent_complete = 95.0 * (num_dirty_blocks - remaining_dirty[0]) / num_dirty_blocks self.progressSignal(percent_complete) # Update all dirty blocks in the cache logger.debug("Updating {} dirty blocks".format(num_dirty_blocks)) # Before updating the blocks, ensure that the necessary block locks exist # It's better to do this now instead of inside each request # to avoid contention over self._lock with self._lock: for block_start in self._dirty_blocks: if block_start not in self._block_locks: self._block_locks[block_start] = RequestLock() # Update each block in its own request. pool = RequestPool() reqs = {} for block_start in self._dirty_blocks: req = Request(partial(self._get_features_for_block, block_start)) req.notify_finished(update_progress) reqs[block_start] = req pool.add(req) pool.wait() # Now store the results we got. # It's better to store the blocks here -- rather than within each request -- to # avoid contention over self._lock from within every block's request. with self._lock: for block_start, req in reqs.items(): if req.result is None: # 'None' means the block wasn't dirty. No need to update. continue labels_and_features_matrix = req.result self._dirty_blocks.remove(block_start) if labels_and_features_matrix.shape[0] > 0: # Update the block entry with the new matrix. self._blockwise_feature_matrices[ block_start] = labels_and_features_matrix else: # All labels were removed from the block, # So the new feature matrix is empty. # Just delete its entry from our list. try: del self._blockwise_feature_matrices[block_start] except KeyError: pass # Concatenate the all blockwise results if self._blockwise_feature_matrices: total_feature_matrix = numpy.concatenate( self._blockwise_feature_matrices.values(), axis=0) else: # No label points at all. # Return an empty label&feature matrix (of the correct shape) num_feature_channels = self.FeatureImage.meta.shape[-1] total_feature_matrix = numpy.ndarray(shape=(0, 1 + num_feature_channels), dtype=numpy.float32) self.progressSignal(100.0) logger.debug("After update, there are {} clean blocks".format( len(self._blockwise_feature_matrices))) result[0] = total_feature_matrix
def _extract(self, image, labels, atlas=None): if not (image.ndim == labels.ndim == 4): raise Exception("both images must be 4D. raw image shape: {}" " label image shape: {}".format( image.shape, labels.shape)) # FIXME: maybe simplify? taggedShape should be easier here class Axes(object): x = image.axistags.index('x') y = image.axistags.index('y') z = image.axistags.index('z') c = image.axistags.index('c') axes = Axes() slc3d = [slice(None)] * 4 # FIXME: do not hardcode slc3d[axes.c] = 0 labels = labels[slc3d] #These are the feature names, selected by the user and the default feature names. feature_names = deepcopy(self.Features([]).wait()) feature_names = self._augmentFeatureNames(feature_names) # do global features logger.debug("Computing global and default features") global_features = {} pool = RequestPool() def compute_for_one_plugin(plugin_name, feature_dict): plugin_inner = pluginManager.getPluginByName( plugin_name, "ObjectFeatures") global_features[ plugin_name] = plugin_inner.plugin_object.compute_global( image, labels, feature_dict, axes) for plugin_name, feature_dict in feature_names.items(): if plugin_name != default_features_key: pool.add( Request( partial(compute_for_one_plugin, plugin_name, feature_dict))) pool.wait() extrafeats = {} for feat_key in default_features: try: sel = feature_names["Standard Object Features"][feat_key][ "selected"] except KeyError: # we don't always set this property to True, sometimes it's just not there. The only important # thing is that it's not False sel = True if not sel: # This feature has not been selected by the user. Remove it from the computed dict into a special dict # for default features feature = global_features["Standard Object Features"].pop( feat_key) else: feature = global_features["Standard Object Features"][feat_key] extrafeats[feat_key] = feature if atlas is not None: extrafeats['AtlasMapping'] = self._createAtlasMapping( extrafeats['RegionCenter'], atlas) extrafeats = dict( (k.replace(' ', ''), v) for k, v in extrafeats.items()) mincoords = extrafeats["Coord<Minimum>"].astype(int) maxcoords = extrafeats["Coord<Maximum>"].astype(int) nobj = mincoords.shape[0] # local features: loop over all objects def dictextend(a, b): for key in b: a[key].append(b[key]) return a local_features = collections.defaultdict( lambda: collections.defaultdict(list)) margin = max_margin(feature_names) has_local_features = {} for plugin_name, feature_dict in feature_names.items(): has_local_features[plugin_name] = False for features in feature_dict.values(): if 'margin' in features: has_local_features[plugin_name] = True break if numpy.any(margin) > 0: #starting from 0, we stripped 0th background object in global computation for i in range(0, nobj): logger.debug("processing object {}".format(i)) extent = self.compute_extent(i, image, mincoords, maxcoords, axes, margin) rawbbox = self.compute_rawbbox(image, extent, axes) #it's i+1 here, because the background has label 0 binary_bbox = numpy.where(labels[tuple(extent)] == i + 1, 1, 0).astype(numpy.bool) for plugin_name, feature_dict in feature_names.items(): if not has_local_features[plugin_name]: continue plugin = pluginManager.getPluginByName( plugin_name, "ObjectFeatures") feats = plugin.plugin_object.compute_local( rawbbox, binary_bbox, feature_dict, axes) local_features[plugin_name] = dictextend( local_features[plugin_name], feats) logger.debug("computing done, removing failures") # remove local features that failed for pname, pfeats in local_features.items(): for key in list(pfeats.keys()): value = pfeats[key] try: pfeats[key] = numpy.vstack( list(v.reshape(1, -1) for v in value)) except: logger.warning('feature {} failed'.format(key)) del pfeats[key] # merge the global and local features logger.debug("removed failed, merging") all_features = {} plugin_names = set(global_features.keys()) | set(local_features.keys()) for name in plugin_names: d1 = global_features.get(name, {}) d2 = local_features.get(name, {}) all_features[name] = dict(list(d1.items()) + list(d2.items())) all_features[default_features_key] = extrafeats # reshape all features for pfeats in all_features.values(): for key, value in pfeats.items(): if value.shape[0] != nobj: raise Exception( 'feature {} does not have enough rows, {} instead of {}' .format(key, value.shape[0], nobj)) # because object classification operator expects nobj to # include background. FIXME: we should change that assumption. value = numpy.vstack((numpy.zeros(value.shape[1]), value)) value = value.astype( numpy.float32) #turn Nones into numpy.NaNs assert value.dtype == numpy.float32 assert value.shape[0] == nobj + 1 assert value.ndim == 2 pfeats[key] = value logger.debug("merged, returning") return all_features
def export(self, filename, hypothesesGraph, pluginExportContext): """ Export the contours and corresponding IDs for all objects on the video Each .outline file consists of a line for each frame the animal is tracked for. The first number on this line is the timestamp of the frame, followed by the id, and unknown number, and then the rest of numbers are the (x,y) coordinates of the contour points (in what units?). :param filename: string of the FILE where to save the result (different .xml files were) :param hypothesesGraph: hytra.core.hypothesesgraph.HypothesesGraph filled with a solution :param pluginExportContext: instance of ilastik.plugins.PluginExportContext containing: labelImageSlot (required here) as well as objectFeaturesSlot, rawImageSlot, additionalPluginArgumentsSlot :returns: True on success, False otherwise """ contoursDict = {} labelImageSlot = pluginExportContext.labelImageSlot tIndex = labelImageSlot.meta.axistags.index("t") tMax = labelImageSlot.meta.shape[tIndex] # Method to compute contours for single frame (called in parallel by a request parallel) def compute_contours_for_frame(tIndex, t, labelImageSlot, hypothesesGraph, contoursDict): roi = [slice(None) for i in range(len(labelImageSlot.meta.shape))] roi[tIndex] = slice(t, t + 1) roi = tuple(roi) frame = labelImageSlot[roi].wait() frame = frame.squeeze() for idx in vigra.analysis.unique(frame): nodeId = (t, idx) if hypothesesGraph.hasNode( nodeId ) and "lineageId" in hypothesesGraph._graph.node[nodeId]: # Generate frame with single label idx frameSingleLabel = np.zeros(frame.shape).astype(np.uint8) frameSingleLabel[frame == idx] = 1 # Find contours using skimage marching squares contours = measure._find_contours.find_contours( frameSingleLabel, 0) # Save contours to dictionary lineageId = hypothesesGraph._graph.node[nodeId][ "lineageId"] if lineageId in contoursDict: contoursDict[lineageId][t] = contours[0] else: contoursDict[lineageId] = {t: contours[0]} # Compute the contours in parallel pool = RequestPool() for t in range(tMax): pool.add( Request( partial(compute_contours_for_frame, tIndex, t, labelImageSlot, hypothesesGraph, contoursDict))) pool.wait() # Generate contour string (from sorted dicts) and save .outline file outlineFile = open(filename + ".outline", "w") for id in sorted(contoursDict): for t in sorted(contoursDict[id]): # Generate contour string compatible with the .outline format contour = contoursDict[id][t] contourString = " ".join( str(contour[i, 1]) + " " + str(contour[i, 0]) for i in range(len(contour))) contourString = ("00000000_" + str(int(t)).zfill(6) + " " + str(int(id)).zfill(5) + " " + "0.000 " + contourString + "\n") # Append contour to file outlineFile.write(contourString) outlineFile.close() return True
def _execute_Output(self, slot, subindex, roi, result): """ Overridden from OpUnblockedArrayCache """ def copy_block(full_block_roi, clipped_block_roi): full_block_roi = numpy.asarray(full_block_roi) clipped_block_roi = numpy.asarray(clipped_block_roi) output_roi = numpy.asarray(clipped_block_roi) - roi.start block_roi = self._get_containing_block_roi(clipped_block_roi) # Skip cache and copy full block directly if self.BypassModeEnabled.value: full_block_data = self.Output.stype.allocateDestination( SubRegion(self.Output, *full_block_roi)) self.Input(*full_block_roi).writeInto(full_block_data).block() roi_within_block = clipped_block_roi - full_block_roi[0] self.Output.stype.copy_data( result[roiToSlice(*output_roi)], full_block_data[roiToSlice(*roi_within_block)]) # If data data exists already or we can just fetch it without needing extra scratch space, # just call the base class elif block_roi is not None or (full_block_roi == clipped_block_roi).all(): self._execute_Output_impl(clipped_block_roi, result[roiToSlice(*output_roi)]) elif self.Input.meta.dontcache: # Data isn't in the cache, but we don't need it in the cache anyway. self.Input(*clipped_block_roi).writeInto( result[roiToSlice(*output_roi)]).block() else: # Data doesn't exist yet in the cache. # Request the full block, but then discard the parts we don't need. # (We use allocateDestination() here to support MaskedArray types.) # TODO: We should probably just get rid of MaskedArray support altogether... full_block_data = self.Output.stype.allocateDestination( SubRegion(self.Output, *full_block_roi)) self._execute_Output_impl(full_block_roi, full_block_data) roi_within_block = clipped_block_roi - full_block_roi[0] self.Output.stype.copy_data( result[roiToSlice(*output_roi)], full_block_data[roiToSlice(*roi_within_block)]) clipped_block_rois = getIntersectingRois(self.Input.meta.shape, self._blockshape, (roi.start, roi.stop), True) full_block_rois = getIntersectingRois(self.Input.meta.shape, self._blockshape, (roi.start, roi.stop), False) pool = RequestPool() for full_block_roi, clipped_block_roi in zip(full_block_rois, clipped_block_rois): req = Request( partial(copy_block, full_block_roi, clipped_block_roi)) pool.add(req) pool.wait()
def _resolveMergers(self, hypothesesGraph, model): ''' run merger resolution on the hypotheses graph which contains the current solution ''' logger.info("Resolving mergers.") parameters = self.Parameters.value withTracklets = parameters['withTracklets'] originalGraph = hypothesesGraph.referenceTraxelGraph if withTracklets else hypothesesGraph resolvedMergersDict = {} # Enable full graph computation for animal tracking workflow withFullGraph = False if 'withAnimalTracking' in parameters and parameters[ 'withAnimalTracking']: # TODO: Setting this parameter outside of the track() function (on AnimalConservationTrackingWorkflow) is not desirable withFullGraph = True logger.info( "Computing full graph on merger resolver (Only enabled on animal tracking workflow)" ) mergerResolver = IlastikMergerResolver(originalGraph, pluginPaths=self.pluginPaths, withFullGraph=withFullGraph) # Check if graph contains mergers, otherwise skip merger resolving if not mergerResolver.mergerNum: logger.info( "Graph contains no mergers. Skipping merger resolving.") else: # Fit and refine merger nodes using a GMM # It has to be done per time-step in order to aviod loading the whole video on RAM traxelIdPerTimestepToUniqueIdMap, uuidToTraxelMap = getMappingsBetweenUUIDsAndTraxels( model) timesteps = [ int(t) for t in list(traxelIdPerTimestepToUniqueIdMap.keys()) ] timesteps.sort() timeIndex = self.LabelImage.meta.axistags.index('t') numTimeStep = len(timesteps) count = 0 for timestep in timesteps: count += 1 self.progressVisitor.showProgress( old_div(count, float(numTimeStep))) roi = [ slice(None) for i in range(len(self.LabelImage.meta.shape)) ] roi[timeIndex] = slice(timestep, timestep + 1) roi = tuple(roi) labelImage = self.LabelImage[roi].wait() # Get coordinates for object IDs in label image. Used by GMM merger fit. objectIds = vigra.analysis.unique(labelImage[0, ..., 0]) maxObjectId = max(objectIds) coordinatesForIds = {} pool = RequestPool() for objectId in objectIds: pool.add( Request( partial(mergerResolver.getCoordinatesForObjectId, coordinatesForIds, labelImage[0, ..., 0], timestep, objectId))) # Run requests to get object ID coordinates pool.wait() # Fit mergers and store fit info in nodes if coordinatesForIds: mergerResolver.fitAndRefineNodesForTimestep( coordinatesForIds, maxObjectId, timestep) self.parent.parent.trackingApplet.progressSignal(100) # Compute object features, re-run flow solver, update model and result, and get merger dictionary resolvedMergersDict = mergerResolver.run() return resolvedMergersDict
def execute(self, slot, subindex, roi, result): assert (roi.start >= 0).all(), \ "Requested roi is out-of-bounds: [{}, {}]".format( roi.start, roi.stop ) assert (roi.stop <= self.Input.meta.shape).all(), \ "Requested roi is out-of-bounds: [{}, {}] (exceeds shape {})"\ .format( roi.start, roi.stop, self.Input.meta.shape ) if not self._configured: # this happens when the operator is not yet fully configured due to fixAtCurrent == True result[:] = 0 return t = time.time() start, stop = roi.start, roi.stop blockStart = (start / self._blockShape) blockStop = (stop * 1.0 / self._blockShape).ceil() innerBlocks = self._get_block_numbers(blockStart, blockStop) pool = RequestPool() for block_index in innerBlocks.flat: #which part of the original key does this block fill? block_multi_index = self._get_block_multi_index(block_index) offset = self._blockShape * block_multi_index bigstart = numpy.maximum(offset, start) bigstop = numpy.minimum(offset + self._blockShape, stop) smallstart = bigstart - offset smallstop = bigstop - offset bigkey = roiToSlice(bigstart - start, bigstop - start) with self._lock: if not self._fixed: if not self._cache_list.has_key(block_index): self._opSub_list[block_index] = generic.OpSubRegion( parent=self) self._opSub_list[block_index].inputs["Input"].connect( self.inputs["Input"]) tstart = self._blockShape * block_multi_index tstop = numpy.minimum((block_multi_index + numpy.ones( block_multi_index.shape, numpy.uint8)) * self._blockShape, self.shape) self._opSub_list[block_index].Roi.setValue( (tuple(tstart), tuple(tstop))) self._cache_list[block_index] = OpArrayCache( parent=self) self._cache_list[block_index].inputs["Input"].connect( self._opSub_list[block_index].outputs["Output"]) self._cache_list[block_index].inputs[ "fixAtCurrent"].connect(self.fixAtCurrent) self._cache_list[block_index].inputs[ "blockShape"].setValue( self.inputs["innerBlockShape"].value) # we dont register a callback for dirtyness, since we already forward the signal # Forward value changed notifications to our own output. self._cache_list[ block_index].Output.notifyValueChanged( self.Output._sig_value_changed) if self._cache_list.has_key(block_index): op = self._cache_list[block_index] #req = self._cache_list[block_index].outputs["Output"][smallkey].writeInto(result[bigkey]) smallroi = SubRegion(op.outputs["Output"], start=smallstart, stop=smallstop) req = op.Output(smallroi.start, smallroi.stop) req.writeInto(result[bigkey]) pool.add(req) #op.execute(op.outputs["Output"], (), smallroi, result[bigkey]) ####op.getOutSlot(op.outputs["Output"],smallkey,result[bigkey]) #requests.append(req) else: #When this block has never been in the cache and the current #value is fixed (fixAtCurrent=True), return 0 values #This prevents random noise appearing in such cases. result[bigkey] = 0 with self._lock: # Since a downstream operator has expressed an interest in this block, # mark it to be signaled as dirty when we become unfixed. # Otherwise, downstream operators won't know when there's valid data in this block. self._fixed_dirty_blocks.add(block_index) pool.wait() self.logger.debug("read %r took %f msec." % (roi.pprint(), 1000.0 * (time.time() - t)))
def read(self, view_roi, result_out): """ roi: (start, stop) tuples, ordered according to description.output_axes roi should be relative to the view """ output_axes = self.description.output_axes roi_transposed = zip(*view_roi) roi_dict = dict(zip(output_axes, roi_transposed)) view_roi = zip(*(roi_dict['z'], roi_dict['y'], roi_dict['x'])) # First, normalize roi and result to zyx order result_out = vigra.taggedView(result_out, output_axes) result_out = result_out.withAxes(*'zyx') assert numpy.array(view_roi).shape == ( 2, 3), "Invalid roi for 3D volume: {}".format(view_roi) view_roi = numpy.array(view_roi) assert (result_out.shape == (view_roi[1] - view_roi[0])).all() # User gave roi according to the view output. # Now offset it find global roi. roi = view_roi + self.description.view_origin_zyx tile_blockshape = (1, ) + tuple(self.description.tile_shape_2d_yx) tile_starts = getIntersectingBlocks(tile_blockshape, roi) pool = RequestPool() for tile_start in tile_starts: tile_roi_in = getBlockBounds(self.description.bounds_zyx, tile_blockshape, tile_start) tile_roi_in = numpy.array(tile_roi_in) # This tile's portion of the roi intersecting_roi = getIntersection(roi, tile_roi_in) intersecting_roi = numpy.array(intersecting_roi) # Compute slicing within destination array and slicing within this tile destination_relative_intersection = numpy.subtract( intersecting_roi, roi[0]) tile_relative_intersection = intersecting_roi - tile_roi_in[0] # Get a view to the output slice result_region = result_out[roiToSlice( *destination_relative_intersection)] rest_args = self._get_rest_args(tile_blockshape, tile_roi_in) if self.description.tile_url_format.startswith('http'): retrieval_fn = partial(self._retrieve_remote_tile, rest_args, tile_relative_intersection, result_region) else: retrieval_fn = partial(self._retrieve_local_tile, rest_args, tile_relative_intersection, result_region) PARALLEL_REQ = True if PARALLEL_REQ: pool.add(Request(retrieval_fn)) else: # execute serially (leave the pool empty) retrieval_fn() if PARALLEL_REQ: with Timer() as timer: pool.wait() logger.info("Loading {} tiles took a total of {}".format( len(tile_starts), timer.seconds()))
def export(self, filename, hypothesesGraph, objectFeaturesSlot, labelImageSlot, rawImageSlot): """ Export the Multi-Worm-Tracker .summary and .blobs files. :param filename: string of the FILE where to save the result (different .xml files were) :param hypothesesGraph: hytra.core.hypothesesgraph.HypothesesGraph filled with a solution :param objectFeaturesSlot: lazyflow.graph.InputSlot, connected to the RegionFeaturesAll output of ilastik.applets.trackingFeatureExtraction.opTrackingFeatureExtraction.OpTrackingFeatureExtraction :returns: True on success, False otherwise """ # Get object features features = objectFeaturesSlot([]).wait() contoursDict = {} summaryDict = {} tIndex = labelImageSlot.meta.axistags.index('t') tMax = labelImageSlot.meta.shape[tIndex] # Method to compute contours for single frame (called in parallel by a request parallel) def compute_dicts_for_frame(tIndex, t, labelImageSlot, hypothesesGraph, contoursDict): roi = [slice(None) for i in range(len(labelImageSlot.meta.shape))] roi[tIndex] = slice(t, t + 1) roi = tuple(roi) frame = labelImageSlot[roi].wait() frame = frame.squeeze() for idx in vigra.analysis.unique(frame): nodeId = (t, idx) if hypothesesGraph.hasNode( nodeId ) and 'lineageId' in hypothesesGraph._graph.node[nodeId]: if t in summaryDict: summaryDict[t]['objectsNumber'] += 1 summaryDict[t]['validObjectsNumber'] += 1 summaryDict[t]['averageDuration'] = 0.0 summaryDict[t]['averageSpeed'] = 0.0 summaryDict[t]['averageAngularSpeed'] = 0.0 summaryDict[t]['averageLength'] += features[t][ 'Standard Object Features']['RegionRadii'][idx, 0] summaryDict[t]['averageRelativeLength'] = summaryDict[ t]['averageLength'] summaryDict[t]['averageWidth'] += features[t][ 'Standard Object Features']['RegionRadii'][idx, 1] summaryDict[t]['averageRelativeWidth'] = summaryDict[ t]['averageWidth'] summaryDict[t]['averageAspectRatio'] += old_div( features[t]['Standard Object Features'] ['RegionRadii'][idx, 0], features[t] ['Standard Object Features']['RegionRadii'][idx, 1]) summaryDict[t][ 'averageRelativeAspectRatio'] = summaryDict[t][ 'averageAspectRatio'] summaryDict[t]['endWiggle'] = 0.0 else: summaryDict[t] = {} summaryDict[t]['objectsNumber'] = 1 summaryDict[t]['validObjectsNumber'] = 1 summaryDict[t]['averageDuration'] = 0.0 summaryDict[t]['averageSpeed'] = 0.0 summaryDict[t]['averageAngularSpeed'] = 0.0 summaryDict[t]['averageLength'] = features[t][ 'Standard Object Features']['RegionRadii'][idx, 0] summaryDict[t]['averageRelativeLength'] = summaryDict[ t]['averageLength'] summaryDict[t]['averageWidth'] = features[t][ 'Standard Object Features']['RegionRadii'][idx, 1] summaryDict[t]['averageRelativeWidth'] = summaryDict[ t]['averageWidth'] summaryDict[t]['averageAspectRatio'] = old_div( features[t]['Standard Object Features'] ['RegionRadii'][idx, 0], features[t] ['Standard Object Features']['RegionRadii'][idx, 1]) summaryDict[t][ 'averageRelativeAspectRatio'] = summaryDict[t][ 'averageAspectRatio'] summaryDict[t]['endWiggle'] = 0.0 # Generate frame with single label idx frameSingleLabel = np.zeros(frame.shape).astype(np.uint8) frameSingleLabel[frame == idx] = 1 # Find contours using skimage marching squares contours = measure._find_contours.find_contours( frameSingleLabel, 0) # Save contours to dictionary if idx in contoursDict: contoursDict[idx][t] = contours[0] else: contoursDict[idx] = {t: contours[0]} # Compute the contours in parallel pool = RequestPool() for t in range(tMax): pool.add( Request( partial(compute_dicts_for_frame, tIndex, t, labelImageSlot, hypothesesGraph, contoursDict))) pool.wait() # Generate .summary file summaryFile = open(filename + '.summary', 'w') for t in sorted(summaryDict): summaryDict[t]['averageLength'] /= summaryDict[t]['objectsNumber'] summaryDict[t]['averageRelativeLength'] = summaryDict[t][ 'averageLength'] summaryDict[t]['averageWidth'] /= summaryDict[t]['objectsNumber'] summaryDict[t]['averageRelativeWidth'] = summaryDict[t][ 'averageWidth'] summaryDict[t]['averageAspectRatio'] /= summaryDict[t][ 'objectsNumber'] summaryDict[t]['averageRelativeAspectRatio'] = summaryDict[t][ 'averageAspectRatio'] summaryFile.write('{} {} {} {} {} {} {} {} {} {} {} {} {} {}\n'.format(t+1, \ t, \ summaryDict[t]['objectsNumber'], \ summaryDict[t]['validObjectsNumber'], \ summaryDict[t]['averageDuration'], \ summaryDict[t]['averageSpeed'], \ summaryDict[t]['averageAngularSpeed'], \ summaryDict[t]['averageLength'], \ summaryDict[t]['averageRelativeLength'], \ summaryDict[t]['averageWidth'], \ summaryDict[t]['averageRelativeWidth'], \ summaryDict[t]['averageAspectRatio'], \ summaryDict[t]['averageRelativeAspectRatio'], \ summaryDict[t]['endWiggle'])) summaryFile.close() # Generate contour string and save .blobs file blobsFile = open(filename + '.blobs', 'w') for idx in sorted(contoursDict): #blobsFile.write('%{}\n'.format(int(idx))) count = 0 for t in sorted(contoursDict[idx]): nodeId = (t, idx) if hypothesesGraph.hasNode( nodeId ) and 'lineageId' in hypothesesGraph._graph.node[nodeId]: lineageId = hypothesesGraph._graph.node[nodeId][ 'lineageId'] contour = contoursDict[idx][t] count += 1 time = t xCoord = features[t]['Standard Object Features'][ 'RegionCenter'][idx, 0] yCoord = features[t]['Standard Object Features'][ 'RegionCenter'][idx, 1] area = features[t]['Standard Object Features']['Count'][ idx, 0] xMajorAxis = features[t]['Standard Object Features'][ 'RegionAxes'][idx, 0] * features[t][ 'Standard Object Features']['RegionRadii'][idx, 0] yMajorAxis = features[t]['Standard Object Features'][ 'RegionAxes'][idx, 1] * features[t][ 'Standard Object Features']['RegionRadii'][idx, 0] std = np.mean([ features[t]['Standard Object Features']['RegionRadii'][ idx, 0], features[t]['Standard Object Features'] ['RegionRadii'][idx, 1] ]) length = features[t]['Standard Object Features'][ 'RegionRadii'][idx, 0] width = features[t]['Standard Object Features'][ 'RegionRadii'][idx, 1] xOffset = contour[0, 0] yOffset = contour[0, 1] # Generate contour string prevPoint = [] contourLength = 0 binaryString = '' pointsString = '' for i, point in enumerate(contour): if len(prevPoint) > 0: # 4-way connected points if point[0] - prevPoint[0] == -1 and point[ 1] - prevPoint[1] == 0: binaryString += '00' contourLength += 1 elif point[0] - prevPoint[0] == 1 and point[ 1] - prevPoint[1] == 0: binaryString += '01' contourLength += 1 elif point[0] - prevPoint[0] == 0 and point[ 1] - prevPoint[1] == -1: binaryString += '10' contourLength += 1 elif point[0] - prevPoint[0] == 0 and point[ 1] - prevPoint[1] == 1: binaryString += '11' contourLength += 1 # Diagonal points elif point[0] - prevPoint[0] == -1 and point[ 1] - prevPoint[1] == -1: binaryString += '00' binaryString += '10' contourLength += 2 elif point[0] - prevPoint[0] == -1 and point[ 1] - prevPoint[1] == 1: binaryString += '00' binaryString += '11' contourLength += 2 elif point[0] - prevPoint[0] == 1 and point[ 1] - prevPoint[1] == -1: binaryString += '01' binaryString += '10' contourLength += 2 elif point[0] - prevPoint[0] == 1 and point[ 1] - prevPoint[1] == 1: binaryString += '01' binaryString += '11' contourLength += 2 if len(binaryString) == 6: pointsString += chr( int(binaryString, 2) + ord('0')) binaryString = '' elif len(binaryString) > 6: diff = len(binaryString) - 6 pointsString += chr( int(binaryString[:-diff], 2) + ord('0')) binaryString = binaryString[-diff:] elif i >= contour.shape[0] - 1: if len(binaryString) == 2: binaryString += '00001' pointsString += chr( int(binaryString, 2) + ord('0')) binaryString = '' elif len(binaryString) == 4: binaryString += '00' pointsString += chr( int(binaryString, 2) + ord('0')) binaryString = '' prevPoint = point # Create contour string and append to file contourString = '{} {} {} {} {} {} {} {} {} {} %% {} {} {} '.format( count, time, xCoord, yCoord, area, xMajorAxis, yMajorAxis, std, length, width, xOffset, yOffset, contourLength + 1) contourString += pointsString if count == 1: blobsFile.write('% {}\n'.format(int(idx))) blobsFile.write(contourString + '\n') blobsFile.close() return True