feat: init
@@ -0,0 +1,60 @@
|
||||
.. _Quick_Start_Guide:
|
||||
|
||||
=================================
|
||||
Quick Start Guide
|
||||
=================================
|
||||
|
||||
This quick start guide covers how to install **navigate**, launch it in synthetic hardware mode to confirm it is working, and save an image to disk.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
1. *Install Miniconda.* Download and install Miniconda from the `official website <https://docs.conda.io/en/latest/miniconda.html>`_.
|
||||
|
||||
2. *Create and Activate a Conda Environment.* Launch a Miniconda Prompt (or a Terminal on MacOS) and enter the following.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(base) conda create -n navigate python=3.9.7
|
||||
(base) conda activate navigate
|
||||
|
||||
3. *Install* **navigate**.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) pip install navigate-micro
|
||||
|
||||
4. *Launch* **navigate** *in synthetic hardware mode.* This will allow you to test its functionality without actual hardware.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) navigate -sh
|
||||
|
||||
Saving a Z-Stack to Disk
|
||||
------------------------
|
||||
|
||||
To save an image to disk, follow these steps:
|
||||
|
||||
* Launch **navigate** in synthetic hardware mode as described above.
|
||||
* Next to the :guilabel:`Acquire` button on the upper left, make sure that the acquisition mode in the dropdown is set to "Continuous Scan".
|
||||
* Press :guilabel:`Acquire` (:kbd:`ctrl + enter`) and confirm that a synthetic noise image is displayed in the :guilabel:`Camera View` window.
|
||||
|
||||
.. Note::
|
||||
At least one channel must be selected (checkbox marked) in the :guilabel:`Channel Settings` window, and all of the parameters (e.g., :guilabel:`Power`) in that row must be populated.
|
||||
|
||||
* Select the :guilabel:`Channels` tab. In the :guilabel:`Stack Acquisition Settings` window in this tab, press the :guilabel:`Set Start Pos/Foc` button. This specifies the starting ``Z`` and ``F`` (e.g., Focus) positions for the stack acquisition.
|
||||
* Select the :guilabel:`Stage Control` tab (:kbd:`ctrl + 3`), move the ``Z`` stage to the desired position (e.g., ``100`` μm), go back to the :guilabel:`Channels` tab (:kbd:`ctrl + 1`), and press the :guilabel:`Set End Pos/Foc` button. This specifies the ending ``Z`` and ``F`` positions for the stack acquisition.
|
||||
* In the :guilabel:`Stack Acquisition Settings` frame, you can now adjust the step size, which determines the number of slices in a z-stack.
|
||||
* In the :guilabel:`Timepoint Settings` window, select :guilabel:`Save Data` by marking the checkbox. If the number of timepoints is set to ``1``, only a single stack will be acquired.
|
||||
* Next to the :guilabel:`Acquire` button on the upper left, change the acquisition mode in the dropdown to "Z-Stack", and press :guilabel:`Acquire` (:kbd:`ctrl + enter`).
|
||||
* A :guilabel:`File Saving Dialog` popup window will appear.
|
||||
* With the exception of :guilabel:`Notes`, all fields must be populated. Any spaces in the fields will be replaced with an underscore.
|
||||
* :guilabel:`Notes` is saved with the metadata, and can be useful for describing the experiment.
|
||||
* :guilabel:`Solvent` is useful for tissue clearing experiments.
|
||||
* :guilabel:`File Type` can be set to :guilabel:`.TIFF`, :guilabel:`OME-TIFF`, :guilabel:`H5`, or :guilabel:`N5`. The latter two options are pyramidal file formats that are best used for large datasets and are immediately compatible with `BigDataViewer <https://imagej.net/plugins/bdv/>`_ and `BigStitcher <https://imagej.net/plugins/bigstitcher/index>`_.
|
||||
* Press :guilabel:`Acquire` to begin the acquisition.
|
||||
* Once complete, the data can be opened using standard image processing software such as `Fiji <https://imagej.net/software/fiji/>`_.
|
||||
|
||||
.. image:: images/save_dialog.png
|
||||
:align: center
|
||||
:alt: File Saving Dialog
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,149 @@
|
||||
=====================
|
||||
Software Installation
|
||||
=====================
|
||||
|
||||
.. _software_installation:
|
||||
|
||||
Computer Specifications
|
||||
=======================
|
||||
|
||||
Below are the recommended specifications for **navigate**.
|
||||
|
||||
Operating System Compatibility
|
||||
------------------------------
|
||||
|
||||
.. important::
|
||||
**navigate** is developed for use on Windows-based systems. This is due to the compatibility of device drivers for various microscope hardware components, such as cameras, stages, and data acquisition cards, which are predominantly designed for the Windows environment. The software is only partially tested on MacOS and Linux systems. Users considering the use of **navigate** software on Linux should proceed with caution and be prepared for potential compatibility issues. For optimal performance and compatibility, it is strongly recommended to run **navigate** on a Windows machine.
|
||||
|
||||
.. _computer_considerations:
|
||||
|
||||
Computer Considerations
|
||||
-----------------------
|
||||
|
||||
**navigate** will run on a mid-range laptop with at least 8 GB of RAM and a processor with two cores. Most of its operations are undemanding. Saving data at a reasonable rate, however, will require an SSD. The hardware configuration for an example microscope control machine is shown below.
|
||||
|
||||
.. important::
|
||||
Scientific cameras are capable of rapidly generating large amounts of high-resolution data. As such, the read/write speed of the data storage device is critical for smooth operation of the software. For example, for a standard Hamamatsu camera with a 2048 x 2048 sensor, operating at 16-bit depth and 20 frames per second, the data save rate is approximately ~167 MB/s. While such capabilities are well within the capabilities of modern SSDs, they are beyond the capabilities of most HDDs. Therefore, it is recommended to use a fast SSD for data saving operations.
|
||||
|
||||
.. collapse:: Example Computer Configuration
|
||||
|
||||
- *Base Platform*
|
||||
- **Product Name**: `Colfax SX6300 Workstation <https://www.colfax-intl.com/workstations/sx6300>`_
|
||||
- **Colfax Part #**: CX-116263
|
||||
|
||||
- *Primary and Secondary CPU*
|
||||
- **CPU Model**: Intel Xeon Silver 4215R
|
||||
- **Configuration**: 8 Cores / 16 Threads
|
||||
- **Frequency**: 3.2 GHz
|
||||
- **Cache**: 11 MB
|
||||
- **TDP**: 130W
|
||||
- **Memory Support**: 2400 MHz
|
||||
|
||||
- *Memory*
|
||||
- **Type**: Registered ECC DDR4
|
||||
- **Speed**: 3200 MHz
|
||||
- **Configuration**: 16 GB per socket, 8 sockets per CPU
|
||||
- **Total RAM**: >64 GB (recommended)
|
||||
|
||||
- *Operating System Drive*:
|
||||
- **Type**: M.2 NVMe SSD
|
||||
- **Model**: Micron 7450 Max
|
||||
- **Capacity**: 800 GB
|
||||
- **Endurance**: 3 DWPD
|
||||
|
||||
- *Primary Data Drive*:
|
||||
- **Type**: NVMe SSD
|
||||
- **Model**: Samsung PM9A3
|
||||
- **Capacity**: 7.68 TB
|
||||
- **Interface**: U.2 Gen4
|
||||
|
||||
- *Secondary Data Drive*:
|
||||
- **Type**: SATA HDD
|
||||
- **Model**: Seagate Exos X20
|
||||
- **Capacity**: 20 TB
|
||||
- **Speed**: 7200 RPM
|
||||
- **Cache**: 256 MB
|
||||
- **Interface**: SATA 6.0 Gb/s
|
||||
|
||||
- *Video Card*
|
||||
- **Model**: PNY nVidia T1000
|
||||
- **Memory**: 4 GB
|
||||
- **Interface**: PCI Express
|
||||
|
||||
- *Network Interface*
|
||||
- **Model**: Intel X710-T2L RJ45 Copper
|
||||
- **Type**: Dual Port 10GbE
|
||||
- **Interface**: PCI-E x 8
|
||||
|
||||
.. note::
|
||||
The specifications listed are based on an example system configuration and can be adjusted based on specific needs and availability.
|
||||
|
||||
---------------------
|
||||
|
||||
Quick install
|
||||
=============
|
||||
|
||||
**Setup your Python Environment**
|
||||
|
||||
Head over to the `miniconda website <https://docs.conda.io/en/latest/miniconda.html>`_ and install the appropriate version based on your operating system.
|
||||
|
||||
.. tip::
|
||||
It is also handy to have the `conda cheatsheet <https://docs.conda.io/projects/conda/en/4.6.0/_downloads/52a95608c49671267e40c689e0bc00ca/conda-cheatsheet.pdf>`_ open when first using miniconda to get accustomed to the commands available.
|
||||
|
||||
* Windows: Use the Windows taskbar search to find ``Anaconda Prompt (Miniconda3)``. Given how frequently you will use this, we recommend pinning it to your taskbar. * Linux/Mac: Open a Terminal.
|
||||
|
||||
**Create a Python environment called navigate that uses Python version 3.9.7**
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(base) MyComputer ~ $ conda create -n navigate python=3.9.7
|
||||
|
||||
**Activate the navigate environment**
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(base) MyComputer ~ $ conda activate navigate
|
||||
|
||||
The active environment is shown in parentheses on the far-left. Originally, we were in the miniconda ``(base)`` environment. After activating the navigate environment, it should now show ``(navigate)``.
|
||||
|
||||
**Install navigate via pip**
|
||||
|
||||
To install the latest stable release of **navigate**, run the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) MyComputer ~ $ pip install navigate-micro
|
||||
|
||||
To install the bleeding edge version of **navigate**, run the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) MyComputer ~ $ pip install git+https://github.com/TheDeanLab/navigate.git
|
||||
|
||||
**Run navigate software**
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) MyComputer ~ $ navigate
|
||||
|
||||
.. note::
|
||||
If you are running the software on a computer that is not connected to microscope hardware, you can add the flag ``-sh`` (``--synthetic-hardware``) to launch the program:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
navigate -sh
|
||||
|
||||
Launching **navigate**
|
||||
======================
|
||||
|
||||
Open an ``Anaconda Prompt (Miniconda3)`` and enter the following.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(base) conda activate navigate
|
||||
(navigate) navigate
|
||||
|
||||
.. tip::
|
||||
If you are running Windows, you can create a desktop shortcut to **navigate** by right-clicking the Desktop, navigating to New and then Shortcut and entering ``%windir%\system32\cmd.exe "/c" C:\path\to\miniconda\Scripts\activate.bat navigate && navigate`` into the location text box.
|
||||
|
||||
This provides a convenient executable shortcut to launch the software, which is advantageous for users who are not comfortable with the command line.
|
||||
19
docs/source/01_getting_started/03_i_want_to/03_i_want_to.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
============
|
||||
I Want To...
|
||||
============
|
||||
|
||||
This section contains how-to documents organized into three levels of difficulty: beginner, intermediate, and advanced.
|
||||
|
||||
- The beginner how-to is intended for users who have little to no experience with computer programming and simply wish to acquire an image.
|
||||
- The intermediate how-to is intended for users who want to learn how to use the graphical user interface to create their own smart acquisition workflows.
|
||||
- The advanced how-to is intended for users who have experience with computer programming and wish to extend the functionality of **navigate** by adding new devices or writing their own acquisition features.
|
||||
|
||||
Through this tiered approach, we hope to provide a gentle introduction to the software while also reaching the maximum number of users.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
beginner
|
||||
intermediate
|
||||
advanced
|
||||
add_new_device
|
||||
101
docs/source/01_getting_started/03_i_want_to/add_new_device.rst
Normal file
@@ -0,0 +1,101 @@
|
||||
======================================
|
||||
Add a New Hardware Device (Advanced)
|
||||
======================================
|
||||
|
||||
**navigate** includes several standard hardware device types. These include:
|
||||
|
||||
- Cameras
|
||||
- Data Acquisition Cards
|
||||
- Filter Wheels
|
||||
- Galvo Scanners
|
||||
- Lasers
|
||||
- Deformable Mirrors
|
||||
- Remote Focusing Systems
|
||||
- Shutters
|
||||
- Stages
|
||||
- Zoom Devices
|
||||
|
||||
To add a new piece of hardware to one of these device types requires knowledge about the software's device abstraction layer. Here’s a detailed guide to help you integrate a new ``CustomStage`` device into **navigate**. The same principles work for other device types.
|
||||
|
||||
.. note::
|
||||
A strong knowledge of Python and object-oriented programming is required to integrate new hardware devices into **navigate**.
|
||||
|
||||
----------------
|
||||
|
||||
What is the Device Abstraction Layer?
|
||||
-------------------------------------
|
||||
|
||||
To ensure compatibility and extendability, **navigate** utilizes a device abstraction layer, which allows the same commands to be used across different hardware devices. For example, all stages in **navigate** are programmed to include the `stop()` command, which can be used to stop the stage's movement. When someone hits the :guilabel:`Stop Stage` button on the GUI, this action is relayed from the ``Controller`` to the ``Model`` and ultimately the ``CustomStage``, which communicates with the hardware in a device-specific format to stop the stage's movement.
|
||||
|
||||
--------------
|
||||
|
||||
Device Integration Approaches
|
||||
-----------------------------
|
||||
|
||||
There are two primary approaches to integrating new hardware into **navigate**:
|
||||
|
||||
- **Plugin**: If you want to continue to work with an up-to-date version of **navigate**, consider integrating your new hardware device as a plugin. This allows you to pull updates from the main repository without losing your custom hardware integration. It also allows you to integrate non-standard device types. Learn more about the plugin architecture :ref:`here <plugin>`, and how to write a custom plugin :ref:`here <advanced>`.
|
||||
- **Fork**: Alternatively, you can fork the **navigate** repository on GitHub and modify it directly. This is useful for custom, in-house developments. In select circumstances, you can contribute your changes back to the main repository through a pull request. Please contact the **navigate** development team for guidance on this approach.
|
||||
|
||||
--------------
|
||||
|
||||
Device Class Creation
|
||||
---------------------
|
||||
- New hardware devices must have a corresponding device class in navigate. To ensure consistency and reduce redundancy, each device must inherit the appropriate abstract base class. For instance, a ``CustomStage`` device would inherit from ``StageBase``.
|
||||
- Classes should follow CamelCase naming conventions and reflect the device they control (e.g., ``NewportStage`` for a stage from the manufacturer Newport).
|
||||
- Place the new device class within the appropriate device directory, `src/navigate/model/devices/`.
|
||||
- Place related API or hardware documentation within the appropriate manufacturer directory, typically under `src/navigate/model/devices/APIs/`.
|
||||
|
||||
--------------
|
||||
|
||||
Establish Device Communication
|
||||
------------------------------
|
||||
|
||||
- Each device requires a unique method to initialize a connection, which may involve APIs, serial communication, or other protocols. This method should be separate from the device class and is typically located at the beginning of the device file.
|
||||
- For example, a function named `build_custom_stage_connection()` would handle the connection setup for ``CustomStage`` class.
|
||||
- By separating the connection setup from the device class, you can easily interact with the hardware device outside of the larger **navigate** ecosystem, which can be useful for debugging and testing (e.g., within a Jupyter notebook).
|
||||
|
||||
--------------
|
||||
|
||||
Device Class Constructor
|
||||
------------------------
|
||||
|
||||
- The constructor for the device class (`__init__`) should accept parameters for the `microscope_name`, `device_connection`, `configuration_file`, and an optional `device_ID` (useful when multiple instances of the same device are used).
|
||||
- The constructor should load and enforce device settings from the `configuration_file`. For a new stage, this could be defining the axes mapping between **navigate** and the device, `{x:'X', y:'Y', z:'Z'}`.
|
||||
- Ensure the device class uses the connection established by your `build_custom_stage_connection` method.
|
||||
|
||||
--------------
|
||||
|
||||
Device Class Methods
|
||||
--------------------
|
||||
|
||||
- Implement any necessary device-specific methods within your device class.
|
||||
- Essential methods are inherited from the base class (e.g., ``StageBase`` for the ``CustomStage``), but you can override them or add new methods as needed for specialized functionality.
|
||||
|
||||
--------------
|
||||
|
||||
Startup and Configuration
|
||||
-------------------------
|
||||
|
||||
- Utilize or modify methods within `src/navigate/model/device_startup_functions` to configure and start your device upon system initialization.
|
||||
- These functions should handle configuration parsing and the device communication setup.
|
||||
- Implement a retry mechanism, such as `auto_redial`, to handle communication issues robustly, attempting multiple times before failing.
|
||||
|
||||
--------------
|
||||
|
||||
Integration with Microscope Object Configurations
|
||||
-------------------------------------------------
|
||||
|
||||
- Each microscope configuration in **navigate** that uses the new device should receive a reference to the established communication object.
|
||||
- This setup is defined in the *configuration.yaml* and handled within the `device_startup_functions`, ensuring each configuration has access to the necessary hardware.
|
||||
|
||||
--------------
|
||||
|
||||
Testing and Validation
|
||||
----------------------
|
||||
- Thoroughly test the new hardware integration to ensure it functions correctly within navigate, across all intended use cases and configurations.
|
||||
- The naming convention for test files is: `test_` + module name.
|
||||
- Device test files are located in `test\model\devices\`
|
||||
- Device testing utilizes the `pytest` package.
|
||||
|
||||
By following these steps, you can effectively integrate new hardware into the **navigate** platform, enhancing its functionality and ensuring it meets specific experimental needs.
|
||||
445
docs/source/01_getting_started/03_i_want_to/advanced.rst
Normal file
@@ -0,0 +1,445 @@
|
||||
.. _advanced:
|
||||
|
||||
=======================================
|
||||
Write a Custom Device Plugin (Advanced)
|
||||
=======================================
|
||||
|
||||
**navigate**'s :ref:`plugin system <plugin>` enables users to easily incorporate new devices and integrate new features and acquisition modes. In this guide, we will add a new device type, titled ``CustomDevice``, and a dedicated GUI window to control it. This hypothetical ``CustomDevice`` is capable of moving a certain distance, rotating a specified number of degrees, and applying a force to halt its movement.
|
||||
|
||||
**navigate** plugins are implemented using a Model-View-Controller architecture. The model contains the device-specific code, the view contains the GUI code, and the controller contains the code that communicates between the model and the view.
|
||||
|
||||
-----------------
|
||||
|
||||
Initial Steps
|
||||
-------------
|
||||
|
||||
To ease the addition of a new plugin, we have created a template plugin that can be used as a starting point.
|
||||
|
||||
* Go to `navigate-plugin-template <https://github.com/TheDeanLab/navigate-plugin-template>`_.
|
||||
* In the upper right, click "Use this template" and then "Create a new repository".
|
||||
* In this repository, rename the ``plugin_device`` folder to ``custom_device``.
|
||||
* Rename the file ``plugin_device.py`` to ``custom_device.py``.
|
||||
|
||||
-----------------
|
||||
|
||||
Model Code
|
||||
----------
|
||||
|
||||
Create a new custom device using the following code.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CustomDevice:
|
||||
""" A Custom Device Class """
|
||||
def __init__(self, device_connection, *args):
|
||||
""" Initialize the Custom Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
device_connection : object
|
||||
The device connection object
|
||||
args : list
|
||||
The arguments for the device
|
||||
"""
|
||||
self.device_connection = device_connection
|
||||
|
||||
def move(self, step_size=1.0):
|
||||
""" Move the Custom Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
step_size : float
|
||||
The step size of the movement. Default is 1.0 micron.
|
||||
"""
|
||||
print("*** Custom Device is moving by", step_size)
|
||||
|
||||
def stop(self):
|
||||
""" Stop the Custom Device """
|
||||
print("*** Stopping the Custom Device!")
|
||||
|
||||
def turn(self, angle=0.1):
|
||||
""" Turn the Custom Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
angle : float
|
||||
The angle of the rotation. Default is 0.1 degree.
|
||||
"""
|
||||
print("*** Custom Device is turning by", angle)
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
""" Return the commands for the Custom Device
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
The commands for the Custom Device
|
||||
"""
|
||||
return {
|
||||
"move_custom_device": lambda *args: self.move(args[0]),
|
||||
"stop_custom_device": lambda *args: self.stop(),
|
||||
"rotate_custom_device": lambda *args: self.rotate(args[0]),
|
||||
}
|
||||
|
||||
All devices should be accompanied by synthetic versions, which enables the software to run without the hardware connected. Thus, in a manner that is similar to the ``CustomDevice`` class, we edit the code in ``synthetic_device.py``, albeit without any calls to the device itself.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class SyntheticCustomDevice:
|
||||
""" A Synthetic Device Class """
|
||||
def __init__(self, device_connection, *args):
|
||||
""" Initialize the Synthetic Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
device_connection : object
|
||||
The device connection object
|
||||
args : list
|
||||
The arguments for the device
|
||||
"""
|
||||
pass
|
||||
|
||||
def move(self, step_size=1.0):
|
||||
""" Move the Synthetic Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
step_size : float
|
||||
The step size of the movement. Default is 1.0 micron.
|
||||
"""
|
||||
print("*** Synthetic Device receive command: move", step_size)
|
||||
|
||||
def stop(self):
|
||||
""" Stop the Synthetic Device """
|
||||
print("*** Synthetic Device receive command: stop")
|
||||
|
||||
def rotate(self, angle=0.1):
|
||||
""" Turn the Synthetic Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
angle : float
|
||||
The angle of the rotation. Default is 0.1 degree.
|
||||
"""
|
||||
print("*** Synthetic Device receive command: turn", angle)
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
""" Return the commands for the Synthetic Device.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
The commands for the Synthetic Device
|
||||
"""
|
||||
return {
|
||||
"move_custom_device": lambda *args: self.move(args[0]),
|
||||
"stop_custom_device": lambda *args: self.stop(),
|
||||
"rotate_custom_device": lambda *args: self.rotate(args[0]),
|
||||
}
|
||||
|
||||
Edit ``device_startup_functions.py`` to tell **navigate** how to connect to and start the ``CustomDevice``. This is the portion of the code that actually makes a connection to the hardware. ``load_device()`` should return an object that can control the hardware.
|
||||
|
||||
**navigate** establishes communication with each device independently, and passes the instance of that device to class that controls it (e.g., in this case, the `CustomDevice` class). This allows **navigate** to be initialized with multiple microscope :ref:`configurations <multiple_microscopes>`, some of which may share devices.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Third party imports
|
||||
|
||||
# Local application imports
|
||||
from navigate.tools.common_functions import load_module_from_file
|
||||
from navigate.model.device_startup_functions import (
|
||||
auto_redial,
|
||||
device_not_found,
|
||||
DummyDeviceConnection,
|
||||
)
|
||||
|
||||
DEVICE_TYPE_NAME = "custom_device"
|
||||
DEVICE_REF_LIST = ["type"]
|
||||
|
||||
def load_device(configuration, is_synthetic=False):
|
||||
""" Load the Custom Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
configuration : dict
|
||||
The configuration for the Custom Device
|
||||
is_synthetic : bool
|
||||
Whether the device is synthetic or not. Default is False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
object
|
||||
The Custom Device object
|
||||
"""
|
||||
return DummyDeviceConnection()
|
||||
|
||||
def start_device(microscope_name, device_connection, configuration, is_synthetic=False):
|
||||
""" Start the Custom Device
|
||||
|
||||
Parameters
|
||||
----------
|
||||
microscope_name : str
|
||||
The name of the microscope
|
||||
device_connection : object
|
||||
The device connection object
|
||||
configuration : dict
|
||||
The configuration for the Custom Device
|
||||
is_synthetic : bool
|
||||
Whether the device is synthetic or not. Default is False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
object
|
||||
The Custom Device object
|
||||
"""
|
||||
if is_synthetic:
|
||||
device_type = "synthetic"
|
||||
else:
|
||||
device_type = configuration["configuration"]["microscopes"][microscope_name][
|
||||
"custom_device"
|
||||
]["hardware"]["type"]
|
||||
|
||||
if device_type == "CustomDevice":
|
||||
custom_device = load_module_from_file(
|
||||
"custom_device",
|
||||
os.path.join(Path(__file__).resolve().parent, "custom_device.py"),
|
||||
)
|
||||
return custom_device.CustomDevice(
|
||||
microscope_name, device_connection, configuration
|
||||
)
|
||||
elif device_type == "synthetic":
|
||||
synthetic_device = load_module_from_file(
|
||||
"custom_synthetic_device",
|
||||
os.path.join(Path(__file__).resolve().parent, "synthetic_device.py"),
|
||||
)
|
||||
return synthetic_device.SyntheticDevice(
|
||||
microscope_name, device_connection, configuration
|
||||
)
|
||||
else:
|
||||
device_not_found(microscope_name, device_type)
|
||||
|
||||
-----------------
|
||||
|
||||
View Code
|
||||
---------
|
||||
To add a GUI control window, go to the ``view`` folder, rename ``plugin_name_frame.py`` to ``custom_device_frame.py``, and edit the code as follows.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Standard library imports
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
# Third party imports
|
||||
|
||||
# Local application imports
|
||||
from navigate.view.custom_widgets.LabelInputWidgetFactory import LabelInput
|
||||
|
||||
class CustomDeviceFrame(ttk.Frame):
|
||||
""" The Custom Device Frame """
|
||||
def __init__(self, root, *args, **kwargs):
|
||||
""" Initialize the Custom Device Frame
|
||||
|
||||
Parameters
|
||||
----------
|
||||
root : object
|
||||
The root Tk object
|
||||
args : list
|
||||
The arguments for the Custom Device Frame
|
||||
kwargs : dict
|
||||
The keyword arguments for the Custom Device Frame
|
||||
"""
|
||||
ttk.Frame.__init__(self, root, *args, **kwargs)
|
||||
|
||||
# Formatting
|
||||
tk.Grid.columnconfigure(self, "all", weight=1)
|
||||
tk.Grid.rowconfigure(self, "all", weight=1)
|
||||
|
||||
# Dictionary for widgets and buttons
|
||||
#: dict: Dictionary of the widgets in the frame
|
||||
self.inputs = {}
|
||||
|
||||
self.inputs["step_size"] = LabelInput(
|
||||
parent=self,
|
||||
label="Step Size",
|
||||
label_args={"padding": (0, 0, 10, 0)},
|
||||
input_class=ttk.Entry,
|
||||
input_var=tk.DoubleVar(),
|
||||
)
|
||||
self.inputs["step_size"].grid(row=0, column=0, sticky="N", padx=6)
|
||||
self.inputs["step_size"].label.grid(sticky="N")
|
||||
self.inputs["angle"] = LabelInput(
|
||||
parent=self,
|
||||
label="Angle",
|
||||
label_args={"padding": (0, 5, 25, 0)},
|
||||
input_class=ttk.Entry,
|
||||
input_var=tk.DoubleVar(),
|
||||
)
|
||||
self.inputs["angle"].grid(row=1, column=0, sticky="N", padx=6)
|
||||
self.inputs["angle"].label.grid(sticky="N")
|
||||
|
||||
self.buttons = {}
|
||||
self.buttons["move"] = ttk.Button(self, text="MOVE")
|
||||
self.buttons["rotate"] = ttk.Button(self, text="ROTATE")
|
||||
self.buttons["stop"] = ttk.Button(self, text="STOP")
|
||||
self.buttons["move"].grid(row=0, column=1, sticky="N", padx=6)
|
||||
self.buttons["rotate"].grid(row=1, column=1, sticky="N", padx=6)
|
||||
self.buttons["stop"].grid(row=2, column=1, sticky="N", padx=6)
|
||||
|
||||
# Getters
|
||||
def get_variables(self):
|
||||
variables = {}
|
||||
for key, widget in self.inputs.items():
|
||||
variables[key] = widget.get_variable()
|
||||
return variables
|
||||
|
||||
def get_widgets(self):
|
||||
return self.inputs
|
||||
|
||||
.. tip::
|
||||
|
||||
**navigate** comes equipped with a large number of validated widgets, which prevent users from entering invalid values that can crash the program or result in undesirable outcomes. It is highly recommended that you use these, which include the following:
|
||||
|
||||
* The ``LabelInput`` widget conveniently combines a label and an input widget into a single object. It is used to create the ``step_size`` and ``angle`` widgets in the code above.
|
||||
* The ``LabelInput`` widget can accept multiple types of ``input_class`` objects, which can include standard tkinter widgets (e.g., spinbox, entry, etc.) or custom widgets. In this example, we use the ``ttk.Entry`` widget.
|
||||
* Other examples of validated widgets include a ``ValidatedSpinbox``, ``ValidatedEntry``, ``ValidatedCombobox``, and ``ValidatedMixin``.
|
||||
* Please see the :any:`navigate.view.custom_widgets` module for more details.
|
||||
|
||||
-----------------
|
||||
|
||||
Controller Code
|
||||
----------------
|
||||
Now, let's build a controller. Open the ``controller`` folder, rename ``plugin_name_controller.py`` to ``custom_device_controller.py``, and edit the code as follows.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Standard library imports
|
||||
import tkinter as tk
|
||||
|
||||
# Third party imports
|
||||
|
||||
# Local application imports
|
||||
from navigate.controller.sub_controllers.gui_controller import GUIController
|
||||
|
||||
class CustomDeviceController(GUIController):
|
||||
""" The Custom Device Controller """
|
||||
def __init__(self, view, parent_controller=None):
|
||||
""" Initialize the Custom Device Controller
|
||||
|
||||
Parameters
|
||||
----------
|
||||
view : object
|
||||
The Custom Device View object
|
||||
parent_controller : object
|
||||
The parent (e.g., main) controller object
|
||||
"""
|
||||
super().__init__(view, parent_controller)
|
||||
|
||||
# Get the variables and buttons from the view
|
||||
self.variables = self.view.get_variables()
|
||||
self.buttons = self.view.buttons
|
||||
|
||||
# Set the trace commands for the variables associated with the widgets in the View.
|
||||
self.buttons["move"].configure(command=self.move_device)
|
||||
self.buttons["rotate"].configure(command=self.rotate_device)
|
||||
self.buttons["stop"].configure(command=self.stop_device)
|
||||
|
||||
def move_device(self, *args):
|
||||
""" Listen to the move button and move the Custom Device upon clicking.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args : list
|
||||
The arguments for the move_device function. Should be included as the tkinter event
|
||||
is passed to this function.
|
||||
"""
|
||||
self.parent_controller.execute(
|
||||
"move_custom_device", self.variables["step_size"].get()
|
||||
)
|
||||
|
||||
def rotate_device(self, *args):
|
||||
""" Listen to the rotate button and rotate the Custom Device upon clicking.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args : list
|
||||
The arguments for the rotate_device function. Should be included as the tkinter event
|
||||
is passed to this function.
|
||||
"""
|
||||
self.parent_controller.execute(
|
||||
"rotate_custom_device", self.variables["angle"].get()
|
||||
)
|
||||
|
||||
def stop_device(self, *args):
|
||||
""" Listen to the stop button and stop the Custom Device upon clicking.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args : list
|
||||
The arguments for the stop_device function. Should be included as the tkinter event
|
||||
is passed to this function.
|
||||
"""
|
||||
self.parent_controller.execute("stop_custom_device")
|
||||
|
||||
In each case above, the sub-controller for the ``custom-device`` establishes what actions should take place once a button in the view is clicked. In this case, the methods ``move_device``, ``rotate_device``, and ``stop_device``. This triggers a sequence of events:
|
||||
|
||||
* The sub-controller passes the command to the parent controller, which is the main controller for the software.
|
||||
* The parent controller passes the command to the model, which is operating in its own sub-process, using an event queue. This eliminates the need for the controller to know anything about the model and prevents race conditions.
|
||||
* The model then executes command, and any updates to the controller from the model are relayed using another event queue.
|
||||
|
||||
-----------------
|
||||
|
||||
Plugin Configuration
|
||||
--------------------
|
||||
Next, update the ``plugin_config.yml`` file as follows:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
name: Custom Device
|
||||
view: Popup
|
||||
|
||||
Remove the folder ``./model/features``, the file ``feature_list.py``, and the file ``plugin_acquisition_mode.py``. The plugin folder structure is as follows.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
custom_device/
|
||||
├── controller/
|
||||
│ └── custom_device_controller.py
|
||||
|
|
||||
├── model/
|
||||
| └── devices/
|
||||
│ └── custom_device/
|
||||
│ ├── device_startup_functions.py
|
||||
│ ├── custom_device.py
|
||||
│ └── synthetic_device.py
|
||||
├── view/
|
||||
| └── custom_device_frame.py
|
||||
│
|
||||
└── plugin_config.yml
|
||||
|
||||
Install the plugin using one of two methods:
|
||||
* Install a plugin by putting the whole plugin folder directly into ``navigate/plugins/``. In this example, put ``custom_device`` folder and all its contents into ``navigate/plugins``.
|
||||
* Alternatively, install this plugin through the menu :menuselection:`Plugins --> Install Plugin` by selecting the plugin folder.
|
||||
|
||||
The plugin is ready to use. For this plugin, you can now specify a CustomDevice in the ``configuration.yaml`` as follows.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
microscopes:
|
||||
microscope_1:
|
||||
daq:
|
||||
hardware:
|
||||
name: daq
|
||||
type: NI
|
||||
...
|
||||
custom_device:
|
||||
hardware:
|
||||
type: CustomDevice
|
||||
|
||||
The ``custom_device`` will be loaded when **navigate** is launched, and it can be controlled through the GUI.
|
||||
207
docs/source/01_getting_started/03_i_want_to/beginner.rst
Normal file
@@ -0,0 +1,207 @@
|
||||
.. _beginner:
|
||||
|
||||
===========================
|
||||
Acquire an Image (Beginner)
|
||||
===========================
|
||||
|
||||
This guide will describe how to acquire a single image and a z-stack using the **navigate** software package.
|
||||
|
||||
Launching the Software Package
|
||||
==============================
|
||||
|
||||
Open Anaconda Prompt
|
||||
--------------------
|
||||
|
||||
To start, you need to open the Anaconda Prompt. Follow these steps:
|
||||
1. On Windows, click on the Start menu.
|
||||
2. Type ``Anaconda Prompt`` into the search bar.
|
||||
3. Click on the Anaconda Prompt application to open it.
|
||||
|
||||
.. note:: Ensure that Anaconda and **navigate** are already installed on your system. If not, please refer to our :ref:`Quick_Start_Guide` for more information.
|
||||
|
||||
Activate Conda Environment
|
||||
--------------------------
|
||||
|
||||
Once the Anaconda Prompt is open, activate the desired conda environment. By default, the command prompt will open the base environment (as shown in parentheses). To activate **navigate** environment, type the following command into the Anaconda command window and press :kbd:`Enter`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(base) conda activate navigate
|
||||
|
||||
Launch the Software Package
|
||||
---------------------------
|
||||
|
||||
After activating the environment, **navigate** should now be shown in parentheses. After you have already :ref:`configured <setup_microscope>` **navigate**, you can launch it by typing the following command into the Anaconda command window:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) navigate
|
||||
|
||||
The **navigate** software package will launch and the main window will appear.
|
||||
|
||||
.. image:: images/beginner/open-navigate.png
|
||||
:alt: Opening **navigate**.
|
||||
|
||||
Configure the Channel Settings
|
||||
==============================
|
||||
|
||||
* Select the :guilabel:`Channels` tab, which is located on the upper left of the main window.
|
||||
* Under the :guilabel:`Channel Settings` section, select the number of channels needed for imaging. For each channel selected, you will need to configure the acquisition settings:
|
||||
|
||||
.. image:: images/beginner/channel-selector.png
|
||||
:alt: Channel settings in the **navigate** software package.
|
||||
|
||||
* Select the appropriate :guilabel:`Laser` from the dropdown menu.
|
||||
* Select the appropriate :guilabel:`Power` for the laser.
|
||||
* Select the appropriate emission :guilabel:`Filter` from the dropdown menu.
|
||||
|
||||
.. image:: images/beginner/channel-selector-filter.png
|
||||
:alt: Changing the emission filter in **navigate**.
|
||||
|
||||
* Specify the camera :guilabel:`Exp. Time (ms)`. A good default value is ``100`` or ``200`` ms.
|
||||
* Specify the :guilabel:`Interval` to be ``1.0``. While this feature is not currently implemented, future releases will allow users to image different channels at different time intervals.
|
||||
* Specify the :guilabel:`Defocus` to be ``0``. This feature allows you to adjust for chromatic aberrations that result in focal shifts between each imaging channel.
|
||||
|
||||
Configure the Camera Settings
|
||||
=============================
|
||||
|
||||
* Select the :guilabel:`Camera Settings` tab.
|
||||
* For standard imaging applications, select :guilabel:`Normal` in the :guilabel:`Sensor Modes` dropdown menu within the :guilabel:`Camera Modes` section.
|
||||
* If you are using the rolling shutter, select :guilabel:`Light-Sheet` and specify its :guilabel:`Readout Direction` and :guilabel:`Number of Pixels`.
|
||||
|
||||
.. note:: For more information on how to configure the rolling shutter for ASLM operation, please refer to :ref:`ASLM <setup_aslm>`.
|
||||
|
||||
.. image:: images/beginner/sensor-mode.png
|
||||
:alt: Changing the camera sensor mode in **navigate**.
|
||||
|
||||
* Choose the size of your camera's field of view.
|
||||
* Specify the :guilabel:`Region of Interest Settings` by entering the appropriate :guilabel:`Number of Pixels` for both the :guilabel:`Width` and :guilabel:`Height` values. Alternatively, one can select from one of several default values in the :guilabel:`Default FOVs` section.
|
||||
|
||||
.. note:: The :guilabel:`FOV Dimensions (microns)` is automatically calculated based on the :guilabel:`Number of Pixels` and the `pixel_size` as specified in the `zoom` section of your ``configuration.yaml`` file.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zoom:
|
||||
pixel_size:
|
||||
20x: 0.325 # magnification, and pixel size in microns
|
||||
|
||||
.. image:: images/beginner/ROI-definition.png
|
||||
:alt: Changing the camera region of interest in **navigate**.
|
||||
|
||||
.. note:: If multiple channels are selected, each channel will be acquired with the same camera :guilabel:`Sensor Mode`, :guilabel:`Readout Direction`, and :guilabel:`Region of Interest Settings`.
|
||||
|
||||
Acquire in a Continuous Scan Mode
|
||||
=================================
|
||||
|
||||
* Select "Continuous Scan" in the dropdown next to the :guilabel:`Acquire` button in the :ref:`acquisition bar <ui_acquisition_bar>`.
|
||||
|
||||
.. image:: images/beginner/continuous-scan-dropdown.png
|
||||
:alt: Selecting the continuous scan mode in **navigate**.
|
||||
|
||||
* Press :guilabel:`Acquire`. This will launch a live acquisition mode.
|
||||
|
||||
.. note:: If multiple channels are selected, each channel will be imaged sequentially. The order of imaging is determined by the order of the channels in the :guilabel:`Channel Settings` section of the :guilabel:`Channels` tab, and will proceed from the top to the bottom of this channel list.
|
||||
|
||||
.. image:: images/beginner/continuous-scan-acquire.png
|
||||
:alt: Launching the continuous scan mode in **navigate**.
|
||||
|
||||
* Move the stage to identify the location of the sample.
|
||||
* Select the :guilabel:`Stage Control` tab, and use the graphical user interface to move the stage. This includes buttons for moving the stage in ``X``, ``Y``, ``Z``, ``F``, and ``Theta`` directions.
|
||||
* The step size for each axis can be adjusted with the spinbox next to each button.
|
||||
* For stages loaded in a synthetic mode, buttons will be disabled.
|
||||
* Absolute positions can be entered in the text boxes next to each button.
|
||||
* Check :ref:`configuration settings <configuration_file>` for more information.
|
||||
* Alternatively, if available, use the manufacturer-provided joystick to position the sample.
|
||||
|
||||
.. note:: The axes for a light-sheet microscope vary in the literature. Here, we define the ``Y`` axis as the direction of the light-sheet propagation, the ``Z`` axis as the direction of the detection objective, and the ``X`` axis as the direction perpendicular to the light-sheet and detection objective axes. The ``F`` axis typically controls the position of the detection objective along the detection axis. The ``Theta`` axis typically controls the rotation of the sample.
|
||||
|
||||
.. warning:: One should always be careful when moving the stage. If the stage is moved too quickly, the sample and/or microscope may be damaged. We strongly recommend that you implement stage limits in your configuration file. Please refer to the :ref:`configuration settings <configuration_file>` for more information.
|
||||
|
||||
.. image:: images/beginner/stage-movement-panel.png
|
||||
:alt: Moving the stage in **navigate**.
|
||||
|
||||
* Press the :guilabel:`Stop` button in the acquisition bar to stop acquisition.
|
||||
|
||||
.. image:: images/beginner/stop-acquisition.png
|
||||
:alt: Stopping the continuous scan mode in **navigate**.
|
||||
|
||||
Acquiring a Single Image
|
||||
=========================
|
||||
|
||||
* Check the :guilabel:`Save Data` box in the :guilabel:`Timepoint Settings` section under the :guilabel:`Channels` tab to save the acquired images. Check this box before acquiring data.
|
||||
|
||||
.. image:: images/beginner/save-data.png
|
||||
:alt: Saving data in **navigate**.
|
||||
|
||||
* Select :guilabel:`Single Acquisition` from the dropdown next to the :guilabel:`Acquire` button.
|
||||
|
||||
.. image:: images/beginner/single-acquisition-dropdown.png
|
||||
:alt: Selecting the single acquisition mode in **navigate**.
|
||||
|
||||
* Press :guilabel:`Acquire` to open the :guilabel:`File Saving Dialog` interface. Enter the sample parameters, notes, location to save file, and filetype in the :guilabel:`File Saving Dialog` that pops up.
|
||||
|
||||
.. image:: images/beginner/save-dialog-box.png
|
||||
:alt: Saving data in **navigate**.
|
||||
|
||||
* Press :guilabel:`Acquire Data` to initiate acquisition. Acquisition will automatically stop once the image is acquired.
|
||||
|
||||
.. note:: Each acquisition will be saved in a separate folder (e.g., ``Cell01``, ``Cell02``, ...) within the directory specified in the :guilabel:`File Saving Dialog` interface. Data will not be overwritten between acquisitions.
|
||||
|
||||
.. image:: images/beginner/save-dialog-box-acquire.png
|
||||
:alt: Saving data in **navigate**.
|
||||
|
||||
.. _i_want_to_z_stack:
|
||||
|
||||
Acquiring a Z-Stack
|
||||
===================
|
||||
|
||||
* Using the :guilabel:`Stage Control`, go to the desired z-position in the sample. Make sure that the sample is in focus. To use the autofocus feature, please refer to the :ref:`Autofocus Settings <ui_autofocus>`.
|
||||
|
||||
.. image:: images/beginner/stage-control-start-pos-zstack.png
|
||||
:alt: Adjusting the stage position in **navigate**.
|
||||
|
||||
* Under the :guilabel:`Channels` tab, in :guilabel:`Stack Acquisition Settings (μm)` press :guilabel:`Set Start Pos`.
|
||||
|
||||
.. image:: images/beginner/press-start-pos.png
|
||||
:alt: Adjusting the stage position in **navigate**.
|
||||
|
||||
* Using the :guilabel:`Stage Control`, go to a different z-position within the sample. Again, make sure that the sample is in focus.
|
||||
|
||||
.. image:: images/beginner/stage-control-end-pos-zstack.png
|
||||
:alt: Adjusting the stage position in **navigate**.
|
||||
|
||||
* Under the :guilabel:`Channels` tab, in :guilabel:`Stack Acquisition Settings (μm)` press :guilabel:`Set End Pos`.
|
||||
|
||||
.. image:: images/beginner/press-end-pos.png
|
||||
:alt: Adjusting the stage position in **navigate**.
|
||||
|
||||
.. note:: If there is a shift in ``F`` between the start and stop positions, the ``F`` axis will be ramped synchronously with ``Z`` to maintain focus. Check :ref:`configuration settings <configuration_file>` for more information to determine if focus is enabled in hardware. Refer to :ref:`Imaging on a mesoSPIM BT <acquire_mesospimbt>` section for an example of how to acquire a z-stack with a focus ramp.
|
||||
|
||||
* Type the desired step size in microns in the :guilabel:`Step Size` dialog box in :guilabel:`Stack Acquisition Settings (μm)`.
|
||||
|
||||
.. note:: The minimum step size, and increment between steps, are graphical user interface defaults that are specified in the ``configuration.yaml`` file. More information can :ref:`configuration settings <configuration_file>`.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
gui:
|
||||
stack_acquisition:
|
||||
step_size:
|
||||
min: 0.100
|
||||
max: 1000
|
||||
step: 0.1
|
||||
|
||||
.. image:: images/beginner/define-step-size.png
|
||||
|
||||
* If using multiple channels for imaging, select either :guilabel:`Per Z` or :guilabel:`Per Stack` under :guilabel:`Laser Cycling Settings` in the :guilabel:`Stack Acquisition Settings (μm)` section under the :guilabel:`Channels` tab.
|
||||
|
||||
* :guilabel:`Per Z` acquires all channels before moving the stage to a new position.
|
||||
* :guilabel:`Per Stack` acquires all images in a stack acquisition for a single channel before moving the stage back to the start position and restarting acquisition for the subsequent channel until all channels are imaged.
|
||||
|
||||
.. image:: images/beginner/laser-cycling-settings.png
|
||||
|
||||
* Select :guilabel:`Z-Stack` from the dropdown next to the :guilabel:`Acquire` button. Press :guilabel:`Acquire`.
|
||||
|
||||
.. image:: images/beginner/z-stack-acquisition.png
|
||||
|
||||
* Enter the sample parameters, notes, location to save file, and filetype in the :guilabel:`File Saving Dialog` that pops up.
|
||||
* Press :guilabel:`Acquire Data` to initiate acquisition. Acquisition will automatically stop once the image series is acquired.
|
||||
|
After Width: | Height: | Size: 3.3 MiB |
|
After Width: | Height: | Size: 361 KiB |
|
After Width: | Height: | Size: 357 KiB |
|
After Width: | Height: | Size: 349 KiB |
|
After Width: | Height: | Size: 347 KiB |
|
After Width: | Height: | Size: 335 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 353 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.1 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 326 KiB |
|
After Width: | Height: | Size: 336 KiB |
|
After Width: | Height: | Size: 336 KiB |
|
After Width: | Height: | Size: 327 KiB |
|
After Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 342 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 294 KiB |
92
docs/source/01_getting_started/03_i_want_to/intermediate.rst
Normal file
@@ -0,0 +1,92 @@
|
||||
.. _intermediate:
|
||||
|
||||
================================================
|
||||
Write A Smart Acquisition Routine (Intermediate)
|
||||
================================================
|
||||
|
||||
**navigate**'s :ref:`feature container <user_guide_features>` enables us to write acquisition routines on the fly by chaining existing :ref:`features <user_guide_features>` into lists. Please see :ref:`Currently Implemented Features <currently_implemented_features>` for a complete list of features. Users can build additional features within :ref:`plugins <plugin>`.
|
||||
|
||||
In this guide, we will use existing features to write a routine that scans through an imaging chamber and takes z-stacks only where it finds the sample.
|
||||
|
||||
Suppose there are two positions listed in the :ref:`multiposition table <ui_multiposition>`, one containing tissue and one empty, as shown below.
|
||||
|
||||
.. image:: images/intermediate/multiposition_tissue.png
|
||||
|
||||
.. image:: images/intermediate/multiposition_empty.png
|
||||
|
||||
We will build a feature that scans both positions, but only takes a z-stack at the one containing tissue. To access the GUI feature list editor, navigate to :menuselection:`Features --> Add Customized Feature List`. A window titled "Add New Feature List" will pop up. Enter ``TestFeature`` in the text box at the top of the popup. Enter
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[{"name": PrepareNextChannel}]
|
||||
|
||||
in the text box at the bottom of this popup and press :guilabel:`Preview`. The window should appear as below.
|
||||
|
||||
.. image:: images/feature_gui_1.png
|
||||
|
||||
The square brackets ``[]`` create a sequence of events to run in the feature container. The ``{}`` braces contain features. In this case, we have a single feature, ``PrepareNextChannel``, which will set up the next color channel for acquisition. The complete sequence (as it stands) will take an image in the first :ref:`selected color channel <ui_channel_settings>`.
|
||||
|
||||
We can build much of the rest of our desired acquisition in the GUI. Right-click on the ``PrepareNextChannel`` tile to reveal the editing menu.
|
||||
|
||||
.. image:: images/feature_gui_2.png
|
||||
|
||||
Select :guilabel:`Insert After`. A second copy of ``PrepareNextChannel`` will appear in the feature list.
|
||||
|
||||
.. image:: images/feature_gui_3.png
|
||||
|
||||
Left-click on this second tile to reveal a popup that allows us to select which feature we want to use in this tile.
|
||||
|
||||
.. image:: images/feature_gui_4.png
|
||||
|
||||
Select ``MoveToNextPositionInMultiPositionTable`` and then close the popup. Your feature list editing window should now appear as below.
|
||||
|
||||
.. image:: images/feature_gui_5.png
|
||||
|
||||
Our feature now takes one image of the first selected color channel at each position in the multiposition table.
|
||||
|
||||
Now, right-click ``MoveToNextPositionInMultiPositionTable`` and press :guilabel:`Insert After`. Click the new tile and change it to ``DetectTissueInStackAndReturn``. There are three options associated with this feature.
|
||||
|
||||
.. image:: images/feature_gui_6.png
|
||||
|
||||
* :guilabel:`planes` indicates how many planes of the z-stack this feature should check for tissue.
|
||||
* :guilabel:`percentage` indicates what percent of the total image must contain tissue for this feature to return ``true`` that tissue was detected. 1 indicates that the entire image contains tissue, 0.5 indicates that half of the image contains tissue, and so on.
|
||||
* :guilabel:`detect_func` is one of the tissue detection functions in :doc:`remove_empty_tiles <../../../05_reference/_autosummary/navigate.model.features.remove_empty_tiles>`. If this is set to ``None``, it defaults to ``detect_tissue()``, which states that tissue is present if signal is above the Otsu threshold of the stack of images acquired.
|
||||
|
||||
In this example, if any plane meets the desired threshold, the feature will return ``true`` and it will be acquired. If no plane meets the desired threshold, the feature will return ``false``.
|
||||
|
||||
Now, right-click ``DetectTissueInStackAndReturn`` and press :guilabel:`Insert After`. Click the new tile and change it to ``LoopByCount``.
|
||||
|
||||
.. image:: images/feature_gui_7.png
|
||||
|
||||
We want to iterate over all of the positions in the multi-position table, so we will set ``steps`` to ``experiment.MicroscopeState.multiposition_count``.
|
||||
|
||||
Notice that the acquisition protocol does not appear to loop, but rather still moves in a sequence. This is because all of the tiles are still in the sequence brackets ``[]``. We can now enclose the section of the protocol we want to loop in parentheses ``()`` and press :guilabel:`Preview` to see the update.
|
||||
|
||||
.. image:: images/feature_gui_8.png
|
||||
|
||||
Now, we set up one color channel to image (``PrepareNextChannel``), and then within this channel visit every position in the multiposition table, and detect if there is tissue. However, we do not yet make any decisions of what to do if tissue is found. To do this, we will convert ``DetectTissueInStackAndReturn`` into a decision node.
|
||||
|
||||
To do this, we add ``true`` and ``false`` options within the feature braces:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{"name": DetectTissueInStackAndReturn,
|
||||
"args": (1, 0.5, None),
|
||||
"true": [{"name": ZStackAcquisition,"args": (False,False,"z-stack",),}],
|
||||
"false": "continue",}
|
||||
|
||||
Our ``true`` argument tells the software what to do if tissue is detected. In this case, we take a z-stack at the positions where tissue is found. The ``false`` argument tells the software how to proceed if no tissue is found. In this case, the ``continue`` option tells the software to keep moving through the loop to the next position in the multi-position table. Press :guilabel:`Preview` to see the update.
|
||||
|
||||
.. image:: images/feature_gui_9.png
|
||||
|
||||
``DetectTissueInStackAndReturn`` now has a red border, indicating it is a decision node. Click on it to access the decision node GUI.
|
||||
|
||||
.. image:: images/feature_gui_10.png
|
||||
|
||||
This contains the same settings for ``DetectTissueInStackAndReturn`` we saw before, but now also features GUI editing windows for the results of ``true`` and ``false`` decisions arising from this node.
|
||||
|
||||
Close the node window and press :guilabel:`Add` in the "Add New Feature List" window. This feature is now available under :menuselection:`Features --> TestFeature` and can be run in "Customized" :ref:`acquisition mode <ui_acquisition_bar>`.
|
||||
|
||||
Select "Customized" acquisition mode, select :menuselection:`Features --> TestFeature`, and press :guilabel:`Acquire`. For the positions shown at the start of this guide, the software will go to the first position in the multi-position table, decide there is tissue present, and take a z-stack. It will then go to the second position in the multi-position table, find there is no tissue, and decide not to take a z-stack. It will then exit the loop as no more positions are available in the multi-position table.
|
||||
|
||||
Now you can use this feature or build another smart acquisition routine suited to your microscope's needs.
|
||||