Coverage for openhcs/io/fiji_stream.py: 21.7%
65 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 18:33 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 18:33 +0000
1"""
2Fiji streaming backend for OpenHCS.
4Streams image data directly to Fiji/ImageJ GUI for interactive exploration.
5Uses PyImageJ to send numpy arrays directly to running Fiji instance.
6Requires PyImageJ, JPype, and Maven to be properly configured.
7"""
9import logging
10from pathlib import Path
11from typing import Any, Union, List
12import numpy as np
14from openhcs.io.streaming import StreamingBackend
15from openhcs.io.backend_registry import StorageBackendMeta
16from openhcs.constants.constants import Backend
18logger = logging.getLogger(__name__)
21class FijiStreamingBackend(StreamingBackend, metaclass=StorageBackendMeta):
22 """
23 Fiji streaming backend with automatic metaclass registration.
25 Streams image data directly to Fiji/ImageJ GUI for interactive exploration.
26 Uses PyImageJ to send numpy arrays directly to running Fiji instance.
27 """
29 # Backend type from enum for registration
30 _backend_type = Backend.FIJI_STREAM.value # You'd need to add this to the Backend enum
32 def __init__(self):
33 """Initialize the Fiji streaming backend."""
34 self._ij = None
36 def save(self, data: Any, file_path: Union[str, Path], **kwargs) -> None:
37 """
38 Stream single image to Fiji.
40 Args:
41 data: Image data (numpy array)
42 file_path: Identifier for the image
43 **kwargs: Additional metadata
44 """
45 if not isinstance(data, np.ndarray):
46 logger.warning(f"Fiji streaming requires numpy arrays, got {type(data)}")
47 return
49 # Send image directly to Fiji GUI using PyImageJ (fail loudly)
50 self._send_to_fiji_pyimagej(data, str(file_path), **kwargs)
51 logger.debug(f"🔬 FIJI: Streamed {file_path} to Fiji GUI for exploration")
53 def save_batch(self, data_list: List[Any], file_paths: List[Union[str, Path]], **kwargs) -> None:
54 """
55 Stream batch of images to Fiji.
57 Args:
58 data_list: List of image data arrays
59 file_paths: List of file path identifiers
60 **kwargs: Additional metadata
61 """
62 if len(data_list) != len(file_paths):
63 raise ValueError("Data list and file paths must have same length")
65 for data, file_path in zip(data_list, file_paths):
66 self.save(data, file_path, **kwargs)
68 logger.info(f"🔬 FIJI: Streamed batch of {len(data_list)} images to Fiji")
70 def _send_to_fiji_pyimagej(self, data: np.ndarray, identifier: str, **kwargs) -> None:
71 """Send image directly to Fiji using PyImageJ."""
72 # Try to import PyImageJ
73 try:
74 import imagej
75 except ImportError:
76 raise ImportError("PyImageJ not available. Install with: pip install 'openhcs[viz]'")
78 # Initialize PyImageJ connection if not already done
79 if not hasattr(self, '_ij') or self._ij is None:
80 logger.info("🔬 FIJI: Attempting to connect via PyImageJ...")
81 self._ij = imagej.init(mode='interactive')
82 logger.info("🔬 FIJI: ✅ Connected to Fiji via PyImageJ")
84 # Convert numpy array to ImageJ format and display
85 ij_image = self._ij.py.to_java(data)
86 self._ij.ui().show(identifier, ij_image)
88 # Apply auto-contrast if requested
89 if kwargs.get('auto_contrast', True):
90 self._ij.op().run("enhance.contrast", ij_image)
92 logger.info(f"🔬 FIJI: ✅ Sent {identifier} to Fiji via PyImageJ")
99 # Example using command line (requires Fiji installation)
100 try:
101 import subprocess
103 # Write macro to temporary file
104 macro_file = self._temp_dir / "temp_macro.ijm"
105 macro_file.write_text(macro_cmd)
107 # Execute via Fiji (adjust path as needed)
108 fiji_path = self._get_fiji_path()
109 if fiji_path:
110 subprocess.run([
111 str(fiji_path),
112 "--headless",
113 "--console",
114 "-macro",
115 str(macro_file)
116 ], check=False, capture_output=True)
118 except Exception as e:
119 logger.warning(f"Failed to execute Fiji macro: {e}")
121 def _get_fiji_path(self) -> Path:
122 """Get path to Fiji executable."""
123 # Try common Fiji installation paths
124 common_paths = [
125 Path("/Applications/Fiji.app/Contents/MacOS/ImageJ-macosx"), # macOS
126 Path("C:/Fiji.app/ImageJ-win64.exe"), # Windows
127 Path("/opt/fiji/ImageJ-linux64"), # Linux
128 Path.home() / "Fiji.app" / "ImageJ-linux64", # User installation
129 ]
131 for path in common_paths:
132 if path.exists():
133 return path
135 logger.warning("Fiji executable not found in common locations")
136 return None
138 def cleanup(self) -> None:
139 """Clean up temporary files and resources."""
140 # Clean up temporary directory
141 if self._temp_dir and self._temp_dir.exists(): 141 ↛ anywhereline 141 didn't jump anywhere: it always raised an exception.
142 try:
143 import shutil
144 shutil.rmtree(self._temp_dir)
145 logger.debug("Cleaned up Fiji temporary files")
146 except Exception as e:
147 logger.warning(f"Failed to cleanup Fiji temp directory: {e}")
149 self._temp_dir = None
150 self._macro_queue.clear()
152 logger.debug("Fiji streaming backend cleaned up")