# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center. # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted for academic and research use only # (subject to the limitations in the disclaimer below) # provided that the following conditions are met: # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY # THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # Standard Imports # Third Party Imports import pytest import random # Local Imports from navigate.controller.sub_controllers.camera_settings import ( CameraSettingController, ) class TestCameraSettingController: @pytest.fixture(autouse=True) def setup_class(self, dummy_controller): c = dummy_controller v = dummy_controller.view self.camera_settings = CameraSettingController( v.settings.camera_settings_tab, c ) def test_init(self): assert isinstance(self.camera_settings, CameraSettingController) # Setup, going to check what the default values widgets are set too microscope_name = self.camera_settings.parent_controller.configuration[ "experiment" ]["MicroscopeState"]["microscope_name"] camera_config_dict = self.camera_settings.parent_controller.configuration[ "configuration" ]["microscopes"][microscope_name]["camera"] # Default Values assert ( self.camera_settings.default_pixel_size == camera_config_dict["pixel_size_in_microns"] ) assert self.camera_settings.default_height == camera_config_dict["y_pixels"] assert self.camera_settings.default_width == camera_config_dict["x_pixels"] # Camera Mode assert list(self.camera_settings.mode_widgets["Sensor"].widget["values"]) == [ "Normal", "Light-Sheet", ] assert ( str(self.camera_settings.mode_widgets["Sensor"].widget["state"]) == "readonly" ) # Readout Mode assert list(self.camera_settings.mode_widgets["Readout"].widget["values"]) == [ "Top-to-Bottom", "Bottom-to-Top", "Bidirectional", "Rev. Bidirectional", ] assert ( str(self.camera_settings.mode_widgets["Readout"].widget["state"]) == "disabled" ) # Pixels assert ( str(self.camera_settings.mode_widgets["Pixels"].widget["state"]) == "disabled" ) assert self.camera_settings.mode_widgets["Pixels"].widget.get() == "" assert self.camera_settings.mode_widgets["Pixels"].widget.cget("from") == 1 assert ( self.camera_settings.mode_widgets["Pixels"].widget.cget("to") == self.camera_settings.default_height / 2 ) assert self.camera_settings.mode_widgets["Pixels"].widget.cget("increment") == 1 # Framerate assert ( str(self.camera_settings.framerate_widgets["exposure_time"].widget["state"]) == "disabled" ) assert ( str(self.camera_settings.framerate_widgets["readout_time"].widget["state"]) == "disabled" ) assert ( str(self.camera_settings.framerate_widgets["max_framerate"].widget["state"]) == "disabled" ) # Set range value assert ( self.camera_settings.roi_widgets["Width"].widget.cget("to") == self.camera_settings.default_width ) assert self.camera_settings.roi_widgets["Width"].widget.cget("from") == 2 assert self.camera_settings.roi_widgets["Width"].widget.cget("increment") == 2 assert ( self.camera_settings.roi_widgets["Height"].widget.cget("to") == self.camera_settings.default_height ) assert self.camera_settings.roi_widgets["Height"].widget.cget("from") == 2 assert self.camera_settings.roi_widgets["Height"].widget.cget("increment") == 2 # Set binning options assert list(self.camera_settings.roi_widgets["Binning"].widget["values"]) == [ "{}x{}".format(i, i) for i in [1, 2, 4] ] assert ( str(self.camera_settings.roi_widgets["Binning"].widget["state"]) == "readonly" ) # FOV assert ( str(self.camera_settings.roi_widgets["FOV_X"].widget["state"]) == "disabled" ) assert ( str(self.camera_settings.roi_widgets["FOV_Y"].widget["state"]) == "disabled" ) def test_attr(self): attrs = [ "in_initialization", "resolution_value", "mode", "solvent", "mode_widgets", "framerate_widgets", "roi_widgets", "roi_btns", "default_pixel_size", "default_width", "default_height", "pixel_event_id", ] for attr in attrs: assert hasattr(self.camera_settings, attr) def test_populate_experiment_values(self): microscope_name = self.camera_settings.parent_controller.configuration[ "experiment" ]["MicroscopeState"]["microscope_name"] self.camera_settings.parent_controller.configuration["experiment"][ "CameraParameters" ][microscope_name]["readout_time"] = 0.1 # Populate widgets with values from experiment file and check self.camera_settings.populate_experiment_values() camera_setting_dict = self.camera_settings.parent_controller.configuration[ "experiment" ]["CameraParameters"][microscope_name] # Checking values altered are correct assert dict(self.camera_settings.camera_setting_dict) == dict( self.camera_settings.parent_controller.configuration["experiment"][ "CameraParameters" ][microscope_name] ) assert ( str(self.camera_settings.mode_widgets["Sensor"].get()) == camera_setting_dict["sensor_mode"] ) if camera_setting_dict["sensor_mode"] == "Normal": pass # assert str(self.camera_settings.mode_widgets[ # "Readout"].get()) == "" # assert str(self.camera_settings.mode_widgets[ # "Pixels"].get()) == "" elif camera_setting_dict["sensor_mode"] == "Light-Sheet": assert ( str(self.camera_settings.mode_widgets["Readout"].get()) == self.camera_settings.camera_setting_dict["readout_direction"] ) assert ( str(self.camera_settings.mode_widgets["Pixels"].get()) == self.camera_settings.camera_setting_dict["number_of_pixels"] ) # ROI assert ( self.camera_settings.roi_widgets["Width"].get() == camera_setting_dict["x_pixels"] ) assert ( self.camera_settings.roi_widgets["Height"].get() == camera_setting_dict["y_pixels"] ) assert self.camera_settings.roi_widgets[ "Top_X" ].get() == camera_setting_dict.get("top_x", 0) assert self.camera_settings.roi_widgets[ "Top_Y" ].get() == camera_setting_dict.get("top_y", 0) if camera_setting_dict.get("is_centered", True): assert ( str(self.camera_settings.roi_widgets["Top_X"].widget["state"]) == "disabled" ) assert ( str(self.camera_settings.roi_widgets["Top_Y"].widget["state"]) == "disabled" ) # Binning assert ( str(self.camera_settings.roi_widgets["Binning"].get()) == camera_setting_dict["binning"] ) # Exposure Time channels = self.camera_settings.parent_controller.configuration["experiment"][ "MicroscopeState" ]["channels"] exposure_time = channels[list(channels.keys())[0]]["camera_exposure_time"] assert ( self.camera_settings.framerate_widgets["exposure_time"].get() == exposure_time ) assert ( self.camera_settings.framerate_widgets["frames_to_average"].get() == camera_setting_dict["frames_to_average"] ) assert self.camera_settings.in_initialization is False @pytest.mark.parametrize("mode", ["Normal", "Light-Sheet"]) def test_update_experiment_values(self, mode): microscope_name = self.camera_settings.parent_controller.configuration[ "experiment" ]["MicroscopeState"]["microscope_name"] # Setup basic default experiment self.camera_settings.camera_setting_dict = ( self.camera_settings.parent_controller.configuration["experiment"][ "CameraParameters" ][microscope_name] ) # Setting up new values in widgets self.camera_settings.mode_widgets["Sensor"].set(mode) self.camera_settings.roi_widgets["Binning"].set("4x4") if mode == "Light-Sheet": self.camera_settings.mode_widgets["Readout"].set("Bottom-to-Top") self.camera_settings.mode_widgets["Pixels"].set(15) self.camera_settings.roi_widgets["Binning"].set("1x1") width, height = random.randint(1, 2000), random.randint(1, 2000) self.camera_settings.roi_widgets["Width"].set(width) self.camera_settings.roi_widgets["Height"].set(height) self.camera_settings.framerate_widgets["frames_to_average"].set(5) # Update experiment dict and assert self.camera_settings.update_experiment_values() assert self.camera_settings.camera_setting_dict["sensor_mode"] == mode if mode == "Light-Sheet": assert ( self.camera_settings.camera_setting_dict["readout_direction"] == "Bottom-to-Top" ) assert ( int(self.camera_settings.camera_setting_dict["number_of_pixels"]) == 15 ) step_width = self.camera_settings.step_width step_height = self.camera_settings.step_height set_width = int(width // step_width) * step_width set_height = int(height // step_height) * step_height if mode == "Light-Sheet": assert self.camera_settings.camera_setting_dict["binning"] == "1x1" assert self.camera_settings.camera_setting_dict["img_x_pixels"] == set_width assert ( self.camera_settings.camera_setting_dict["img_y_pixels"] == set_height ) binning = 1 else: assert self.camera_settings.camera_setting_dict["binning"] == "4x4" # make sure image size is divisible by step_width and step_height assert self.camera_settings.camera_setting_dict["img_x_pixels"] == ( set_width // 4 ) - (set_width // 4 % step_width) assert self.camera_settings.camera_setting_dict["img_y_pixels"] == ( set_height // 4 ) - (set_height // 4 % step_height) binning = 4 # make sure x, y pixels are img_x, img_y pixels * binning assert ( self.camera_settings.camera_setting_dict["x_pixels"] == self.camera_settings.camera_setting_dict["img_x_pixels"] * binning ) assert ( self.camera_settings.camera_setting_dict["y_pixels"] == self.camera_settings.camera_setting_dict["img_y_pixels"] * binning ) assert ( self.camera_settings.camera_setting_dict["pixel_size"] == self.camera_settings.default_pixel_size ) assert self.camera_settings.camera_setting_dict["frames_to_average"] == 5 @pytest.mark.parametrize("mode", ["Normal", "Light-Sheet"]) def test_update_sensor_mode(self, mode): self.camera_settings.populate_experiment_values() microscope_name = self.camera_settings.parent_controller.configuration[ "experiment" ]["MicroscopeState"]["microscope_name"] camera_setting_dict = self.camera_settings.parent_controller.configuration[ "experiment" ]["CameraParameters"][microscope_name] # Set mode self.camera_settings.mode_widgets["Sensor"].widget.set(mode) self.camera_settings.mode_widgets["Sensor"].widget.event_generate( "<>" ) # Call update # self.camera_settings.update_sensor_mode() # Check values if mode == "Normal": assert str(self.camera_settings.mode_widgets["Readout"].get()) == " " assert ( str(self.camera_settings.mode_widgets["Readout"].widget["state"]) == "disabled" ) assert ( str(self.camera_settings.mode_widgets["Pixels"].widget["state"]) == "disabled" ) assert str(self.camera_settings.mode_widgets["Pixels"].widget.get()) == "" if mode == "Light-Sheet": assert ( str(self.camera_settings.mode_widgets["Readout"].get()) == camera_setting_dict["readout_direction"] ) assert ( str(self.camera_settings.mode_widgets["Readout"].widget["state"]) == "readonly" ) assert ( str(self.camera_settings.mode_widgets["Pixels"].widget["state"]) == "normal" ) assert int(self.camera_settings.mode_widgets["Pixels"].widget.get()) == int( self.camera_settings.camera_setting_dict["number_of_pixels"] ) def test_update_exposure_time(self): # Call funciton self.camera_settings.update_exposure_time(35) # Check assert self.camera_settings.framerate_widgets["exposure_time"].get() == 35 @pytest.mark.parametrize("name", ["All", "1600", "1024", "512"]) def test_update_roi(self, name): # Call button to check if handler setup correctly self.camera_settings.roi_btns[name].invoke() # Check if name == "All": name = "2048" assert str(self.camera_settings.roi_widgets["Width"].get()) == name assert str(self.camera_settings.roi_widgets["Height"].get()) == name def test_update_fov(self): self.camera_settings.populate_experiment_values() # Change invoke self.camera_settings.in_initialization = False self.camera_settings.roi_widgets["Width"].widget.set(2048) self.camera_settings.roi_widgets["Height"].widget.set(2048) xFov = int(self.camera_settings.roi_widgets["FOV_X"].get()) yFov = int(self.camera_settings.roi_widgets["FOV_Y"].get()) self.camera_settings.roi_widgets["Width"].widget.set(1600) self.camera_settings.roi_widgets["Height"].widget.set(1600) # need these since we switched to read events self.camera_settings.roi_widgets["Width"].get_variable().get() self.camera_settings.roi_widgets["Height"].get_variable().get() # Check assert xFov != int(self.camera_settings.roi_widgets["FOV_X"].get()) assert yFov != int(self.camera_settings.roi_widgets["FOV_Y"].get()) # Reset self.camera_settings.roi_widgets["Width"].widget.set(2048) self.camera_settings.roi_widgets["Height"].widget.set(2048) # need these since we switched to read events self.camera_settings.roi_widgets["Width"].get_variable().get() self.camera_settings.roi_widgets["Height"].get_variable().get() assert int(self.camera_settings.roi_widgets["FOV_X"].get()) == 13066 assert int(self.camera_settings.roi_widgets["FOV_Y"].get()) == 13066 @pytest.mark.parametrize("mode", ["live", "z-stack", "stop", "single"]) @pytest.mark.parametrize("readout", ["Normal", "Light-Sheet"]) def test_set_mode(self, mode, readout): # Populate widgets with values from experiment file self.camera_settings.populate_experiment_values() # Set mode self.camera_settings.mode_widgets["Sensor"].widget.set(readout) self.camera_settings.update_sensor_mode() self.camera_settings.set_mode(mode) # Check assert self.camera_settings.mode == mode if mode != "stop": state = "disabled" else: state = "normal" if mode != "stop": state_readonly = "disabled" else: state_readonly = "readonly" assert ( str(self.camera_settings.mode_widgets["Sensor"].widget["state"]) == state_readonly ) if str(self.camera_settings.mode_widgets["Sensor"].get()) == "Light-Sheet": assert ( str(self.camera_settings.mode_widgets["Readout"].widget["state"]) == state_readonly ) if mode == "live": assert ( str(self.camera_settings.mode_widgets["Pixels"].widget["state"]) == "normal" ) else: assert ( str(self.camera_settings.mode_widgets["Pixels"].widget["state"]) == state ) else: assert ( str(self.camera_settings.mode_widgets["Readout"].widget["state"]) == "disabled" ) assert ( str(self.camera_settings.mode_widgets["Pixels"].widget["state"]) == "disabled" ) assert ( str( self.camera_settings.framerate_widgets["frames_to_average"].widget[ "state" ] ) == state ) assert str(self.camera_settings.roi_widgets["Width"].widget["state"]) == state assert str(self.camera_settings.roi_widgets["Height"].widget["state"]) == state assert ( str(self.camera_settings.roi_widgets["Binning"].widget["state"]) == state_readonly ) for btn_name in self.camera_settings.roi_btns: assert str(self.camera_settings.roi_btns[btn_name]["state"]) == state @pytest.mark.parametrize("zoom", ["0.63x", "1x", "2x", "3x", "4x", "5x", "6x"]) def test_calculate_physical_dimensions(self, zoom): self.camera_settings.parent_controller.configuration["experiment"][ "MicroscopeState" ]["zoom"] = zoom self.camera_settings.populate_experiment_values() # Calling self.camera_settings.calculate_physical_dimensions() pixel_size = self.camera_settings.default_pixel_size x_pixel = float(self.camera_settings.roi_widgets["Width"].get()) y_pixel = float(self.camera_settings.roi_widgets["Height"].get()) microscope_state_dict = self.camera_settings.parent_controller.configuration[ "experiment" ]["MicroscopeState"] zoom = microscope_state_dict["zoom"] microscope_name = microscope_state_dict["microscope_name"] pixel_size = self.camera_settings.parent_controller.configuration[ "configuration" ]["microscopes"][microscope_name]["zoom"]["pixel_size"][zoom] dim_x = x_pixel * pixel_size dim_y = y_pixel * pixel_size assert float(self.camera_settings.roi_widgets["FOV_X"].get()) == float( int(dim_x) ) assert float(self.camera_settings.roi_widgets["FOV_Y"].get()) == float( int(dim_y) ) # Reset to zoom of 1 self.camera_settings.parent_controller.configuration["experiment"][ "MicroscopeState" ]["zoom"] = "1x" assert ( self.camera_settings.parent_controller.configuration["experiment"][ "MicroscopeState" ]["zoom"] == "1x" ) def test_calculate_readout_time(self): """ TODO need more info about camera before testing """ pass @pytest.mark.parametrize( "mode", ["single", "live", "customized", "z-stack", "stop"] ) def test_update_number_of_pixels(self, mode): import random self.camera_settings.populate_experiment_values() self.camera_settings.mode = mode self.camera_settings.mode_widgets["Pixels"].set("") assert self.camera_settings.camera_setting_dict["number_of_pixels"] != "" n_pixels = random.randint(1, 100) self.camera_settings.mode_widgets["Pixels"].set(n_pixels) # Check assert self.camera_settings.camera_setting_dict["number_of_pixels"] == int( n_pixels ) if mode != "live" and mode != "stop": assert ( self.camera_settings.camera_setting_dict["number_of_pixels"] == n_pixels ) @pytest.mark.parametrize( "x_pixels, y_pixels", [(512, 512), (4096, 4096), (2304, 1024), (1024, 2048)] ) def test_update_camera_device_related_setting(self, x_pixels, y_pixels): self.camera_settings.populate_experiment_values() microscope_name = self.camera_settings.parent_controller.configuration[ "experiment" ]["MicroscopeState"]["microscope_name"] camera_config = self.camera_settings.parent_controller.configuration[ "configuration" ]["microscopes"][microscope_name]["camera"] default_x_pixels = camera_config["x_pixels"] default_y_pixels = camera_config["y_pixels"] camera_config["x_pixels"] = x_pixels camera_config["y_pixels"] = y_pixels self.camera_settings.update_camera_device_related_setting() assert self.camera_settings.roi_widgets["Width"].get() == min( self.camera_settings.camera_setting_dict["x_pixels"], x_pixels ) assert self.camera_settings.roi_widgets["Height"].get() == min( self.camera_settings.camera_setting_dict["y_pixels"], y_pixels ) assert self.camera_settings.roi_widgets["Width"].widget["to"] == x_pixels assert self.camera_settings.roi_widgets["Height"].widget["to"] == y_pixels camera_config["x_pixels"] = default_x_pixels camera_config["y_pixels"] = default_y_pixels