Stream RHEED Video#
Stream live RHEED frames directly from your instrument to Atomscale. Analysis runs in real-time as data arrives.
When to Use This#
Your camera SDK provides frames as numpy arrays
You want real-time analysis during growth
You need results before the growth completes
Tip
For pre-recorded videos, use Upload Files instead.
Create a Streamer#
from atomscale.streaming import RHEEDStreamer
streamer = RHEEDStreamer(api_key="YOUR_API_KEY")
Initialize a Stream#
data_id = streamer.initialize(
fps=120.0,
rotations_per_min=15.0, # Use 0.0 for stationary RHEED
chunk_size=240, # Frames per chunk (2 seconds at 120fps)
stream_name="Growth 001",
physical_sample="GaN-2025-001",
)
The rotations_per_min parameter determines whether data is classified as
rotating or stationary RHEED.
Link to a Physical Sample#
The physical_sample parameter accepts either a name or a UUID:
By name (recommended for new samples):
data_id = streamer.initialize(
fps=120.0,
rotations_per_min=15.0,
chunk_size=240,
physical_sample="Wafer-A1", # Matched case-insensitively; created if not found
)
By UUID (for existing samples):
data_id = streamer.initialize(
fps=120.0,
rotations_per_min=15.0,
chunk_size=240,
physical_sample="a1b2c3d4-e5f6-7890-abcd-ef1234567890", # Must exist
)
When you also provide a project_id, the project’s tracking_physical_sample_id
configuration is automatically updated to link the sample for growth monitoring:
data_id = streamer.initialize(
fps=120.0,
rotations_per_min=15.0,
chunk_size=240,
physical_sample="Wafer-A1",
project_id="your-project-uuid",
)
Push Mode (Live Capture)#
Use when your camera SDK delivers frames in real-time:
import numpy as np
import time
fps = 120.0
chunk_size = 240
seconds_per_chunk = chunk_size / fps
for chunk_idx in range(num_chunks):
# Get frames from your camera (shape: [chunk_size, height, width])
frames = camera.capture(chunk_size) # Your camera SDK
streamer.push(data_id, chunk_idx, frames)
time.sleep(seconds_per_chunk)
streamer.finalize(data_id)
Run Mode (Buffered Data)#
Use when frames are already in memory:
import numpy as np
import time
def frame_generator(all_frames, chunk_size, fps):
"""Yield chunks at the correct pace."""
for start in range(0, len(all_frames), chunk_size):
yield all_frames[start:start + chunk_size]
time.sleep(chunk_size / fps)
# Frames already in memory
frames = np.load("recorded_frames.npy")
data_id = streamer.initialize(
fps=120.0,
rotations_per_min=0.0,
chunk_size=240,
stream_name="Playback",
)
streamer.run(data_id, frame_generator(frames, chunk_size=240, fps=120.0))
streamer.finalize(data_id)
Frame Requirements#
Data type:
uint8grayscaleShape:
(N, height, width)for chunks,(height, width)for single framesTiming: Maintain real-time pacing for accurate analysis
Best Practices#
Practice |
Why |
|---|---|
Chunk size >= 2 seconds |
Balances upload overhead with latency |
Maintain capture cadence |
Server expects real-time pacing |
Always call |
Signals stream is complete so processing can finish |
Use descriptive |
Makes data easier to find later |
Warning
Failing to call finalize() leaves the stream incomplete and may prevent
analysis from completing.