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

Update shutter behavior and LF acquisition channel order #160

Merged
merged 21 commits into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5a266b9
fix shutter bugs
ieivanov Aug 29, 2022
3793618
fix load_calibration bugs
ieivanov Aug 29, 2022
55caabe
don't update calibration metadata
ieivanov Aug 29, 2022
1318f2d
change pol data acquisition order
ieivanov Aug 29, 2022
778b9df
Revert exposure check --- move to separate PR.
talonchandler Aug 31, 2022
4532f7f
Revert "don't update calibration metadata"
talonchandler Aug 31, 2022
238bdb1
Remove orphan `Calibration/run_calibration` code
talonchandler Aug 31, 2022
1cdd194
Correct shutter device check
talonchandler Aug 31, 2022
9792dc4
Add breaks for manually opening and closing the shutter
talonchandler Sep 1, 2022
5e663e3
Close shutter and calc black level in single function
talonchandler Sep 9, 2022
eafa746
Keep shutter open for multi-slice, multi-channel MDAs
talonchandler Sep 9, 2022
ab90712
Merge branch 'main' into bugfix-shutter-channelOrder
talonchandler Sep 9, 2022
39ae495
Move napari to a requirement for better testing
talonchandler Sep 10, 2022
45860a9
Merge branch 'bugfix-shutter-channelOrder' of https://github.com/meht…
talonchandler Sep 10, 2022
58b9790
Drop support for 3.7
talonchandler Sep 10, 2022
ec2ab0b
Update docs
talonchandler Sep 10, 2022
528e4cc
Test against 3.10
talonchandler Sep 12, 2022
547b145
Improved shutter warning msg.
talonchandler Sep 12, 2022
f5e273c
Remove unnecessary return
talonchandler Sep 12, 2022
07e984e
Tell user to return to napari window.
talonchandler Sep 12, 2022
ef9de8f
settings -> self.settings
talonchandler Sep 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pytests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10']
python-version: ['3.8', '3.9', '3.10']

steps:
- name: Checkout repo
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
python-version: [3.7, 3.8, 3.9]
python-version: ['3.8', '3.9', '3.10']

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ The acquisition, calibration, background correction, reconstruction, and applica
conda create -y -n recOrder python=3.9
conda activate recOrder
```
Install `napari` and `recOrder-napari`:
Install `recOrder-napari`:
```
pip install "napari[all]" recOrder-napari
pip install recOrder-napari
```
Open `napari` with `recOrder-napari`:
```
Expand Down
4 changes: 2 additions & 2 deletions docs/microscope-installation-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ conda create -y -n recOrder python=3.9
conda activate recOrder
```

Install `napari` and `recOrder`:
Install `recOrder`:
```
pip install "napari[all]" recOrder-napari
pip install recOrder-napari
```
Check your installation:
```
Expand Down
4 changes: 2 additions & 2 deletions docs/software-installation-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
conda create -y -n recOrder python=3.9
conda activate recOrder
```
Install `napari` and `recOrder-napari`:
Install `recOrder-napari`:
```
pip install "napari[all]" recOrder-napari
pip install recOrder-napari
```
Open `napari` with `recOrder-napari`:
```
Expand Down
5 changes: 3 additions & 2 deletions recOrder/acq/acq_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import glob

def generate_acq_settings(mm, channel_group, channels=None, zstart=None, zend=None, zstep=None,
save_dir=None, prefix=None, keep_shutter_open = False):
save_dir=None, prefix=None, keep_shutter_open_channels=False, keep_shutter_open_slices=False):
"""
This function generates a json file specific to the micromanager SequenceSettings.
It has default parameters for a multi-channels z-stack acquisition but does not yet
Expand Down Expand Up @@ -74,7 +74,8 @@ def generate_acq_settings(mm, channel_group, channels=None, zstart=None, zend=No
original_json['relativeZSlice'] = True
original_json['slicesFirst'] = True
original_json['timeFirst'] = False
original_json['keepShutterOpenSlices'] = keep_shutter_open
original_json['keepShutterOpenSlices'] = keep_shutter_open_slices
original_json['keepShutterOpenChannels'] = keep_shutter_open_channels
original_json['useAutofocus'] = False
original_json['saveMode'] = 'MULTIPAGE_TIFF'
original_json['save'] = True if save_dir else False
Expand Down
182 changes: 44 additions & 138 deletions recOrder/calib/Calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
set_lc_state, snap_and_average, snap_and_get_image, get_lc, define_config_state
from recOrder.calib.Optimization import BrentOptimizer, MinScalarOptimizer
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
from napari.utils.notifications import show_warning
from scipy.interpolate import interp1d
from scipy.stats import linregress
from scipy.optimize import least_squares
Expand Down Expand Up @@ -145,6 +146,11 @@ def __init__(self, mmc, mm, group='Channel', lc_control_mode='MM-Retardance', in
self.directory = None
self.inst_mat = None

# Shutter
self.shutter_device = self.mmc.getShutterDevice()
ziw-liu marked this conversation as resolved.
Show resolved Hide resolved
self._auto_shutter_state = None
self._shutter_state = None

def set_dacs(self, lca_dac, lcb_dac):
self.PROPERTIES['LCA-DAC'] = (f'TS_{lca_dac}', 'Volts')
self.PROPERTIES['LCB-DAC'] = (f'TS_{lcb_dac}', 'Volts')
Expand Down Expand Up @@ -639,13 +645,46 @@ def opt_I135(self, lca_bound, lcb_bound):
logging.info("--------done--------")
logging.debug("--------done--------")

def calc_blacklevel(self):
def open_shutter(self):
if self.shutter_device == '': # no shutter
input('Please manually open the shutter and press <Enter>')
else:
self.mmc.setShutterOpen(True)

def reset_shutter(self):
"""
Return autoshutter to its original state before closing

auto_shutter = self.mmc.getAutoShutter()
shutter = self.mmc.getShutterOpen()
Returns
-------

self.mmc.setAutoShutter(False)
self.mmc.setShutterOpen(False)
"""
if self.shutter_device == '': # no shutter
input('Please reset the shutter to its original state and press <Enter>')
talonchandler marked this conversation as resolved.
Show resolved Hide resolved
logging.info("This is the end of the command-line instructions. You can return to the napari window.")
else:
self.mmc.setAutoShutter(self._auto_shutter_state)
self.mmc.setShutterOpen(self._shutter_state)

def close_shutter_and_calc_blacklevel(self):
self._auto_shutter_state = self.mmc.getAutoShutter()
self._shutter_state = self.mmc.getShutterOpen()

if self.shutter_device == '': # no shutter
show_warning('No shutter found. Please follow the command-line instructions...')
shutter_warning_msg = """
recOrder could not find an automatic shutter configured through Micro-Manager.
>>> If you would like manually enter the black level, enter an integer or float and press <Enter>
>>> If you would like to estimate the black level, please close the shutter and press <Enter>
"""

in_string = input(shutter_warning_msg)
if in_string.isdigit(): # True if positive integer
self.I_Black = float(in_string)
return
else:
self.mmc.setAutoShutter(False)
self.mmc.setShutterOpen(False)

n_avg = 20
avgs = []
Expand All @@ -655,16 +694,8 @@ def calc_blacklevel(self):
avgs.append(mean)

blacklevel = np.mean(avgs)

self.mmc.setAutoShutter(auto_shutter)

if not auto_shutter:
self.mmc.setShutterOpen(shutter)

self.I_Black = blacklevel

return blacklevel

def get_full_roi(self):
# Get Image Parameters
self.mmc.snapImage()
Expand Down Expand Up @@ -718,131 +749,6 @@ def display_and_check_ROI(self, rect):
else:
raise ValueError('Did not understand your answer, please check spelling')

def run_5state_calibration(self, param):
"""
Param is a list or tuple of:
(swing, wavelength, lc_bounds, black level)
"""
self.swing = param[0]
self.wavelength = param[1]
self.meta_file = param[2]
use_full_FOV = param[3]

# Get Image Parameters
self.mmc.snapImage()
self.mmc.getImage()
self.height, self.width = self.mmc.getImageHeight(), self.mmc.getImageWidth()
self.ROI = (0, 0, self.width, self.height)

# Check if change of ROI is needed
if use_full_FOV is False:
rect = self.check_and_get_roi()
cont = self.display_and_check_ROI(rect)

if not cont:
print('\n---------Stopping Calibration---------\n')
return
else:
self.mmc.setROI(rect.x, rect.y, rect.width, rect.height)
self.ROI = (rect.x, rect.y, rect.width, rect.height)

# Calculate Blacklevel
logging.debug('Calculating Blacklevel ...')
self.I_Black = self.calc_blacklevel()
logging.debug(f'Blacklevel: {self.I_Black}\n')

# Set LC Wavelength:
if self.mode == 'MM-Retardance':
self.mmc.setProperty(LC_DEVICE_NAME, 'Wavelength', self.wavelength)

self.opt_Iext()
self.opt_I0()
self.opt_I45(0.05, 0.05)
self.opt_I90(0.05, 0.05)
self.opt_I135(0.05, 0.05)

# Calculate Extinction
self.extinction_ratio = self.calculate_extinction()

# Write Metadata
self.write_metadata()

# Return ROI to full FOV
if use_full_FOV is False:
self.mmc.clearROI()

logging.info("\n=======Finished Calibration=======\n")
logging.info(f"EXTINCTION = {self.extinction_ratio}")
logging.debug("\n=======Finished Calibration=======\n")
logging.debug(f"EXTINCTION = {self.extinction_ratio}")

def run_4state_calibration(self, param):
"""
Param is a list or tuple of:
(swing, wavelength, lc_bounds, black level, <mode>)
where <mode> is one of 'full','coarse','fine'
"""
self.swing = param[0]
self.wavelength = param[1]
self.meta_file = param[2]
use_full_FOV = param[3]

# Get Image Parameters
self.mmc.snapImage()
self.mmc.getImage()
self.height, self.width = self.mmc.getImageHeight(), self.mmc.getImageWidth()
self.ROI = (0, 0, self.width, self.height)

# Check if change of ROI is needed
if use_full_FOV is False:
rect = self.check_and_get_roi()
cont = self.display_and_check_ROI(rect)

if not cont:
print('\n---------Stopping Calibration---------\n')
return
else:
self.mmc.setROI(rect.x, rect.y, rect.width, rect.height)
self.ROI = (rect.x, rect.y, rect.width, rect.height)

# Calculate Blacklevel
print('Calculating Blacklevel ...')
self.I_Black = self.calc_blacklevel()
print(f'Blacklevel: {self.I_Black}\n')

# Set LC Wavelength:
if self.mode == 'MM-Retardance':
self.mmc.setProperty(LC_DEVICE_NAME, 'Wavelength', self.wavelength)

self.opt_Iext()
self.opt_I0()
self.opt_I60(0.05, 0.05)
self.opt_I120(0.05, 0.05)

# Calculate Extinction
self.extinction_ratio = self.calculate_extinction()

# Write Metadata
self.write_metadata()

# Return ROI to full FOV
if use_full_FOV is False:
self.mmc.clearROI()

print("\n=======Finished Calibration=======\n")
print(f"EXTINCTION = {self.extinction_ratio}")

def run_calibration(self, scheme, options):

if scheme == '5-State':
self.run_5state_calibration(options)

elif scheme == '4-State Extinction':
self.run_4state_calibration(options)

else:
raise ValueError('Please define the calibration scheme')

def calculate_extinction(self, swing, black_level, intensity_extinction, intensity_elliptical):
return np.round((1 / np.sin(np.pi * swing) ** 2) * \
(intensity_elliptical - black_level) / (intensity_extinction - black_level), 2)
Expand Down
3 changes: 2 additions & 1 deletion recOrder/plugin/widget/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2417,7 +2417,8 @@ def load_calibration(self):
self.swing = metadata.Swing

# Initialize calibration class
self.calib = QLIPP_Calibration(self.mmc, self.mm, group=self.config_group)
self.calib = QLIPP_Calibration(self.mmc, self.mm, group=self.config_group, lc_control_mode=self.calib_mode,
interp_method=self.interp_method, wavelength=self.wavelength)
self.calib.swing = self.swing
self.ui.le_swing.setText(str(self.swing))
self.calib.wavelength = self.wavelength
Expand Down
12 changes: 10 additions & 2 deletions recOrder/plugin/workers/acquisition_workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,8 @@ def work(self):
channel_group=self.channel_group,
channels=channels,
save_dir=self.snap_dir,
prefix=self.prefix)
prefix=self.prefix,
keep_shutter_open_channels=True)
self._check_abort()
# acquire images
stack = self._acquire()
Expand All @@ -872,9 +873,16 @@ def work(self):
zend=self.calib_window.z_end,
zstep=self.calib_window.z_step,
save_dir=self.snap_dir,
prefix=self.prefix)
prefix=self.prefix,
keep_shutter_open_channels=True,
keep_shutter_open_slices=True)

self._check_abort()

# set acquisition order to channel-first
self.settings['slicesFirst'] = False
self.settings['acqOrderMode'] = 0 # TIME_POS_SLICE_CHANNEL

# acquire images
stack = self._acquire()

Expand Down
15 changes: 12 additions & 3 deletions recOrder/plugin/workers/calibration_workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,20 @@ def work(self):

self._check_abort()

# Calculate Blacklevel
logging.info('Calculating Black Level ...')
logging.debug('Calculating Black Level ...')
self.calib.calc_blacklevel()
self.calib.close_shutter_and_calc_blacklevel()

# Calculate Blacklevel
logging.info(f'Black Level: {self.calib.I_Black:.0f}\n')
logging.debug(f'Black Level: {self.calib.I_Black:.0f}\n')

self._check_abort()
self.progress_update.emit((10, 'Calibrating Extinction State...'))

# Open shutter
self.calib.open_shutter()

# Set LC Wavelength:
self.calib.set_wavelength(int(self.calib_window.wavelength))
if self.calib_window.calib_mode == 'MM-Retardance':
Expand All @@ -90,6 +94,9 @@ def work(self):
# Optimize States
self._calibrate_4state() if self.calib_window.calib_scheme == '4-State' else self._calibrate_5state()

# Reset shutter autoshutter
self.calib.reset_shutter()

# Return ROI to full FOV
if self.calib_window.use_cropped_roi:
self.calib_window.mmc.clearROI()
Expand Down Expand Up @@ -356,11 +363,13 @@ def load_calibration(calib, metadata: MetadataReader):

# Calculate black level after loading these properties
calib.intensity_emitter = MockEmitter()
calib.calc_blacklevel()
calib.close_shutter_and_calc_blacklevel()
calib.open_shutter()
set_lc_state(calib.mmc, calib.group, 'State0')
calib.I_Ext = snap_and_average(calib.snap_manager)
set_lc_state(calib.mmc, calib.group, 'State1')
calib.I_Elliptical = snap_and_average(calib.snap_manager)
calib.reset_shutter()

yield str(calib.calculate_extinction(calib.swing, calib.I_Black, calib.I_Ext, calib.I_Elliptical))

Expand Down
Loading