IO/GPU#

Open in Colab

Use this page when you have microscope data on disk and want to open it. Every reader below runs on a real gold HAADF microscope session from the public bobleesj/quantem-data Hugging Face dataset, so you can see exactly what each IO path returns and how the viewers render it.

For what the dtypes mean, how much memory each load mode takes, GPU selection, and freeing memory afterward, see Memory management.

Tip

Run this exact notebook with the Colab badge above, or View or download this notebook on GitHub. For finished results, use HTML and file export to export interactive HTML or share a trusted notebook with widget state.

from quantem.widget.datasets import showfolder_gold

folder = showfolder_gold()  # downloads and caches the real session folder
sorted(p.name for p in folder.glob("*.emd"))[:5]
Tutorial ShowFolder folder: /home/runner/.cache/huggingface/hub/datasets--bobleesj--quantem-data/snapshots/ea467e0e1cbe2dfe8fa5a4bb72e2a69d86237b37/widget-tutorials/showfolder/gold-haadf-session/small
Files: 26 EMD
['0001 - HAADF 15Mx gold FOV 01 0deg.emd',
 '0002 - HAADF 15Mx gold FOV 01 90deg.emd',
 '0003 - HAADF 15Mx gold FOV 02 0deg.emd',
 '0004 - HAADF 15Mx gold FOV 02 90deg.emd',
 '0005 - HAADF 15Mx gold FOV 03 0deg.emd']

Inspect before loading#

Use ShowFolder to inspect microscopy folders before allocating memory. It builds cached thumbnails, shows image metadata, lets you star files for downstream analysis, and can open the starred images immediately as Show2D or Show3D from the embedded selection panel. This is the real session folder we just downloaded:

from quantem.widget import ShowFolder

ShowFolder(folder, thumb=256)

For 4D-STEM master files, discover_masters collects the candidate HDF5 masters before loading (this session folder has survey images only, so run this on your own acquisition folder):

from quantem.widget.io import discover_masters

masters = discover_masters("/data/session", scan_shape=(512, 512))

Two different IO paths#

Use the 4D-STEM loader for detector stacks, and use the image readers for ordinary survey images. They are intentionally different:

data on disk

reader

what is accelerated

4D-STEM HDF5 master files

load(...)

optimized detector decompression, optional detector binning, GPU-resident arrays when CUDA or Metal is available

HAADF/ADF/BF survey images, PNG, TIFF, JPEG, GIF, DM, NPY

read_image(...), read_images(...)

CPU file decoding through the format library, optional threaded batch reads, then explicit GPU transfer if you need Torch/CuPy

folders of same-size image frames

read_image_stack(...)

threaded CPU decoding, preallocated stack, then optional GPU transfer

PNG, TIFF, and EMD survey-image decoding is not a WebGPU operation and is not decoded by the browser. It happens in Python. That is still the right path for single images and 50-60 survey files because the files are independent and can be read in parallel. Move the resulting arrays to the GPU only when you are about to compute on them (see Memory management).

Survey images and frame folders#

Open a HAADF or EMD survey image#

For a single 2D microscope image, use read_image. It returns a calibrated Dataset2d when the file carries metadata, so Show2D draws the physical scale bar automatically. This is a real 3.7Mx gold overview from the session:

from quantem.widget import Show2D, read_image

overview = sorted(folder.glob("*overview*"))[0]
haadf = read_image(overview)  # Velox EMD HAADF -> calibrated Dataset2d
Show2D(haadf, display_bin=4)
  Display bin 4×: 768×768 → 192×192 (0 MB preview; full-res detail streams on zoom)

The same reader works for common image files:

image = read_image("overview.tif")    # also .png, .jpg, .bmp, .gif, .dm3, .dm4, .npy
Show2D(image)

Use this path for HAADF, ADF, BF, overview images, diffraction snapshots saved as images, and quick previews. It avoids the 4D-STEM decompression path entirely, so loading is immediate for normal image sizes.

Open many EMD survey images#

For a folder of independent microscope images, use read_images(folder, workers=8): one call reads every image in the folder concurrently and returns a list of calibrated Dataset2d (it runs on the full session in the profiling cell below). Any list of datasets renders as a gallery - here are three of the real 3.7Mx overview images. The docs widget uses display_bin=4 so the published HTML stays small; remove it when you need pixel-level inspection:

from quantem.widget import Show2D, read_image

overviews = [read_image(p) for p in sorted(folder.glob("*overview*"))[:3]]
Show2D(overviews, display_bin=4)
  Display bin 4×: 768×768 → 192×192 (0 MB preview; full-res detail streams on zoom)

This is the best fit for 50-60 HAADF/ADF/BF survey images that may be different sizes or formats. If every frame is the same size and you want a time/depth slider, read_image_stack decodes the folder in parallel into one scrubbable stack - and when the frames are a calibrated format (EMD/DM), the pixel sampling carries onto the stack so Show3D draws the scale bar. Two frames and display_bin=8 keep this docs page lightweight:

from quantem.widget import Show3D, read_image_stack

# Two fields of view keep this docs page light; drop the [1-2] for all ten.
stack = read_image_stack(folder, pattern="*15Mx* 0[1-2] 0deg*", workers=8)
Show3D(stack, display_bin=8)
Decoding 2 frames: 100%|██████████| 1/1 [00:00<?, ?frame/s]
                                                           
  Display bin 8× (explicit): 512×512 → 64×64

The same call handles PNG, TIFF, and NPY frame folders (an in-situ sequence, tilt series, or denoising sweep):

stack = read_image_stack("frames", file_type="tif", workers=8)
stack = read_image_stack("frames", pattern="frame_*.png", workers=8)

Keep the files in their original integer dtype on disk. The reader converts the viewer stack to float32, which is the right display/processing dtype for most image stacks and avoids accidental float64 memory growth.

Profile your own image folder#

Before changing file formats, measure the folder you actually have. This times the real session folder on the machine that built this page:

import time
from quantem.widget import read_images

for workers in (1, 8):
    t0 = time.perf_counter()
    images = read_images(folder, workers=workers)
    dt = time.perf_counter() - t0
    pixels = sum(ds.array.size for ds in images)
    print(f"workers={workers}: {len(images)} images, {pixels / 1e6:.0f} MP in {dt:.2f} s ({pixels / dt / 1e6:.0f} MP/s)")
workers=1: 26 images, 9 MP in 0.22 s (40 MP/s)
workers=8: 26 images, 9 MP in 0.26 s (34 MP/s)

On many image folders the parallel path is faster; on slow network storage, the disk can become the limit. For 50-60 full 4k survey images, prefer EMD or TIFF when you control the export format. PNG is fine for screenshots and compact sharing, but it is not the fastest format for high-throughput analysis.

Open a diffraction image or diffraction stack#

Use ShowDiffraction for a single diffraction pattern or a small stack of patterns:

from quantem.widget import ShowDiffraction, read_image, read_image_stack

dp = read_image("diffraction.tif")
ShowDiffraction(dp.array)

dp_stack = read_image_stack("diffraction_frames", file_type="tif")
ShowDiffraction(dp_stack.array)

This is separate from Show4DSTEM: use ShowDiffraction when the data is just one detector image or a detector-image stack, and use Show4DSTEM(load(...)) when you have a scan grid with a diffraction pattern at every probe position.

4D-STEM detector stacks#

For your own acquisitions, load opens one or more *_master.h5 files with optimized decompression, optional detector binning, and GPU-resident arrays:

from quantem.widget import load, Show4DSTEM
from quantem.widget.io import discover_masters

data = load("scan_001_master.h5", verbose=True)   # full precision when it fits
Show4DSTEM(data)

# browse a whole session quickly behind one dataset slider
masters = discover_masters("/data/session", scan_shape=(512, 512))
data = load(masters, det_bin=4, dtype="u8", verbose=True)   # browsing copy
data = load(masters, det_bin=2, dtype="u16")                # count-preserving middle ground

To check VRAM before and after the load, and to free it when you are done, see Memory management.

The public tutorial dataset ships a real binned gold 4D-STEM scan, so the same viewer runs here on real detector counts:

from quantem.widget import Show4DSTEM
from quantem.widget.datasets import show4dstem_gold

stem_dataset = show4dstem_gold(size="small")
Show4DSTEM(stem_dataset)
Source: gold_128_npy_bin8 from Hugging Face
Full stack: (128, 128, 24, 24) uint16
Preview: (32, 32, 24, 24), scan stride 4
Sampling: [8.   8.   3.68 3.68] ['A', 'A', 'mrad', 'mrad']
Processing: real-space 4x4 block average from gold_512_npy_bin8; detector remains bin8

What should I choose?#

  1. Need exact detector counts or reconstruction: keep uint16.

  2. Browsing a session to find good fields of view: try det_bin=4, dtype="u8".

  3. Need live virtual detectors with useful angular detail: try det_bin=2.

  4. Have 48 GB or more GPU memory: start full resolution.

  5. Making a tutorial or HTML preview: use a visibly reduced copy and say how it was reduced.

Do not silently crop, bin, or change dtype in a tutorial. Put the reduction in the code so another scientist can see exactly what happened.