Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved birefringence overlays #426

Merged
merged 14 commits into from
Aug 24, 2023
50 changes: 40 additions & 10 deletions recOrder/acq/acquisition_workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@
from recOrder.plugin.main_widget import MainWidget


def _check_scale_mismatch(
recon_scale: np.array,
ngff_scale: tuple[float, float, float, float, float],
) -> None:
if not np.allclose(np.array(ngff_scale[2:]), recon_scale, rtol=1e-2):
show_warning(
f"Requested reconstruction scale = {recon_scale} "
f"and OME-Zarr metadata scale = {ngff_scale[2:]} are not equal. "
"recOrder's reconstruction uses the GUI's "
"Z-step, pixel size, and magnification, "
"while napari's viewer uses the input array's metadata."
)


def _generate_reconstruction_config_from_gui(
reconstruction_config_path,
mode,
Expand Down Expand Up @@ -117,8 +131,8 @@ class PolarizationAcquisitionSignals(WorkerBaseSignals):
Custom Signals class that includes napari native signals
"""

phase_image_emitter = Signal(object)
bire_image_emitter = Signal(object)
phase_image_emitter = Signal(tuple)
bire_image_emitter = Signal(tuple)
phase_reconstructor_emitter = Signal(object)
aborted = Signal()

Expand All @@ -128,7 +142,7 @@ class BFAcquisitionSignals(WorkerBaseSignals):
Custom Signals class that includes napari native signals
"""

phase_image_emitter = Signal(object)
phase_image_emitter = Signal(tuple)
phase_reconstructor_emitter = Signal(object)
aborted = Signal()

Expand Down Expand Up @@ -258,7 +272,7 @@ def work(self):
# Reconstruct snapped images
self.n_slices = stack.shape[2]

phase = self._reconstruct()
phase, scale = self._reconstruct()
self._check_abort()

# Warn the user about axial
Expand All @@ -267,11 +281,18 @@ def work(self):
"Inverting the phase contrast. This affects the visualization and saved reconstruction."
)

# Warn user about mismatched scales
recon_scale = np.array(
(self.calib_window.z_step,)
+ 2 * (self.calib_window.ps / self.calib_window.mag,)
)
_check_scale_mismatch(recon_scale, scale)

logging.info("Finished Acquisition")
logging.debug("Finished Acquisition")

# Emit the images and let thread know function is finished
self.phase_image_emitter.emit(phase)
self.phase_image_emitter.emit((phase, scale))

def _reconstruct(self):
"""
Expand Down Expand Up @@ -301,8 +322,9 @@ def _reconstruct(self):
# Read reconstruction to pass to emitters
with open_ome_zarr(reconstruction_path, mode="r") as dataset:
phase = dataset["0/0/0/0"][0]
scale = dataset["0/0/0"].scale

return phase
return phase, scale

def _cleanup_acq(self):
# Get display windows
Expand Down Expand Up @@ -479,7 +501,7 @@ def work(self):
# Reconstruct snapped images
self._check_abort()
self.n_slices = stack.shape[2]
birefringence, phase = self._reconstruct()
birefringence, phase, scale = self._reconstruct()
self._check_abort()

# Warn the user about rotations and flips
Expand All @@ -492,12 +514,19 @@ def work(self):
"Applying a flip to orientation channel. This affects the visualization and saved reconstruction."
)

# Warn user about mismatched scales
recon_scale = np.array(
(self.calib_window.z_step,)
+ 2 * (self.calib_window.ps / self.calib_window.mag,)
)
_check_scale_mismatch(recon_scale, scale)

logging.info("Finished Acquisition")
logging.debug("Finished Acquisition")

# Emit the images and let thread know function is finished
self.bire_image_emitter.emit(birefringence)
self.phase_image_emitter.emit(phase)
self.bire_image_emitter.emit((birefringence, scale))
self.phase_image_emitter.emit((phase, scale))

def _check_exposure(self) -> None:
"""
Expand Down Expand Up @@ -579,8 +608,9 @@ def _reconstruct(self):
phase = czyx_data[4]
except:
phase = None
scale = dataset["0/0/0"].scale

return birefringence, phase
return birefringence, phase, scale

def _cleanup_acq(self):
# Get display windows
Expand Down
11 changes: 7 additions & 4 deletions recOrder/calib/calibration_workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ class BackgroundSignals(WorkerBaseSignals):
Custom Signals class that includes napari native signals
"""

bg_image_emitter = Signal(object)
bire_image_emitter = Signal(object)
bg_image_emitter = Signal(tuple)
bire_image_emitter = Signal(tuple)
bg_path_update_emitter = Signal(Path)
aborted = Signal()

Expand Down Expand Up @@ -358,6 +358,7 @@ def work(self):
with open_ome_zarr(reconstruction_path, mode="r") as dataset:
self.retardance = dataset["0/0/0/0"][0, 0, 0]
self.birefringence = dataset["0/0/0/0"][0, :, 0]
scale = dataset["0/0/0"].scale

# Save metadata file and emit imgs
meta_file = bg_path / "polarization_calibration.txt"
Expand All @@ -381,8 +382,10 @@ def work(self):
self._check_abort()

# Emit background images + background birefringence
self.bg_image_emitter.emit(imgs)
self.bire_image_emitter.emit((self.retardance, self.birefringence[1]))
self.bg_image_emitter.emit((imgs, scale))
self.bire_image_emitter.emit(
((self.retardance, self.birefringence[1]), scale)
)

# Emit bg path
self.bg_path_update_emitter.emit(bg_path)
Expand Down
10 changes: 8 additions & 2 deletions recOrder/io/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import textwrap
from pathlib import Path
from typing import Literal
from typing import Literal, Union

import numpy as np
import psutil
Expand Down Expand Up @@ -130,7 +130,10 @@ def generic_hsv_overlay(


def ret_ori_overlay(
retardance, orientation, ret_max=10, cmap: Literal["JCh", "HSV"] = "JCh"
retardance,
orientation,
ret_max: Union[float, Literal["auto"]] = 10,
cmap: Literal["JCh", "HSV"] = "JCh",
):
"""
This function will create an overlay of retardance and orientation with two different colormap options.
Expand All @@ -154,6 +157,9 @@ def ret_ori_overlay(
f"{retardance.shape} vs. {orientation.shape}"
)

if ret_max == "auto":
ret_max = np.percentile(np.ravel(retardance), 99.99)

# Prepare input and output arrays
ret_ = np.clip(retardance, 0, ret_max) # clip and copy
# Convert 180 degree range into 360 to match periodicity of hue.
Expand Down
Loading
Loading