Skip to content

Commit eca33ff

Browse files
authored
Merge pull request #6 from Julie-Fabre/JOSS_submission
Joss submission
2 parents fbb68b7 + eba765a commit eca33ff

6 files changed

Lines changed: 61 additions & 7 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Load and plot Allen Connectivity Data ([Oh et al., Nature, 2014](https://doi.org
1212

1313
Available in both **MATLAB** and **Python**. **[Documentation](https://brain-street-view.readthedocs.io)**
1414

15+
<p align="center">
16+
<img src="./images/docs/upstream_projectome_scroll.gif" width="80%" alt="Scrolling through coronal slices showing projections into the caudate putamen from several visual areas, each in its own colour">
17+
</p>
18+
19+
*Scroll through the brain to see projections into a target region from several source areas at once, each in its own colour ([interactive slice viewer](https://brain-street-view.readthedocs.io/en/latest/interactive_viewer.html)).*
20+
1521
### 🏁 Quick start
1622

1723
**Python**:

‎bsv/plot_upstream_projectome.py‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
def plot_upstream_projectome(experiment_ids, source_regions, target_region, save_location,
1313
allen_atlas_path, atlas_resolution=10, atlas_type='allen',
1414
slice_thickness=2, density_percentile=99,
15-
static_ap=None, save_path=None):
15+
static_ap=None, save_path=None,
16+
save_gif=None, gif_ap_step=2, gif_fps=8, gif_width=720):
1617
"""Interactive coronal slice viewer of upstream projections into a target region.
1718
1819
Displays a Jupyter widget with a coronal AP slider showing:
@@ -52,6 +53,16 @@ def plot_upstream_projectome(experiment_ids, source_regions, target_region, save
5253
documentation figures headlessly.
5354
save_path : str or None, optional
5455
When *static_ap* is set, save the rendered figure to this path.
56+
save_gif : str or None, optional
57+
If given, render an animated GIF scrolling through the AP slices that
58+
contain the target region and write it to this path (requires Pillow).
59+
Skips the interactive widget.
60+
gif_ap_step : int, optional
61+
Step between AP slices when building the GIF (default 2).
62+
gif_fps : int, optional
63+
Frames per second of the GIF (default 8).
64+
gif_width : int, optional
65+
Width in pixels to downscale GIF frames to (default 720).
5566
"""
5667
AP, DV, ML = PROJECTION_GRID_SIZE
5768
# Number of full-res atlas voxels per 100 µm projection grid voxel
@@ -256,6 +267,36 @@ def _draw(ap_idx):
256267
plt.tight_layout()
257268
plt.show()
258269

270+
# ------------------------------------- animated GIF (scroll through AP)
271+
if save_gif is not None:
272+
try:
273+
from PIL import Image
274+
except ImportError as exc:
275+
raise ImportError("Saving a GIF requires Pillow (pip install pillow).") from exc
276+
277+
present = np.where(target_mask.any(axis=(1, 2)))[0]
278+
ap_lo, ap_hi = (int(present.min()), int(present.max())) if present.size else (0, AP - 1)
279+
ap_indices = list(range(ap_lo, ap_hi + 1, max(1, int(gif_ap_step))))
280+
281+
frames = []
282+
for ap in ap_indices:
283+
_draw(ap)
284+
fig = plt.gcf()
285+
fig.canvas.draw()
286+
w, h = fig.canvas.get_width_height()
287+
rgba = np.frombuffer(fig.canvas.buffer_rgba(), dtype=np.uint8).reshape(h, w, 4)
288+
img = Image.fromarray(rgba[:, :, :3].copy())
289+
if gif_width and img.width > gif_width:
290+
img = img.resize((int(gif_width), round(img.height * gif_width / img.width)))
291+
frames.append(img.convert('P', palette=Image.ADAPTIVE, colors=256))
292+
plt.close(fig)
293+
294+
if frames:
295+
frames[0].save(save_gif, save_all=True, append_images=frames[1:],
296+
duration=int(1000 / max(gif_fps, 1)), loop=0, optimize=True)
297+
print(f'Saved GIF: {save_gif} ({len(frames)} frames)')
298+
return
299+
259300
# ------------------------------------- static render (docs / headless use)
260301
if static_ap is not None:
261302
_draw(int(static_ap))

‎docs/generate_images.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,11 @@ def save_current(name, facecolor='white'):
138138
static_ap=55)
139139
save_current('plot_upstream_projectome_CP.png', facecolor='black')
140140

141+
# ── 9. Animated GIF scrolling through the brain (for README / docs) ──
142+
print('Generating: upstream projectome scroll GIF...')
143+
bsv.plot_upstream_projectome(
144+
proj_src_ids, proj_src_regions, 'CP', save_location, allen_atlas_path,
145+
save_gif=os.path.join(img_dir, 'upstream_projectome_scroll.gif'),
146+
gif_ap_step=2, gif_fps=8, gif_width=720)
147+
141148
print('\nDone! All images in:', img_dir)

‎docs/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ find_and_fetch
4747
plot_2d
4848
plot_3d
4949
plot_injections
50-
upstream_projectome
50+
interactive_viewer
5151
compare_regions
5252
thresholding
5353
striatum_analysis
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Interactive upstream projectome
1+
# Interactive slice viewer
22

33
Scroll through the brain and see, at every coronal level, the projections that
44
*multiple* source regions send into a target region — each source drawn in its own
@@ -12,12 +12,12 @@ slider. At each slice it overlays, on the Allen CCF structure boundaries:
1212
- injection-site dots (sized by injection volume) at the AP levels where the injections
1313
actually sit.
1414

15-
```{image} ../images/docs/plot_upstream_projectome_CP.png
15+
```{image} ../images/docs/upstream_projectome_scroll.gif
1616
:width: 100%
1717
```
18-
*One AP slice (CCF 5500 µm) of projections into the caudate putamen (CP) from four
19-
visual areas, each in its own colour. Dragging the slider scrolls through the brain;
20-
injection-site dots appear at their own (more posterior) AP levels.*
18+
*Scrolling through coronal slices: projections into the caudate putamen (CP) from four
19+
visual areas, each in its own colour. In the live widget an anterior-posterior slider
20+
drives this; injection-site dots appear at their own (more posterior) AP levels.*
2121

2222
**Python:**
2323
```python
1010 KB
Loading

0 commit comments

Comments
 (0)