Exemplo n.º 1
0
    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()
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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]
Exemplo n.º 4
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]
Exemplo n.º 5
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 )
Exemplo n.º 7
0
    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()
Exemplo n.º 8
0
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()
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
    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() ))
Exemplo n.º 11
0
    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()
Exemplo n.º 12
0
 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()
Exemplo n.º 13
0
    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)]
Exemplo n.º 14
0
        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
Exemplo n.º 15
0
    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()
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    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)
Exemplo n.º 19
0
    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)
Exemplo n.º 20
0
    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
Exemplo n.º 21
0
    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
Exemplo n.º 23
0
    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
Exemplo n.º 24
0
    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)
Exemplo n.º 25
0
    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()
Exemplo n.º 26
0
    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
Exemplo n.º 27
0
    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()
Exemplo n.º 29
0
    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
Exemplo n.º 30
0
    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 _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
Exemplo n.º 32
0
    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()
Exemplo n.º 33
0
    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
Exemplo n.º 37
0
    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]
Exemplo n.º 39
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))
Exemplo n.º 40
0
    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
Exemplo n.º 41
0
    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
Exemplo n.º 42
0
 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()
Exemplo n.º 43
0
    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)
Exemplo n.º 44
0
    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
Exemplo n.º 48
0
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)
Exemplo n.º 49
0
    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()
Exemplo n.º 50
0
    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))
Exemplo n.º 51
0
    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
Exemplo n.º 52
0
    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
Exemplo n.º 53
0
    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
Exemplo n.º 54
0
    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()
Exemplo n.º 55
0
    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
Exemplo n.º 56
0
    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)))
Exemplo n.º 57
0
    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()))
Exemplo n.º 58
0
    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