def test_volume(self):
        superpixels = generate_random_voronoi((100,200), 200)
        feature_names = ['edgeregion_edge_regionradii', 'edgeregion_edge_volume']
        rag = Rag( superpixels )
        
        try:
            rag.compute_features(None, feature_names)
        except AssertionError:
            pass
        except:
            raise
        else:
            assert False, "EdgeRegion accumulator should refuse to compute 'volume' for 2D images."

        superpixels = generate_random_voronoi((25,50,100), 200)
        feature_names = ['edgeregion_edge_regionradii', 'edgeregion_edge_area', 'edgeregion_edge_volume']

        rag = Rag( superpixels )
        features_df = rag.compute_features(None, feature_names)
        
        radii = features_df[['edgeregion_edge_regionradii_0', 'edgeregion_edge_regionradii_1', 'edgeregion_edge_regionradii_2']].values
        assert (radii[:,0] >= radii[:,1]).all() and (radii[:,1] >= radii[:,2]).all()
        
        volumes = features_df[['edgeregion_edge_volume']].values        
        assert ((radii[:,0] * radii[:,1] * radii[:,2]) == volumes[:,0]).all()

        areas = features_df[['edgeregion_edge_area']].values        
        assert ((radii[:,0] * radii[:,1]) == areas[:,0]).all()
    def test_regionradii(self):
        # Create a volume of flat superpixels, where every slice
        # is the same (except for the actual sp ids)
        num_sp_per_slice = 200
        slice_superpixels = generate_random_voronoi((100,200), num_sp_per_slice)
        
        superpixels = np.zeros( shape=((10,) + slice_superpixels.shape), dtype=np.uint32 )
        for z in range(10):
            superpixels[z] = slice_superpixels + z*num_sp_per_slice
        superpixels = vigra.taggedView(superpixels, 'zyx')

        rag_flat = Rag( superpixels, flat_superpixels=True )
        
        values = np.random.random(size=(superpixels.shape)).astype(np.float32)
        values = vigra.taggedView(values, 'zyx')
        
        flat_features_df = rag_flat.compute_features(values, ['standard_flatedge_regionradii'], edge_group='z')

        # Now compute the radii using a normal 'dense' rag
        rag_dense = Rag( superpixels )
        dense_features_df = rag_dense.compute_features(values, ['edgeregion_edge_regionradii'])

        # Both methods should be reasonably close.
        combined_features_df = pd.merge(flat_features_df, dense_features_df, how='left', on=['sp1', 'sp2'])
        assert np.isclose(combined_features_df['standard_flatedge_regionradii_0'], combined_features_df['edgeregion_edge_regionradii_0'], atol=0.001).all()
        assert np.isclose(combined_features_df['standard_flatedge_regionradii_1'], combined_features_df['edgeregion_edge_regionradii_1'], atol=0.001).all()
    def test_quantiles(self):
        # Create a volume of flat superpixels, where every slice 
        # is the same (except for the actual sp ids)
        num_sp_per_slice = 200
        slice_superpixels = generate_random_voronoi((100,200), num_sp_per_slice)
        
        superpixels = np.zeros( shape=((10,) + slice_superpixels.shape), dtype=np.uint32 )
        for z in range(10):
            superpixels[z] = slice_superpixels + z*num_sp_per_slice
        superpixels = vigra.taggedView(superpixels, 'zyx')

        rag_flat = Rag( superpixels, flat_superpixels=True )
        
        values = np.random.random(size=(superpixels.shape)).astype(np.float32)
        values = vigra.taggedView(values, 'zyx')
        
        flat_features_df = rag_flat.compute_features(values, ['standard_flatedge_quantiles'], edge_group='z')
        flat_features_df2 = rag_flat.compute_features(values, ['standard_edge_quantiles'], edge_group='yx')
        
        # Rename columns
        flat_features_df2.columns = flat_features_df.columns.values
        flat_features_df = pd.concat( (flat_features_df, flat_features_df2), axis=0 )

        # Now compute the quantiles using a normal 'dense' rag
        rag_dense = Rag( superpixels )
        dense_features_df = rag_dense.compute_features(values, ['standard_edge_quantiles'])

        all_features_df = pd.merge(dense_features_df, flat_features_df, how='left', on=['sp1', 'sp2'])
        assert (all_features_df['standard_edge_quantiles_0'] == all_features_df['standard_flatedge_quantiles_0']).all()
        assert (all_features_df['standard_edge_quantiles_100'] == all_features_df['standard_flatedge_quantiles_100']).all()

        # Due to the way histogram ranges are computed, we can't expect quantiles_10 to match closely
        # ... but quantiles_50 seems to be good.
        assert np.isclose(all_features_df['standard_edge_quantiles_50'], all_features_df['standard_edge_quantiles_50']).all()
    def test1(self):
        superpixels = generate_random_voronoi((100,200), 200)
        superpixels.axistags = vigra.defaultAxistags('yx')

        feature_names = ['edgeregion_edge_regionradii']

        rag = Rag( superpixels )
        acc = EdgeRegionEdgeAccumulator(rag, feature_names)
        features_df = rag.compute_features(None, feature_names, accumulator_set=[acc])
        radii = features_df[features_df.columns.values[2:]].values
        assert (radii[:,0] >= radii[:,1]).all()
 
        # Transpose superpixels and check again
        # Should match (radii are sorted by magnitude).
        superpixels.axistags = vigra.defaultAxistags('xy')
        rag = Rag( superpixels )
        acc = EdgeRegionEdgeAccumulator(rag, feature_names)

        transposed_features_df = rag.compute_features(None, feature_names, accumulator_set=[acc])
        transposed_radii = transposed_features_df[transposed_features_df.columns.values[2:]].values

        assert (transposed_features_df[['sp1', 'sp1']].values == features_df[['sp1', 'sp1']].values).all()
        
        DEBUG = False
        if DEBUG:
            count_features = rag.compute_features(None, ['standard_edge_count', 'standard_sp_count'])
    
            import pandas as pd
            combined_features_df = pd.merge(features_df, transposed_features_df, how='left', on=['sp1', 'sp2'], suffixes=('_orig', '_transposed'))
            combined_features_df = pd.merge(combined_features_df, count_features, how='left', on=['sp1', 'sp2'])
            
            problem_rows = np.logical_or(np.isclose(radii[:, 0], transposed_radii[:, 0]) != 1,
                                         np.isclose(radii[:, 1], transposed_radii[:, 1]) != 1)
            problem_df = combined_features_df.loc[problem_rows][sorted(list(combined_features_df.columns))]
            print(problem_df.transpose())
            
            debug_sp = np.zeros_like(superpixels, dtype=np.uint8)
            for sp1 in problem_df['sp1'].values:
                debug_sp[superpixels == sp1] = 128
            for sp2 in problem_df['sp2'].values:
                debug_sp[superpixels == sp2] = 255
    
            vigra.impex.writeImage(debug_sp, '/tmp/debug_sp.png', dtype='NATIVE')
                
        # The first axes should all be close.
        # The second axes may differ somewhat in the case of purely linear edges,
        # so we allow a higher tolerance.
        assert np.isclose(radii[:,0], transposed_radii[:,0]).all()
        assert np.isclose(radii[:,1], transposed_radii[:,1], atol=0.001).all()
    def test2(self):
        superpixels = np.zeros((10, 10), dtype=np.uint32)
        superpixels[1:2] = 1
        superpixels = vigra.taggedView(superpixels, 'yx')

        rag = Rag( superpixels )

        feature_names = ['edgeregion_edge_regionradii', 'edgeregion_edge_regionaxes']
        acc = EdgeRegionEdgeAccumulator(rag, feature_names)

        features_df = rag.compute_features(None, feature_names, accumulator_set=[acc])
         
        # Just 1 edge in this rag (0 -> 1)
        assert len(features_df) == 1
        assert (features_df[['sp1', 'sp2']].values == [0,1]).all()

        # Manually compute the radius 
        x_coords = np.arange(0, 10)
        x_coord_mean = x_coords.mean()
        centralized_x_coords = x_coords - x_coord_mean
        x_coord_variance = centralized_x_coords.dot(centralized_x_coords)/len(x_coords)
        x_radius = np.sqrt(x_coord_variance).astype(np.float32)
         
        assert features_df['edgeregion_edge_regionradii_0'].values[0] == x_radius        
 
        # Eigenvectors are just parallel to the axes
        assert features_df['edgeregion_edge_regionaxes_0x'].values[0] == 1.0
        assert features_df['edgeregion_edge_regionaxes_0y'].values[0] == 0.0
        
        assert features_df['edgeregion_edge_regionaxes_1x'].values[0] == 0.0
        assert features_df['edgeregion_edge_regionaxes_1y'].values[0] == 1.0
    def test_edge_features_no_histogram(self):
        """
        Make sure vigra edge filters still work even if no histogram features are selected.
        """
        superpixels = generate_random_voronoi((100,200), 200)
        rag = Rag( superpixels )

        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)

        feature_names = ['standard_edge_mean', 'standard_edge_minimum', 'standard_edge_maximum']

        features_df = rag.compute_features(values, feature_names)
        assert len(features_df) == len(rag.edge_ids)
        assert list(features_df.columns.values) == ['sp1', 'sp2'] + list(feature_names), \
            "Wrong output feature names: {}".format( features_df.columns.values )

        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        for row_tuple in features_df.itertuples():
            row = OrderedDict( zip(['index', 'sp1', 'sp2'] + list(feature_names),
                                   row_tuple) )
            sp1 = row['sp1']
            sp2 = row['sp2']
            # Values were identical to the superpixels, so this is boring...
            assert row['standard_edge_mean'] == (sp1+sp2)/2.
            assert row['standard_edge_minimum'] == (sp1+sp2)/2.
            assert row['standard_edge_maximum'] == (sp1+sp2)/2.
    def test_sp_features_no_histogram(self):
        superpixels = generate_random_voronoi((100,200), 200)
        rag = Rag( superpixels )

        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)

        # Manually compute the sp counts
        sp_counts = np.bincount(superpixels.flat[:])

        # COUNT
        features_df = rag.compute_features(values, ['standard_sp_count'])
        assert len(features_df) == len(rag.edge_ids)
        assert (features_df.columns.values == ['sp1', 'sp2', 'standard_sp_count_sum', 'standard_sp_count_difference']).all()
        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()
        dtypes = { colname: series.dtype for colname, series in features_df.iterkv() }
        assert all(dtype != np.float64 for dtype in dtypes.values()), \
            "An accumulator returned float64 features. That's a waste of ram.\n"\
            "dtypes were: {}".format(dtypes)

        # sp count features are normalized, consistent with the multicut paper.
        for _index, sp1, sp2, sp_count_sum, sp_count_difference in features_df.itertuples():
            assert sp_count_sum == np.power(sp_counts[sp1] + sp_counts[sp2], 1./superpixels.ndim).astype(np.float32)
            assert sp_count_difference == np.power(np.abs(sp_counts[sp1] - sp_counts[sp2]), 1./superpixels.ndim).astype(np.float32)

        # SUM
        features_df = rag.compute_features(values, ['standard_sp_sum'])
        assert len(features_df) == len(rag.edge_ids)
        assert (features_df.columns.values == ['sp1', 'sp2', 'standard_sp_sum_sum', 'standard_sp_sum_difference']).all()
        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        # sp sum features ought to be normalized, too...
        for _index, sp1, sp2, sp_sum_sum, sp_sum_difference in features_df.itertuples():
            assert sp_sum_sum == np.power(sp1*sp_counts[sp1] + sp2*sp_counts[sp2], 1./superpixels.ndim).astype(np.float32)
            assert sp_sum_difference == np.power(np.abs(sp1*sp_counts[sp1] - sp2*sp_counts[sp2]), 1./superpixels.ndim).astype(np.float32)

        # MEAN
        features_df = rag.compute_features(values, ['standard_sp_mean'])
        assert len(features_df) == len(rag.edge_ids)
        assert (features_df.columns.values == ['sp1', 'sp2', 'standard_sp_mean_sum', 'standard_sp_mean_difference']).all()
        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        # No normalization for other features...
        # Should there be?
        for _index, sp1, sp2, sp_mean_sum, sp_mean_difference in features_df.itertuples():
            assert sp_mean_sum == sp1 + sp2
            assert sp_mean_difference == np.abs(np.float32(sp1) - sp2)
    def test_area(self):
        superpixels = generate_random_voronoi((100,200), 200)
        feature_names = ['edgeregion_edge_regionradii', 'edgeregion_edge_area']

        rag = Rag( superpixels )
        features_df = rag.compute_features(None, feature_names)
        
        radii = features_df[['edgeregion_edge_regionradii_0', 'edgeregion_edge_regionradii_1']].values
        assert (radii[:,0] >= radii[:,1]).all()

        areas = features_df[['edgeregion_edge_area']].values
        assert ((radii[:,0] * radii[:,1]) == areas[:,0]).all()
    def test_correlation(self):
        # Create a volume of flat superpixels, where every slice 
        # is the same (except for the actual sp ids)
        num_sp_per_slice = 200
        slice_superpixels = generate_random_voronoi((100,200), num_sp_per_slice)
        
        superpixels = np.zeros( shape=((10,) + slice_superpixels.shape), dtype=np.uint32 )
        for z in range(10):
            superpixels[z] = slice_superpixels + z*num_sp_per_slice
        superpixels = vigra.taggedView(superpixels, 'zyx')

        rag = Rag( superpixels, flat_superpixels=True )

        # For simplicity, just make values identical to the first slice
        values = np.zeros_like(superpixels, dtype=np.float32)
        values[:] = slice_superpixels[None]

        features_df = rag.compute_features(values, ['similarity_flatedge_correlation'], edge_group='z')
        assert (features_df['similarity_flatedge_correlation'].values == 1.0).all()

        # Now add a little noise from one slice to the next.
        for z in range(10):
            if z == 0:
                continue
            if z == 1:
                values[z] += 0.001*np.random.random(size=(values[z].shape))
            else:
                values[z] += 1.0001*np.abs(values[z-1] - values[z-2])

        features_df = rag.compute_features(values, ['similarity_flatedge_correlation'], edge_group='z')        
        assert (features_df['similarity_flatedge_correlation'].values >= 0.7).all()
        assert (features_df['similarity_flatedge_correlation'].values <= 1.0).all()
        
        # Now use just noise
        values = np.random.random(size=values.shape).astype(np.float32)
        values = vigra.taggedView(values, 'zyx')
        features_df = rag.compute_features(values, ['similarity_flatedge_correlation'], edge_group='z')
        assert (features_df['similarity_flatedge_correlation'].values <= 1.0).all()
        assert (features_df['similarity_flatedge_correlation'].values >= -1.0).all()
Beispiel #10
0
    def test_serialization_without_labels(self):
        """
        Users can opt to serialize the Rag without serializing the labels,
        but then they can't use superpixel features on the deserialized Rag.
        """
        import h5py
 
        superpixels = generate_random_voronoi((100,200), 200)
        original_rag = Rag( superpixels )
 
        tmp_dir = tempfile.mkdtemp()
        filepath = os.path.join(tmp_dir, 'test_rag.h5')
        rag_groupname = 'saved_rag'
 
        # Serialize with labels       
        with h5py.File(filepath, 'w') as f:
            rag_group = f.create_group(rag_groupname)
            original_rag.serialize_hdf5(rag_group, store_labels=False) # Don't store
 
        # Deserialize
        with h5py.File(filepath, 'r') as f:
            rag_group = f[rag_groupname]
            deserialized_rag = Rag.deserialize_hdf5(rag_group)
 
        assert deserialized_rag.label_img.dtype == original_rag.label_img.dtype
        assert deserialized_rag.label_img.shape == original_rag.label_img.shape
        assert deserialized_rag.label_img.axistags == original_rag.label_img.axistags
        #assert (deserialized_rag.label_img == original_rag.label_img).all() # not stored
 
        assert (deserialized_rag.sp_ids == original_rag.sp_ids).all()
        assert deserialized_rag.max_sp == original_rag.max_sp
        assert deserialized_rag.num_sp == original_rag.num_sp
        assert deserialized_rag.num_edges == original_rag.num_edges
        assert (deserialized_rag.edge_ids == original_rag.edge_ids).all()
 
        # Check some features
        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)
        feature_names = ['standard_edge_mean', 'standard_edge_count']
        features_df_original = original_rag.compute_features(values, feature_names)
        features_df_deserialized = deserialized_rag.compute_features(values, feature_names)
        assert (features_df_original.values == features_df_deserialized.values).all()
 
        try:
            deserialized_rag.compute_features(values, ['standard_sp_count'])
        except NotImplementedError:
            pass
        except:
            raise
        else:
            assert False, "Shouldn't be able to use superpixels if labels weren't serialized/deserialized!"
    def test_sp_features_with_histogram(self):
        superpixels = generate_random_voronoi((100,200), 200)
        rag = Rag( superpixels )

        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)

        # Manually compute the sp counts
        sp_counts = np.bincount(superpixels.flat[:])

        # COUNT
        features_df = rag.compute_features(values, ['standard_sp_count', 'standard_sp_quantiles_25', 'standard_sp_quantiles_75'])
        assert len(features_df) == len(rag.edge_ids)
        assert (features_df.columns.values == ['sp1', 'sp2',
                                               'standard_sp_count_sum',
                                               'standard_sp_count_difference',
                                               'standard_sp_quantiles_25_sum',
                                               'standard_sp_quantiles_25_difference',
                                               'standard_sp_quantiles_75_sum',
                                               'standard_sp_quantiles_75_difference']).all()

        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        # Check dtypes (pandas makes it too easy to get this wrong).
        dtypes = { colname: series.dtype for colname, series in features_df.iterkv() }
        assert all(dtype != np.float64 for dtype in dtypes.values()), \
            "An accumulator returned float64 features. That's a waste of ram.\n"\
            "dtypes were: {}".format(dtypes)

        # sp count features are normalized, consistent with the multicut paper.
        for _index, sp1, sp2, \
            sp_count_sum, sp_count_difference, \
            sp_quantiles_25_sum, sp_quantiles_25_difference, \
            sp_quantiles_75_sum, sp_quantiles_75_difference in features_df.itertuples():
            
            assert type(sp_quantiles_25_sum) == np.float32
            assert type(sp_quantiles_75_sum) == np.float32
            assert type(sp_quantiles_25_difference) == np.float32
            assert type(sp_quantiles_75_difference) == np.float32
            
            assert sp_count_sum == np.power(sp_counts[sp1] + sp_counts[sp2], 1./superpixels.ndim).astype(np.float32)
            assert sp_count_difference == np.power(np.abs(sp_counts[sp1] - sp_counts[sp2]), 1./superpixels.ndim).astype(np.float32)
            assert sp_quantiles_25_sum == float(sp1 + sp2), \
                "{} != {}".format( sp_quantiles_25_sum, float(sp1 + sp2) )
            assert sp_quantiles_75_sum == float(sp1 + sp2)
            assert sp_quantiles_25_difference == abs(float(sp1) - sp2)
            assert sp_quantiles_75_difference == abs(float(sp1) - sp2)
Beispiel #12
0
    def test_serialization_with_external_labels(self):
        """
        Users can opt to serialize the Rag without serializing the labels,
        but then they can't use superpixel features on the deserialized Rag.
         
        When deserializing, they can provide the labels from an external source,
        as tested here.
        """
        import h5py
 
        superpixels = generate_random_voronoi((100,200), 200)
        original_rag = Rag( superpixels )
 
        tmp_dir = tempfile.mkdtemp()
        filepath = os.path.join(tmp_dir, 'test_rag.h5')
        rag_groupname = 'saved_rag'
 
        # Serialize with labels       
        with h5py.File(filepath, 'w') as f:
            rag_group = f.create_group(rag_groupname)
            original_rag.serialize_hdf5(rag_group, store_labels=False)
 
        # Deserialize
        with h5py.File(filepath, 'r') as f:
            rag_group = f[rag_groupname]
            deserialized_rag = Rag.deserialize_hdf5(rag_group, label_img=superpixels) # Provide labels explicitly
 
        assert deserialized_rag.label_img.dtype == original_rag.label_img.dtype
        assert deserialized_rag.label_img.shape == original_rag.label_img.shape
        assert deserialized_rag.label_img.axistags == original_rag.label_img.axistags
        assert (deserialized_rag.label_img == original_rag.label_img).all()
 
        assert (deserialized_rag.sp_ids == original_rag.sp_ids).all()
        assert deserialized_rag.max_sp == original_rag.max_sp
        assert deserialized_rag.num_sp == original_rag.num_sp
        assert deserialized_rag.num_edges == original_rag.num_edges
        assert (deserialized_rag.edge_ids == original_rag.edge_ids).all()
 
        # Check some features
        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)
        feature_names = ['standard_edge_mean', 'standard_sp_count']
        features_df_original = original_rag.compute_features(values, feature_names)
        features_df_deserialized = deserialized_rag.compute_features(values, feature_names)
        
        assert (features_df_original.values == features_df_deserialized.values).all()
    def test_shorthand_names(self):
        superpixels = generate_random_voronoi((100,200), 200)
        rag = Rag( superpixels )

        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)

        features_df = rag.compute_features(values, ['standard_edge_quantiles', 'standard_sp_quantiles'])
        
        quantile_names = ['quantiles_0', 'quantiles_10', 'quantiles_25', 'quantiles_50', 'quantiles_75', 'quantiles_90', 'quantiles_100']
        edge_feature_names = map( lambda name: 'standard_edge_' + name, quantile_names )
        sp_features_names = map( lambda name: 'standard_sp_' + name, quantile_names )
        
        sp_output_columns = []
        for name in sp_features_names:
            sp_output_columns.append( name + '_sum' )
            sp_output_columns.append( name + '_difference' )

        assert list(features_df.columns.values) == ['sp1', 'sp2'] + edge_feature_names + sp_output_columns
Beispiel #14
0
    def test_serialization_with_labels(self):
        """
        Serialize the rag and labels to hdf5,
        then deserialize it and make sure nothing was lost.
        """
        import h5py
 
        superpixels = generate_random_voronoi((100,200), 200)
        original_rag = Rag( superpixels )
 
        tmp_dir = tempfile.mkdtemp()
        filepath = os.path.join(tmp_dir, 'test_rag.h5')
        rag_groupname = 'saved_rag'
 
        # Serialize with labels       
        with h5py.File(filepath, 'w') as f:
            rag_group = f.create_group(rag_groupname)
            original_rag.serialize_hdf5(rag_group, store_labels=True)
 
        # Deserialize
        with h5py.File(filepath, 'r') as f:
            rag_group = f[rag_groupname]
            deserialized_rag = Rag.deserialize_hdf5(rag_group)
 
        assert deserialized_rag.label_img.dtype == original_rag.label_img.dtype
        assert deserialized_rag.label_img.shape == original_rag.label_img.shape
        assert deserialized_rag.label_img.axistags == original_rag.label_img.axistags
        assert (deserialized_rag.label_img == original_rag.label_img).all()        
 
        assert (deserialized_rag.sp_ids == original_rag.sp_ids).all()
        assert deserialized_rag.max_sp == original_rag.max_sp
        assert deserialized_rag.num_sp == original_rag.num_sp
        assert deserialized_rag.num_edges == original_rag.num_edges
        assert (deserialized_rag.edge_ids == original_rag.edge_ids).all()
 
        # Check some features
        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)
        feature_names = ['standard_edge_mean', 'standard_sp_count']
        features_df_original = original_rag.compute_features(values, feature_names)
        features_df_deserialized = deserialized_rag.compute_features(values, feature_names)
        assert (features_df_original.values == features_df_deserialized.values).all()
    def test_count(self):
        # Create a volume of flat superpixels, where every slice 
        # is the same (except for the actual sp ids)
        num_sp_per_slice = 200
        slice_superpixels = generate_random_voronoi((100,200), num_sp_per_slice)
         
        superpixels = np.zeros( shape=((10,) + slice_superpixels.shape), dtype=np.uint32 )
        for z in range(10):
            superpixels[z] = slice_superpixels + z*num_sp_per_slice
        superpixels = vigra.taggedView(superpixels, 'zyx')
 
        rag = Rag( superpixels, flat_superpixels=True )
 
        # Manually compute the sp counts for the first N-1 slices,
        # which happens to be the same as the edge counts (since superpixels are identical on all slices)
        sp_counts = np.bincount(superpixels[:-1].flat[:])
        sp_counts = sp_counts[1:] # Drop zero sp (it doesn't exist)
         
        features_df = rag.compute_features(None, ['standard_flatedge_count'], edge_group='z')
        assert sorted(features_df['standard_flatedge_count'].values) == sorted(sp_counts)
    def test_edge_features_with_histogram_blank_data(self):
        """
        There was a bug related to histogram min/max if all edge pixels had the exact same value.
        In that case, min and max were identical and vigra complained.
        For example, if you give vigra histogramRange=(0.0, 0.0), that's a problem.
        This test verifies that no crashes occur in such cases.
        """
        superpixels = generate_random_voronoi((100,200), 200)
        rag = Rag( superpixels )

        values = np.zeros_like(superpixels, dtype=np.float32)

        feature_names = ['standard_edge_mean', 'standard_edge_minimum', 'standard_edge_maximum', 'standard_edge_variance',
                         'standard_edge_quantiles_25', 'standard_edge_quantiles_50', 'standard_edge_quantiles_75',
                         'standard_edge_count', 'standard_edge_sum']

        features_df = rag.compute_features(values, feature_names)
        assert len(features_df) == len(rag.edge_ids)
        assert list(features_df.columns.values) == ['sp1', 'sp2'] + list(feature_names), \
            "Wrong output feature names: {}".format( features_df.columns.values )

        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        # Check dtypes (pandas makes it too easy to get this wrong).
        dtypes = { colname: series.dtype for colname, series in features_df.iteritems() }
        assert all(dtype != np.float64 for dtype in list(dtypes.values())), \
            "An accumulator returned float64 features. That's a waste of ram.\n"\
            "dtypes were: {}".format(dtypes)

        for row_tuple in features_df.itertuples():
            row = OrderedDict( list(zip(['index', 'sp1', 'sp2'] + list(feature_names),
                                   row_tuple)) )

            assert row['standard_edge_mean'] == 0.0
            assert row['standard_edge_minimum'] == 0.0
            assert row['standard_edge_maximum'] == 0.0
            assert row['standard_edge_variance'] == 0.0
            assert row['standard_edge_quantiles_25'] == 0.0
            assert row['standard_edge_quantiles_75'] == 0.0
            assert row['standard_edge_count'] > 0
            assert row['standard_edge_sum'] == 0.0
    def test_edge_features_with_histogram(self):
        superpixels = generate_random_voronoi((100,200), 200)
        rag = Rag( superpixels )

        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)

        feature_names = ['standard_edge_mean', 'standard_edge_minimum', 'standard_edge_maximum', 'standard_edge_variance',
                         'standard_edge_quantiles_25', 'standard_edge_quantiles_50', 'standard_edge_quantiles_75',
                         'standard_edge_count', 'standard_edge_sum']

        features_df = rag.compute_features(values, feature_names)
        assert len(features_df) == len(rag.edge_ids)
        assert list(features_df.columns.values) == ['sp1', 'sp2'] + list(feature_names), \
            "Wrong output feature names: {}".format( features_df.columns.values )

        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        # Check dtypes (pandas makes it too easy to get this wrong).
        dtypes = { colname: series.dtype for colname, series in features_df.iterkv() }
        assert all(dtype != np.float64 for dtype in dtypes.values()), \
            "An accumulator returned float64 features. That's a waste of ram.\n"\
            "dtypes were: {}".format(dtypes)

        for row_tuple in features_df.itertuples():
            row = OrderedDict( zip(['index', 'sp1', 'sp2'] + list(feature_names),
                                   row_tuple) )
            sp1 = row['sp1']
            sp2 = row['sp2']
            # Values were identical to the superpixels, so this is boring...
            assert row['standard_edge_mean'] == (sp1+sp2)/2.
            assert row['standard_edge_minimum'] == (sp1+sp2)/2.
            assert row['standard_edge_maximum'] == (sp1+sp2)/2.
            assert row['standard_edge_variance'] == 0.0
            assert row['standard_edge_quantiles_25'] == (sp1+sp2)/2.
            assert row['standard_edge_quantiles_75'] == (sp1+sp2)/2.
            assert row['standard_edge_count'] > 0
            assert row['standard_edge_sum'] == row['standard_edge_count'] * (sp1+sp2)/2.
    def test_sp_region_features(self):
        # Create a superpixel for each y-column
        superpixels = np.zeros( (100, 200), dtype=np.uint32 )
        superpixels[:] = np.arange(200)[None, :]
        superpixels = vigra.taggedView(superpixels, 'yx')
        
        rag = Rag( superpixels )

        # For simplicity, just make values identical to superpixels
        values = superpixels.astype(np.float32)

        feature_names = ['standard_sp_regionradii_0',
                         'standard_sp_regionradii_1',
                         'standard_sp_regionaxes_0x',
                         'standard_sp_regionaxes_0y',
                         'standard_sp_regionaxes_1x',
                         'standard_sp_regionaxes_1y',
                         ]

        output_columns = ['sp1', 'sp2',
                          'standard_sp_regionradii_0_sum',
                          'standard_sp_regionradii_0_difference',
                          'standard_sp_regionradii_1_sum',
                          'standard_sp_regionradii_1_difference',
                        
                          'standard_sp_regionaxes_0x_sum',
                          'standard_sp_regionaxes_0x_difference',
                          'standard_sp_regionaxes_0y_sum',
                          'standard_sp_regionaxes_0y_difference',

                          'standard_sp_regionaxes_1x_sum',
                          'standard_sp_regionaxes_1x_difference',
                          'standard_sp_regionaxes_1y_sum',
                          'standard_sp_regionaxes_1y_difference' ]
        
        features_df = rag.compute_features(values, feature_names)
        assert len(features_df) == len(rag.edge_ids)
        assert list(features_df.columns.values) == output_columns, \
            "Wrong output feature names: {}".format( features_df.columns.values )

        # Using shorthand names should result in the same columns
        features_df = rag.compute_features(values, ['standard_sp_regionradii', 'standard_sp_regionaxes'])
        assert len(features_df) == len(rag.edge_ids)
        assert list(features_df.columns.values) == output_columns, \
            "Wrong output feature names: {}".format( features_df.columns.values )

        assert (features_df[['sp1', 'sp2']].values == rag.edge_ids).all()

        # Check dtypes (pandas makes it too easy to get this wrong).
        dtypes = { colname: series.dtype for colname, series in features_df.iterkv() }
        assert all(dtype != np.float64 for dtype in dtypes.values()), \
            "An accumulator returned float64 features. That's a waste of ram.\n"\
            "dtypes were: {}".format(dtypes)

        # Manually compute the 'radius' of each superpixel
        # This is easy because each superpixel is just 1 column wide.
        col_coord_mean = np.arange(100).mean()
        centralized_col_coords = np.arange(100) - col_coord_mean
        col_coord_variance = centralized_col_coords.dot(centralized_col_coords)/100.
        col_radius = np.sqrt(col_coord_variance).astype(np.float32)

        for row_tuple in features_df.itertuples():
            row = OrderedDict( zip(['index'] + list(features_df.columns.values),
                                   row_tuple) )
            # The superpixels were just vertical columns
            assert row['standard_sp_regionradii_0_sum'] == 2*col_radius
            assert row['standard_sp_regionradii_0_difference'] == 0.0
            assert row['standard_sp_regionradii_1_sum'] == 0.0
            assert row['standard_sp_regionradii_1_difference'] == 0.0

            # Axes are just parallel to the coordinate axes, so this is boring.
            # The x_sum is 0.0 because all superpixels are only 1 pixel wide in that direction.
            assert row['standard_sp_regionaxes_0x_sum'] == 0.0
            assert row['standard_sp_regionaxes_0x_difference'] == 0.0
            assert row['standard_sp_regionaxes_0y_sum'] == 2.0
            assert row['standard_sp_regionaxes_0y_difference'] == 0.0

            # The second axis is the smaller one, so here the y_sum axes are non-zero
            assert row['standard_sp_regionaxes_1x_sum'] == 2.0
            assert row['standard_sp_regionaxes_1x_difference'] == 0.0
            assert row['standard_sp_regionaxes_1y_sum'] == 0.0
            assert row['standard_sp_regionaxes_1y_difference'] == 0.0