.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/webgl/plot_panels_headless.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_webgl_plot_panels_headless.py: ============================================= Plot 3D Brain Views Headlessly (No Browser) ============================================= ``cortex.export.save_3d_views`` and ``cortex.export.plot_panels`` can render and save multiple 3D screenshots of brain data without any manual browser interaction by passing ``headless=True``. Under the hood this launches a headless Chromium browser via Playwright, which connects to the pycortex webviewer and renders the WebGL scene using software rasterisation. You can use these functions to display and save 3D views of brain data in scripts and notebooks. Prerequisites ------------- Install Playwright and download the bundled Chromium binary once:: pip install playwright playwright install chromium .. GENERATED FROM PYTHON SOURCE LINES 23-48 .. code-block:: Python import os import tempfile import numpy as np import matplotlib.pyplot as plt import cortex import cortex.export np.random.seed(42) volume = cortex.Volume.random(subject="S1", xfmname="fullhead") # Choose which angles and surface states to render # Each entry in ``list_angles`` is paired with the corresponding entry in # ``list_surfaces``. Both lists must have the same length. list_angles = [ "lateral_pivot", "medial_pivot", "left", "right", ] list_surfaces = ["inflated"] * len(list_angles) .. GENERATED FROM PYTHON SOURCE LINES 49-54 Render and save using plot_panels --------------------------------- Build a list of panels (one panel per angle/surface) and render them into a single figure with `cortex.export.plot_panels`. This uses the same headless renderer as `cortex.export.save_3d_views`. .. GENERATED FROM PYTHON SOURCE LINES 54-75 .. code-block:: Python panels: list[cortex.export.PanelParams] = [] n = len(list_angles) for i, (angle, surface) in enumerate(zip(list_angles, list_surfaces)): panels.append( { "extent": (i / n, 0.0, 1.0 / n, 1.0), "view": cortex.export.PanelView(angle=angle, surface=surface), } ) fig = cortex.export.plot_panels( volume, panels=panels, figsize=(2 * n, 2), windowsize=(1024 * 2, 768 * 2), viewer_params=dict(labels_visible=[], overlays_visible=["rois"]), headless=True, ) plt.show() .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_001.png :alt: plot panels headless :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Generating new ctm file... wm wm inflated inflated Started server on port 39192 {'camera.azimuth': 270, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 0, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 90, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 0, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} Stopping server .. GENERATED FROM PYTHON SOURCE LINES 76-79 Save individual views to files ------------------------------ ``cortex.export.save_3d_views`` saves each angle as a separate PNG file. .. GENERATED FROM PYTHON SOURCE LINES 79-104 .. code-block:: Python # sphinx_gallery_multi_image_block = "single" TARGET_WIDTH = 10 # inches — consistent width for uniform title sizing base_name = os.path.join(tempfile.mkdtemp(), "fig") fnames = cortex.export.save_3d_views( volume, base_name=base_name, list_angles=list_angles, list_surfaces=list_surfaces, viewer_params=dict(labels_visible=[], overlays_visible=["rois"]), headless=True, ) for fname, angle in zip(fnames, list_angles): img = plt.imread(fname) aspect = img.shape[0] / img.shape[1] fig, ax = plt.subplots(figsize=(TARGET_WIDTH, TARGET_WIDTH * aspect)) ax.imshow(img) ax.axis("off") ax.set_title(angle, fontsize=14, fontweight="bold") fig.subplots_adjust(left=0, right=1, top=0.88, bottom=0) plt.show() .. rst-class:: sphx-glr-horizontal * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_002.png :alt: lateral_pivot :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_002.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_003.png :alt: medial_pivot :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_003.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_004.png :alt: left :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_004.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_005.png :alt: right :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_005.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Started server on port 6691 {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 0 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 10 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 {'camera.azimuth': 90, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 0, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 90 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 0 waiting for surface.S1.shift {} -> 0 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 {'camera.azimuth': 270, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 0, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 270 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 0 waiting for surface.S1.shift {} -> 0 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 Stopping server .. GENERATED FROM PYTHON SOURCE LINES 105-112 Predefined panel layouts ------------------------ ``cortex.export`` ships with several ready-made panel configurations. Every public name matching ``params_*`` is a dict that can be passed directly to ``cortex.export.plot_panels``. The loop below discovers them automatically, so this gallery stays up-to-date when new presets are added. .. GENERATED FROM PYTHON SOURCE LINES 112-134 .. code-block:: Python # sphinx_gallery_multi_image_block = "single" predefined = { name: getattr(cortex.export, name) for name in sorted(dir(cortex.export)) if name.startswith("params_") } for name, params in predefined.items(): fig = cortex.export.plot_panels(volume, headless=True, **params) w, h = fig.get_size_inches() # Rescale to a consistent width new_w = TARGET_WIDTH new_h = h * (TARGET_WIDTH / w) + 0.6 scale = (h * TARGET_WIDTH / w) / new_h for ax in fig.get_axes(): pos = ax.get_position() ax.set_position([pos.x0, pos.y0 * scale, pos.width, pos.height * scale]) fig.set_size_inches(new_w, new_h) fig.suptitle(name, fontsize=14, fontweight="bold", y=1.0 - 0.2 / new_h) plt.show() .. rst-class:: sphx-glr-horizontal * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_006.png :alt: params_flatmap_inflated_lateral_medial_ventral :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_006.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_007.png :alt: params_flatmap_lateral_medial :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_007.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_008.png :alt: params_inflated_dorsal_lateral_medial_ventral :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_008.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_009.png :alt: params_inflatedless_lateral_medial_ventral :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_009.png :class: sphx-glr-single-img * .. image-sg:: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_010.png :alt: params_occipital_triple_view :srcset: /auto_examples/webgl/images/sphx_glr_plot_panels_headless_010.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Started server on port 60082 {'camera.azimuth': 180, 'camera.altitude': 180, 'camera.target': [0, -100, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.altitude 179.9 -> 180 {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 0 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 10 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 {'camera.azimuth': 180, 'camera.altitude': 0, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 1, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 180 waiting for camera.altitude {} -> 0 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 1 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 0 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 Stopping server Started server on port 13581 {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 0 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 10 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 {'camera.azimuth': 180, 'camera.altitude': 0, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 1, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 180 waiting for camera.altitude {} -> 0 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 1 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 0 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 Stopping server Started server on port 23598 {'camera.azimuth': 180, 'camera.altitude': 180, 'camera.target': [0, -100, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.altitude 179.9 -> 180 {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 0 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 10 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 {'camera.azimuth': 180, 'camera.altitude': 0, 'camera.target': [0, -100, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 180 waiting for camera.altitude {} -> 0 waiting for camera.target {} -> [0, -100, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 10 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 Stopping server Started server on port 21312 {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.25, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 180, 'camera.altitude': 180, 'camera.target': [0, -100, 0], 'surface.{subject}.unfold': 0.25, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.altitude 179.9 -> 180 {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.25, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} Stopping server Started server on port 31735 {'camera.azimuth': 180, 'camera.altitude': 180, 'camera.target': [0, -100, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.altitude 179.9 -> 180 {'camera.azimuth': 180, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} {'camera.azimuth': 0, 'camera.altitude': 90, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 0.5, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 10, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 0 waiting for camera.altitude {} -> 90 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 0.5 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 10 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 {'camera.azimuth': 180, 'camera.altitude': 0, 'camera.target': [0, 0, 0], 'surface.{subject}.unfold': 1, 'surface.{subject}.pivot': 180, 'surface.{subject}.shift': 0, 'surface.{subject}.specularity': 0, 'surface.{subject}.sampler': 'nearest', 'surface.{subject}.layers': 1} waiting for camera.azimuth {} -> 180 waiting for camera.altitude {} -> 0 waiting for camera.target {} -> [0, 0, 0] waiting for surface.S1.unfold {} -> 1 waiting for surface.S1.pivot {} -> 180 waiting for surface.S1.shift {} -> 0 waiting for surface.S1.specularity {} -> 0 waiting for surface.S1.sampler {} -> nearest waiting for surface.S1.layers {} -> 1 Stopping server .. rst-class:: sphx-glr-timing **Total running time of the script:** (6 minutes 33.686 seconds) .. _sphx_glr_download_auto_examples_webgl_plot_panels_headless.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_panels_headless.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_panels_headless.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_panels_headless.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_