Open GenevieveBuckley opened 6 months ago
@Czaki has an IJ tiff writer here:
but has the following issues (Zulip discussion) in making it a real napari writer plugin: 1) lack of units in napari metadata, 2) I only write minimum set of metadata, it will be nice to improve it (for example save colormap etc) 3) lack of axes order in napari metadata where both Imagej-tiff and ome-tiff require to declare them.
But that code will be a good reference once we have the necessary metadata on layers.
This issue has been mentioned on Image.sc Forum. There might be relevant details there:
Relevant zulip discussion: https://napari.zulipchat.com/#narrow/stream/212875-general/topic/How.20do.20I.20save.20in.20the.20ImageJ.20TIFF-format
David has come up with this solution, for preserving the image metadata (scales, etc.)
import napari
import tifffile
from qtpy.QtWidgets import QFileDialog, QPushButton, QVBoxLayout, QWidget, QListWidget, QListWidgetItem
import numpy as np
class SaveLayerWidget(QWidget):
def __init__(self, viewer):
super().__init__()
self.viewer = viewer
# Layout and buttons
layout = QVBoxLayout()
self.layer_list = QListWidget()
self.layer_list.setSelectionMode(QListWidget.MultiSelection)
layout.addWidget(self.layer_list)
save_button = QPushButton('Save Selected Layers')
save_button.clicked.connect(self.save_selected_layers)
layout.addWidget(save_button)
self.setLayout(layout)
self.populate_layer_list()
def populate_layer_list(self):
self.layer_list.clear()
for layer in self.viewer.layers:
item = QListWidgetItem(layer.name)
item.setData(0x0100, layer)
self.layer_list.addItem(item)
def save_selected_layers(self):
selected_items = self.layer_list.selectedItems()
if not selected_items:
print("No layers selected.")
return
dialog = QFileDialog()
save_dir = dialog.getExistingDirectory(caption="Select Directory to Save Layers")
if not save_dir:
print("No directory selected.")
return
for item in selected_items:
layer = item.data(0x0100)
if isinstance(layer, napari.layers.Image):
self.save_layer(layer, save_dir)
else:
print(f"Layer {layer.name} is not an image layer. Skipping.")
def save_layer(self, layer, save_dir):
try:
image_data = layer.data
metadata = layer.metadata or {}
scale_metadata = layer.scale
if len(scale_metadata) == 3:
z_scale, y_scale, x_scale = scale_metadata
elif len(scale_metadata) == 2:
z_scale = 1
y_scale, x_scale = scale_metadata
else:
raise ValueError('Unexpected scale dimensions.')
ij_metadata = {
'axes': 'ZYX',
'spacing': z_scale,
'unit': 'micron',
'Info': 'Exported from Napari using tifffile.',
'Properties': {'License': 'Public domain'},
}
resolution = (1.0 / x_scale, 1.0 / y_scale)
filename = f"{save_dir}/{layer.name}.tif"
tifffile.imwrite(
filename,
image_data.astype(np.float32),
imagej=True,
resolution=resolution,
metadata=ij_metadata
)
print(f"Image layer {layer.name} saved with metadata to {filename}")
except Exception as e:
print(f"An error occurred: {e}")
# Hook into Napari
def main():
viewer = napari.current_viewer()
save_layer_widget = SaveLayerWidget(viewer)
viewer.window.add_dock_widget(save_layer_widget, area='right')
if __name__ == "__main__":
main()
It'd be nice if this plugin could write tiff images, as well as read them.
Adding a writer function will involve:
napari_tiff/napari_tiff_writer.py
file, similar to this examplenapari.yaml
file to include the writer plugin information (see here and here, you need to updatenapari.yaml
in two places)