Skip to main content

Data Export Guide

The Voyager API provides multiple methods for exporting your CT scan data. This guide covers all export options and helps you choose the right method for your use case.

Export Methods Overview

Voyager offers three main export methods:
MethodData ScopeResolutionOutput FormatBest For
Project ExportEntire projectOriginalZipped folderComplete backups, archiving
Data View ExportSingle volumeDownsampledNRRDQuick analysis, visualization
Individual File ExportSingle fileOriginalVariousSelective downloads

Project Export

Project export creates a comprehensive ZIP archive containing all project data.

What’s Included

  • Meshes: STL, GLB, PLY formats
  • Volumes: Full-resolution TIFF stacks and NRRD files
  • Radiographs: Original scan images
  • Surface Captures: Texture data
  • Metadata: Project information and analysis results

Requesting an Export

  • Python
  • Python Client
import requests
import time

api_url = "https://voyager.lumafield.com"
project_id = "your-project-uuid"
headers = {"Authorization": f"Token {token}"}

# Request export
response = requests.post(
    f"{api_url}/api/v2/projects/{project_id}/export",
    headers=headers,
    json={
        "config": {
            "exportMeshes": True,
            "exportVolumes": True,
            "exportRadiographs": True
        },
        "sendToRequester": False
    }
)
response.raise_for_status()
export_info = response.json()
print(f"Export requested: {export_info['id']}")

Checking Export Status

Project exports are asynchronous and can take 5-30+ minutes. Poll the status endpoint:
  • Python
  • Python Client
def wait_for_export(project_id, max_wait=1800, check_interval=30):
    """Wait for export to complete"""
    start_time = time.time()
    
    while time.time() - start_time < max_wait:
        response = requests.get(
            f"{api_url}/api/v2/projects/{project_id}/export",
            headers=headers
        )
        response.raise_for_status()
        status = response.json()
        
        if status.get("url"):
            print("Export ready!")
            return status["url"]
        
        print(f"Still processing... ({int(time.time() - start_time)}s elapsed)")
        time.sleep(check_interval)
    
    raise TimeoutError("Export not ready after maximum wait time")

download_url = wait_for_export(project_id)

Downloading the Export

Once ready, download the ZIP archive:
import os

def download_export(download_url, output_path):
    """Download export archive"""
    response = requests.get(download_url, stream=True)
    response.raise_for_status()
    
    total_size = int(response.headers.get("content-length", 0))
    downloaded = 0
    
    with open(output_path, "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:
                f.write(chunk)
                downloaded += len(chunk)
                if total_size > 0:
                    progress = (downloaded / total_size) * 100
                    print(f"\rProgress: {progress:.1f}%", end="", flush=True)
    print("\nDownload complete!")

download_export(download_url, "project_export.zip")

Downloading Data Views

Data views are one of the most efficient ways to download volume data. They provide downsampled, single-file NRRD volumes that are perfect for quick analysis and visualization.

Complete Data View Download Example

Here’s a complete example for downloading a data view:
  • Python
  • Python Client
import requests
import os

api_url = "https://voyager.lumafield.com"
project_id = "your-project-uuid"
headers = {"Authorization": f"Token {token}"}

# Step 1: Get project to find volume documents
project_response = requests.get(
    f"{api_url}/api/v2/projects/{project_id}",
    headers=headers
)
project = project_response.json()

# Step 2: Extract volume document ID
current_revision = project.get("currentRevision", {})
document = current_revision.get("document", {})
objects = document.get("objects", {})

# Find first volume
volume_doc_id = None
for doc_id, doc_obj in objects.items():
    if doc_obj.get("type") == "volume":
        volume_doc_id = doc_id
        data_object_id = doc_obj.get("dataObjectId")
        break

if not data_object_id:
    raise ValueError("No volume found in project")

# Step 3: Request data view
print("Requesting data view...")
data_view_response = requests.get(
    f"{api_url}/api/v2/data/{data_object_id}/view",
    headers=headers,
    params={
        "outputFormat": "nrrd",
        "max_voxel_count": 100000000,  # 100M voxels
        "max_dimension_size": 2048
    }
)
data_view_response.raise_for_status()
data_view = data_view_response.json()

# Step 4: Download the NRRD file
download_url = data_view["data"]
print(f"Downloading data view from: {download_url}")

output_path = "data_view.nrrd"
response = requests.get(download_url, stream=True)
response.raise_for_status()

with open(output_path, "wb") as f:
    for chunk in response.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)

file_size_mb = os.path.getsize(output_path) / (1024 * 1024)
print(f"✓ Data view downloaded: {output_path} ({file_size_mb:.1f} MB)")

Data View Workflow

The data view download process:
  1. Get project - Retrieve project details to find volume documents
  2. Find volume - Locate the volume document you want to export
  3. Get data object ID - Extract the data object ID from the volume document
  4. Request data view - Call /api/v2/data/{id}/view with parameters
  5. Download file - Download the NRRD file from the returned URL

Data View Parameters Explained

  • outputFormat: Choose "nrrd" (recommended), "raw", or "tiff"
  • max_voxel_count: Limits total voxels (lower = smaller files)
    • Default: 1 billion (1,000,000,000)
    • For web visualization: 10-50 million
    • For quick analysis: 100-500 million
  • max_dimension_size: Maximum size per dimension (default: 2048)
  • mapped_percentile_range: Intensity mapping [min, max] (default: [0.2, 99.8])
  • skip_normalization: Skip normalization step (default: false)

Why Use Data Views?

Data views offer significant advantages:
  • Single file - NRRD format is a single file vs. multi-file TIFF stacks
  • Smaller size - Typically 10-1000x smaller than full volumes
  • Faster downloads - Much quicker to download and process
  • Web-friendly - NRRD works well for web visualization
  • Cached - Subsequent requests use cached data (faster)

Data View vs. Full Volume Export

FeatureData ViewFull Volume Export
File FormatSingle NRRD fileMulti-file TIFF stack
File Size1-500 MB100 MB - 50+ GB
ResolutionDownsampledFull resolution
Download TimeSeconds to minutesMinutes to hours
Use CaseQuick analysis, visualizationDetailed analysis, research

Data View Export

Data views provide downsampled volume data optimized for quick analysis and visualization. This is one of the most efficient ways to download volume data when you don’t need full resolution.

When to Use Data Views

  • Quick data exploration - Analyze data without downloading full-resolution volumes
  • Web-based visualization - NRRD format is web-friendly and loads quickly
  • Testing workflows - Use smaller datasets to test your analysis pipelines
  • Reduced storage - Data views are typically 10-1000x smaller than full volumes
  • Performance-critical applications - Faster downloads and processing

Understanding Data Views

Data views are cached, downsampled representations of volume data:
  • First request: Triggers computation (may take longer)
  • Subsequent requests: Uses cached data (much faster)
  • Format: Single NRRD file (vs. multi-file TIFF stacks for full volumes)
  • Size: Typically 1-500 MB (vs. 100 MB - 50+ GB for full volumes)

Requesting and Downloading a Data View

  • Python
  • Python Client
data_object_id = "your-data-object-uuid"

response = requests.get(
    f"{api_url}/api/v2/data/{data_object_id}/view",
    headers=headers,
    params={
        "outputFormat": "nrrd",
        "max_voxel_count": 100000000,  # 100M voxels
        "max_dimension_size": 2048,
        "mapped_percentile_range": [0.2, 99.8]
    }
)
response.raise_for_status()
data_view = response.json()

# Download the NRRD file
download_url = data_view["data"]
# ... download logic ...

Data View Parameters

  • outputFormat: "nrrd" (default), "raw", or "tiff"
  • max_voxel_count: Maximum number of voxels (default: 1 billion)
  • max_dimension_size: Maximum dimension size (default: 2048)
  • mapped_percentile_range: Intensity mapping range [min, max] (default: [0.2, 99.8])
  • skip_normalization: Skip normalization (default: false)

Individual File Export

Download specific files from data objects using pre-signed URLs.

Getting a Download URL

  • Python
  • Python Client
data_object_id = "your-data-object-uuid"
filename = "mesh.stl"

response = requests.get(
    f"{api_url}/api/v2/export/{data_object_id}/{filename}",
    headers=headers
)
response.raise_for_status()

download_info = response.json()
download_url = download_info["url"]

# Download the file
file_response = requests.get(download_url, stream=True)
with open(filename, "wb") as f:
    for chunk in file_response.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)

Finding Available Files

To find available files in a data object:
# Get data object details
response = requests.get(
    f"{api_url}/api/v2/data/{data_object_id}",
    headers=headers
)
data_object = response.json()

# List available files (structure varies by object type)
# Check the object's metadata for file listings

Export Formats

Mesh Formats

  • STL: Standard triangulation format (binary/ASCII)
  • GLB: glTF binary format (with textures)
  • PLY: Polygon file format (supports colors/normals)
  • Deviation PLY: Color-coded comparison meshes

Volume Formats

  • TIFF Stack: Multi-file volume data (one slice per file)
  • NRRD: Single-file volume format (for data views)
  • RAW: Raw binary volume data

Analysis Results

  • CSV: Tabular analysis data (Excel-compatible)
  • JSON: Structured metadata and results
  • PLY: 3D visualization meshes

Required Capabilities

Some export operations require specific capabilities:
  • PROJECT_DATA_EXPORT: Required for project exports
  • EXPORT_VOLUME: Required for volume exports
  • EXPORT_POROSITY_DATA: Required for porosity analysis exports
  • EXPORT_CRACK_DETECTION_DATA: Required for crack analysis exports
Contact support@lumafield.com if you need these capabilities enabled.

Best Practices

1. Export Only What You Need

Select specific data types to reduce export size and time:
config = {
    "exportMeshes": True,
    "exportVolumes": False,  # Skip if not needed
    "exportRadiographs": False  # Skip if not needed
}

2. Handle Large Exports

For large exports:
  • Use streaming downloads
  • Monitor disk space
  • Consider exporting during off-peak hours
  • Use data views for quick analysis

3. Check Export Status

Always check export status before downloading:
status = client.export_project_get(id=project_id)
if not status.get("url"):
    print("Export still processing...")
    return

4. Error Handling

Handle common errors gracefully:
try:
    export_info = client.export_project(id=project_id, config=config)
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 403:
        print("Insufficient permissions. Check PROJECT_DATA_EXPORT capability.")
    elif e.response.status_code == 404:
        print("Project not found.")
    else:
        raise

Real-World Example

Complete example: Export all active projects
from api_client import LumafieldAPIClient
import os

client = LumafieldAPIClient(api_url, email, password)

# Get all active projects
projects = client.list_projects(state=["active"])

# Export each project
for project in projects['results']:
    project_id = project['id']
    project_name = project['name']
    
    print(f"Exporting: {project_name}")
    
    try:
        # Request export
        export_info = client.export_project(
            id=project_id,
            config={
                "exportMeshes": True,
                "exportVolumes": True,
                "exportRadiographs": True
            }
        )
        
        # Wait and download (simplified - add proper polling)
        # ... download logic ...
        
        print(f"✓ Exported: {project_name}")
    except Exception as e:
        print(f"✗ Failed: {project_name} - {e}")

Next Steps