Skip to content

Commit

Permalink
Merge pull request #135 from Alpaca233/save_merged_image
Browse files Browse the repository at this point in the history
bug fix: channel colormap and configuration manager connections
  • Loading branch information
hongquanli authored Mar 4, 2025
2 parents a64f5d4 + da98c42 commit 0336ae8
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 36 deletions.
15 changes: 5 additions & 10 deletions software/control/_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,6 @@ class Acquisition:
NUMBER_OF_FOVS_PER_AF = 3
IMAGE_FORMAT = "bmp"
IMAGE_DISPLAY_SCALING_FACTOR = 0.3
PSEUDO_COLOR = False
MERGE_CHANNELS = False
PSEUDO_COLOR_MAP = {
"405": {"hex": 0x0000FF}, # blue
"488": {"hex": 0x00FF00}, # green
"561": {"hex": 0xFFCF00}, # yellow
"638": {"hex": 0xFF0000}, # red
"730": {"hex": 0x770000}, # dark red
}
DX = 0.9
DY = 0.9
DZ = 1.5
Expand Down Expand Up @@ -657,8 +648,10 @@ class SOFTWARE_POS_LIMIT:
IS_HCS = False
DYNAMIC_REGISTRATION = False
STITCH_COMPLETE_ACQUISITION = False

# Pseudo color settings
CHANNEL_COLORS_MAP = {
"405": {"hex": 0x3300FF, "name": "blue"},
"405": {"hex": 0x20ADF8, "name": "bop blue"},
"488": {"hex": 0x1FFF00, "name": "green"},
"561": {"hex": 0xFFCF00, "name": "yellow"},
"638": {"hex": 0xFF0000, "name": "red"},
Expand All @@ -667,6 +660,8 @@ class SOFTWARE_POS_LIMIT:
"G": {"hex": 0x1FFF00, "name": "green"},
"B": {"hex": 0x3300FF, "name": "blue"},
}
SAVE_IN_PSEUDO_COLOR = False
MERGE_CHANNELS = False

# Emission filter wheel
USE_ZABER_EMISSION_FILTER_WHEEL = False
Expand Down
28 changes: 14 additions & 14 deletions software/control/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1908,20 +1908,21 @@ def save_image(self, image, file_ID, config, current_path):
elif MULTIPOINT_BF_SAVING_OPTION == "Green Channel Only":
image = image[:, :, 1]

if Acquisition.PSEUDO_COLOR:
if SAVE_IN_PSEUDO_COLOR:
image = self.return_pseudo_colored_image(image, config)

if Acquisition.MERGE_CHANNELS:
if MERGE_CHANNELS:
self._save_merged_image(image, file_ID, current_path)

iio.imwrite(saving_path, image)

def _save_merged_image(self, image, file_ID, current_path):
self.image_count += 1

if self.image_count == 1:
self.merged_image = image
else:
self.merged_image += image
self.merged_image = np.maximum(self.merged_image, image)

if self.image_count == len(self.selected_configurations):
if image.dtype == np.uint16:
Expand All @@ -1931,19 +1932,22 @@ def _save_merged_image(self, image, file_ID, current_path):

iio.imwrite(saving_path, self.merged_image)
self.image_count = 0

return

def return_pseudo_colored_image(self, image, config):
if "405 nm" in config.name:
image = self.grayscale_to_rgb(image, Acquisition.PSEUDO_COLOR_MAP["405"]["hex"])
image = self.grayscale_to_rgb(image, CHANNEL_COLORS_MAP["405"]["hex"])
elif "488 nm" in config.name:
image = self.grayscale_to_rgb(image, Acquisition.PSEUDO_COLOR_MAP["488"]["hex"])
image = self.grayscale_to_rgb(image, CHANNEL_COLORS_MAP["488"]["hex"])
elif "561 nm" in config.name:
image = self.grayscale_to_rgb(image, Acquisition.PSEUDO_COLOR_MAP["561"]["hex"])
image = self.grayscale_to_rgb(image, CHANNEL_COLORS_MAP["561"]["hex"])
elif "638 nm" in config.name:
image = self.grayscale_to_rgb(image, Acquisition.PSEUDO_COLOR_MAP["638"]["hex"])
image = self.grayscale_to_rgb(image, CHANNEL_COLORS_MAP["638"]["hex"])
elif "730 nm" in config.name:
image = self.grayscale_to_rgb(image, Acquisition.PSEUDO_COLOR_MAP["730"]["hex"])
image = self.grayscale_to_rgb(image, CHANNEL_COLORS_MAP["730"]["hex"])
else:
image = np.stack([image] * 3, axis=-1)

return image

Expand All @@ -1961,7 +1965,7 @@ def update_napari(self, image, config_name, k):
self.napari_layers_init.emit(image.shape[0], image.shape[1], image.dtype)
pos = self.stage.get_pos()
objective_magnification = str(int(self.objectiveStore.get_current_objective_info()["magnification"]))
self.napari_layers_update.emit(image, pos.x_mm, pos.y_mm, k, objective_magnification + "x_" + config_name)
self.napari_layers_update.emit(image, pos.x_mm, pos.y_mm, k, objective_magnification + "x " + config_name)

def handle_dpc_generation(self, current_round_images):
keys_to_check = [
Expand Down Expand Up @@ -3767,11 +3771,9 @@ def update_laser_af_settings(
self.autofocus_configurations[objective].set_reference_image(crop_image)


class ConfigurationManager(QObject):
class ConfigurationManager:
"""Main configuration manager that coordinates channel and autofocus configurations."""

signal_profile_loaded = Signal()

def __init__(
self,
channel_manager: ChannelConfigurationManager,
Expand Down Expand Up @@ -3821,8 +3823,6 @@ def load_profile(self, profile_name: str) -> None:
if self.laser_af_manager:
self.laser_af_manager.load_configurations(objective)

self.signal_profile_loaded.emit()

def create_new_profile(self, profile_name: str) -> None:
"""Create a new profile using current configurations."""
new_profile_path = self.base_config_path / profile_name
Expand Down
18 changes: 13 additions & 5 deletions software/control/gui_hcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,9 +572,7 @@ def waitForMicrocontroller(self, timeout=5.0, error_message=None):
def loadWidgets(self):
# Initialize all GUI widgets
if ENABLE_SPINNING_DISK_CONFOCAL:
self.spinningDiskConfocalWidget = widgets.SpinningDiskConfocalWidget(
self.xlight, self.channelConfigurationManager
)
self.spinningDiskConfocalWidget = widgets.SpinningDiskConfocalWidget(self.xlight)
if ENABLE_NL5:
import control.NL5Widget as NL5Widget

Expand Down Expand Up @@ -1022,7 +1020,7 @@ def makeConnections(self):
self.wellSelectionWidget.signal_wellSelected.connect(self.wellplateMultiPointWidget.update_well_coordinates)
self.objectivesWidget.signal_objective_changed.connect(self.wellplateMultiPointWidget.update_coordinates)

self.configurationManager.signal_profile_loaded.connect(
self.profileWidget.signal_profile_changed.connect(
lambda: self.liveControlWidget.update_microscope_mode_by_name(
self.liveControlWidget.currentConfiguration.name
)
Expand All @@ -1040,7 +1038,7 @@ def slot_settings_changed_laser_af():
self.laserAutofocusControlWidget.update_init_state()
self.laserAutofocusSettingWidget.update_values()

self.configurationManager.signal_profile_loaded.connect(slot_settings_changed_laser_af)
self.profileWidget.signal_profile_changed.connect(slot_settings_changed_laser_af)
self.objectivesWidget.signal_objective_changed.connect(slot_settings_changed_laser_af)
self.laserAutofocusSettingWidget.signal_newExposureTime.connect(
self.cameraSettingWidget_focus_camera.set_exposure_time
Expand Down Expand Up @@ -1082,6 +1080,16 @@ def slot_settings_changed_laser_af():
self.piezoWidget.update_displacement_um_display
)

if ENABLE_SPINNING_DISK_CONFOCAL:
self.spinningDiskConfocalWidget.signal_toggle_confocal_widefield.connect(
self.channelConfigurationManager.toggle_confocal_widefield
)
self.spinningDiskConfocalWidget.signal_toggle_confocal_widefield.connect(
lambda: self.liveControlWidget.update_microscope_mode_by_name(
self.liveControlWidget.currentConfiguration.name
)
)

self.camera.set_callback(self.streamHandler.on_new_frame)

def setup_movement_updater(self):
Expand Down
14 changes: 7 additions & 7 deletions software/control/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,10 +643,11 @@ def show_cross_correlation_result(self, value):


class SpinningDiskConfocalWidget(QWidget):
def __init__(self, xlight, config_manager=None):
super(SpinningDiskConfocalWidget, self).__init__()

self.config_manager = config_manager
signal_toggle_confocal_widefield = Signal(bool)

def __init__(self, xlight):
super(SpinningDiskConfocalWidget, self).__init__()

self.xlight = xlight

Expand All @@ -660,8 +661,7 @@ def __init__(self, xlight, config_manager=None):

self.disk_position_state = self.xlight.get_disk_position()

if self.config_manager:
self.config_manager.toggle_confocal_widefield(self.disk_position_state)
self.signal_toggle_confocal_widefield.emit(self.disk_position_state) # signal initial state

if self.disk_position_state == 1:
self.btn_toggle_widefield.setText("Switch to Widefield")
Expand Down Expand Up @@ -780,9 +780,8 @@ def toggle_disk_position(self):
else:
self.disk_position_state = self.xlight.set_disk_position(1)
self.btn_toggle_widefield.setText("Switch to Widefield")
if self.config_manager is not None:
self.config_manager.toggle_confocal_widefield(self.disk_position_state)
self.enable_all_buttons()
self.signal_toggle_confocal_widefield.emit(self.disk_position_state)

def toggle_motor(self):
self.disable_all_buttons()
Expand Down Expand Up @@ -5271,6 +5270,7 @@ def finishedSaving(self, output_path, dtype):
self.output_path = output_path

def extractWavelength(self, name):
# TODO: Use the 'color' attribute of the ChannelMode object
# Split the string and find the wavelength number immediately after "Fluorescence"
parts = name.split()
if "Fluorescence" in parts:
Expand Down

0 comments on commit 0336ae8

Please sign in to comment.