Skip to content

Conversation

@chenyair
Copy link

Overview

This PR adds optional image compression to the Cornerstone3D cache, reducing memory usage by 10-20x for large datasets such as ultrasound cine loops and multi-frame studies.

Problem Statement

When loading large medical imaging studies (e.g., ultrasound with 500+ frames), the browser can run out of memory even with a large cache size configured. This limits the ability to prefetch entire studies for smooth cine playback.

Solution

Implement transparent image compression in the cache:

  • Images are automatically compressed after loading (asynchronous, non-blocking)
  • Compressed images are stored as WebP or JPEG blobs
  • Images are decompressed on-demand when accessed
  • Race conditions during decompression are prevented by storing the decompression promise

Key Features

  • 10-20x memory reduction - Tested with ultrasound showing ~97% reduction
  • Non-blocking - Compression happens asynchronously after image load
  • On-demand decompression - Transparent to consuming code
  • Configurable - Format (WebP/JPEG) and quality (0.0-1.0)
  • Backward compatible - Disabled by default, opt-in via configuration
  • Race condition safe - Multiple concurrent accesses handled correctly

API Changes

New Configuration Methods

// Enable compression
cache.setCompressionConfig({
  enabled: true,
  format: 'webp',  // or 'jpeg'
  quality: 0.8     // 0.0 to 1.0
});

// Get current configuration
const config = cache.getCompressionConfig();

New Type Definitions

  • CacheCompressionConfig - Configuration interface
  • ICachedImage extended with compressedBlob, compressionQuality, isCompressed

New Utilities

  • compressImageToBlob() - Compress Cornerstone image to blob
  • decompressBlobToImage() - Decompress blob back to Cornerstone image

Implementation Details

Files Modified

  • packages/core/src/cache/cache.ts - Core cache logic with compression
  • packages/core/src/cache/index.ts - Export compression utilities
  • packages/core/src/types/ICachedImage.ts - Extended interface
  • packages/core/src/types/ICache.ts - Added configuration type
  • packages/core/src/types/index.ts - Export new types

Files Added

  • packages/core/src/cache/imageCompression.ts - Compression utilities

Testing

Tested with:

  • Ultrasound studies (500+ frames)
  • Memory reduction: ~97% (3.28MB → 0.10MB per frame)
  • Visual quality: Acceptable for diagnostic viewing at quality 0.8
  • Race condition handling: Multiple concurrent accesses during decompression

Example Usage

import { cache } from '@cornerstonejs/core';

// Enable compression for memory-constrained environments
cache.setCompressionConfig({
  enabled: true,
  format: 'webp',
  quality: 0.8
});

// Images are now automatically compressed after loading
// and decompressed transparently when accessed

Breaking Changes

None - feature is disabled by default and opt-in.

Performance Characteristics

  • Compression: Asynchronous, ~50-100ms per image (non-blocking)
  • Decompression: ~20-50ms per image (on-demand)
  • Memory savings: 10-20x reduction typical
  • Quality: Configurable, 0.8 recommended for diagnostic viewing

Future Enhancements

Possible future improvements:

  • Modality-specific compression settings
  • Progressive decompression for faster initial display
  • Worker-based compression for better parallelization
  • Compression metrics/statistics API

Checklist

  • Code follows project style guidelines
  • No linter errors
  • Comprehensive JSDoc documentation
  • Backward compatible (no breaking changes)
  • Tested with real-world datasets (ultrasound)
  • Race conditions handled correctly

Add optional image compression to the cache to reduce memory usage by 10-20x
for large datasets like ultrasound cine loops.

Key features:
- Asynchronous compression after image load (non-blocking)
- On-demand decompression when images are accessed
- Race condition prevention for concurrent access during decompression
- Configurable format (WebP/JPEG) and quality (0.0-1.0)
- Backward compatible (disabled by default)

Implementation details:
- Added compressedBlob, compressionQuality, and isCompressed fields to ICachedImage
- Created imageCompression.ts module with compress/decompress utilities
- Added setCompressionConfig() and getCompressionConfig() public APIs to Cache
- Modified getImageLoadObject() to decompress on-demand
- Updated _decacheImage() to clean up compressed blobs

Example usage:
  cache.setCompressionConfig({
    enabled: true,
    format: 'webp',
    quality: 0.8
  });

Tested with ultrasound studies showing ~97% memory reduction while maintaining
acceptable visual quality for diagnostic viewing.
Copy link
Member

@sedghi sedghi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I understand the use case. However, I'm more in favor of enabling the addition of image compression to the API (setters and getters). There are countless ways to compress an object, and we certainly can't open a separate pull request for each one. Let me know what you think.

@chenyair
Copy link
Author

chenyair commented Nov 17, 2025

Thanks, I understand the use case. However, I'm more in favor of enabling the addition of image compression to the API (setters and getters). There are countless ways to compress an object, and we certainly can't open a separate pull request for each one. Let me know what you think.

Thanks for the feedback @sedghi ! From what I understand you want a more flexible API where users can plug in their own compression and decompression methods. I'm working on a fix and will upload a new commit

- Remove built-in WebP compression implementation from core
- Add CompressionProvider interface for pluggable compression strategies
- Update cache to use provider.compress() and provider.decompress()
- Keep images compressed in cache, decompress on-demand for each access
- Preserve cancelFn and decache from original image load
- Make cache completely algorithm-agnostic (WebP, JPEG, PNG, custom formats)

This allows:
- Users to implement any compression algorithm (WebP, JPEG, PNG, custom)
- Maximum memory efficiency (images stay compressed)
- Prefetching 700+ ultrasound frames in ~70MB instead of 2.1GB

API: setCompressionProvider(provider) where provider implements CompressionProvider interface
Copy link
Member

sedghi commented Nov 17, 2025

yes, just an optional api that enables registration of optional compression methods, and as an example in the docs one can use yours optionally

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants