Repository: mne-tools/mne-python

Issue:
Spherical searchlights as labels for all vertices in source space
#### Describe the new feature or enhancement
I would like to use spherical searchlights as labels for all vertices in source space to compute PSFs/CTFs for sources with spatial extent (rather than point sources). I tried to use mne.grow_labels(), but it produces the error "NotImplementedError: Can't produce more than 1024 unique colors". 


#### Describe your proposed implementation
Could one implement an option like '''colors=False''' to prevent the error message?


#### Describe possible alternatives
Possible workaround: One can use list comprehension to call grow_labels per vertex/seed, but I don't see a reason to limit it to 1024 colors.

#### Additional comments




Discussion:
Yes having `colors` as a limitation seems silly. I would add a `colors=None` argument that by default does what is done now, and allow `ndarray, shape ([n_seeds, ][3])` that allows setting the color of the labels manually. The `n_seeds` and `3` are both optional because it should be any array that broadcasts to shape `(n_seeds, 3)` so for example you can use `colors=0.` to get all black labels.

Resolving patch:
diff --git a/mne/label.py b/mne/label.py
--- a/mne/label.py
+++ b/mne/label.py
@@ -1502,7 +1502,7 @@ def _grow_labels(seeds, extents, hemis, names, dist, vert, subject):
 
 @fill_doc
 def grow_labels(subject, seeds, extents, hemis, subjects_dir=None, n_jobs=1,
-                overlap=True, names=None, surface='white'):
+                overlap=True, names=None, surface='white', colors=None):
     """Generate circular labels in source space with region growing.
 
     This function generates a number of labels in source space by growing
@@ -1534,6 +1534,11 @@ def grow_labels(subject, seeds, extents, hemis, subjects_dir=None, n_jobs=1,
         seeds).
     surface : str
         The surface used to grow the labels, defaults to the white surface.
+    colors : array, shape (n, 4) or (, 4) | None
+        How to assign colors to each label. If None then unique colors will be
+        chosen automatically (default), otherwise colors will be broadcast
+        from the array. The first three values will be interpreted as RGB
+        colors and the fourth column as the alpha value (commonly 1).
 
     Returns
     -------
@@ -1569,6 +1574,21 @@ def grow_labels(subject, seeds, extents, hemis, subjects_dir=None, n_jobs=1,
         raise ValueError('The hemis parameter has to be of length 1 or '
                          'len(seeds)')
 
+    if colors is not None:
+        if len(colors.shape) == 1:  # if one color for all seeds
+            n_colors = 1
+            n = colors.shape[0]
+        else:
+            n_colors, n = colors.shape
+
+        if n_colors != n_seeds and n_colors != 1:
+            msg = ('Number of colors (%d) and seeds (%d) are not compatible.' %
+                   (n_colors, n_seeds))
+            raise ValueError(msg)
+        if n != 4:
+            msg = 'Colors must have 4 values (RGB and alpha), not %d.' % n
+            raise ValueError(msg)
+
     # make the arrays the same length as seeds
     if len(extents) == 1:
         extents = np.tile(extents, n_seeds)
@@ -1615,9 +1635,15 @@ def grow_labels(subject, seeds, extents, hemis, subjects_dir=None, n_jobs=1,
         labels = _grow_nonoverlapping_labels(subject, seeds, extents, hemis,
                                              vert, dist, names)
 
-    # add a unique color to each label
-    colors = _n_colors(len(labels))
-    for label, color in zip(labels, colors):
+    if colors is None:
+        # add a unique color to each label
+        label_colors = _n_colors(len(labels))
+    else:
+        # use specified colors
+        label_colors = np.empty((len(labels), 4))
+        label_colors[:] = colors
+
+    for label, color in zip(labels, label_colors):
         label.color = color
 
     return labels


Test patch:
diff --git a/mne/tests/test_label.py b/mne/tests/test_label.py
--- a/mne/tests/test_label.py
+++ b/mne/tests/test_label.py
@@ -814,6 +814,24 @@ def test_grow_labels():
     assert_equal(l11.name, 'Label_0-lh')
     assert_equal(l12.name, 'Label_1-lh')
 
+    # test color assignment
+    l11_c, l12_c = grow_labels('fsaverage', seeds, 20, [0, 0], subjects_dir,
+                               overlap=False, colors=None)
+    assert_equal(l11_c.color, _n_colors(2)[0])
+    assert_equal(l12_c.color, _n_colors(2)[1])
+
+    lab_colors = np.array([[0, 0, 1, 1], [1, 0, 0, 1]])
+    l11_c, l12_c = grow_labels('fsaverage', seeds, 20, [0, 0], subjects_dir,
+                               overlap=False, colors=lab_colors)
+    assert_array_equal(l11_c.color, lab_colors[0, :])
+    assert_array_equal(l12_c.color, lab_colors[1, :])
+
+    lab_colors = np.array([.1, .2, .3, .9])
+    l11_c, l12_c = grow_labels('fsaverage', seeds, 20, [0, 0], subjects_dir,
+                               overlap=False, colors=lab_colors)
+    assert_array_equal(l11_c.color, lab_colors)
+    assert_array_equal(l12_c.color, lab_colors)
+
     # make sure set 1 does not overlap
     overlap = np.intersect1d(l11.vertices, l12.vertices, True)
     assert_array_equal(overlap, [])