├── .github
└── workflows
│ ├── docs.yml
│ └── python-package.yml
├── .gitignore
├── CHANGES.md
├── INSTALLATION.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── example
├── autolamella.py
├── example.ipynb
├── example.py
├── example_imaging.py
├── example_milling.py
├── example_movement.py
├── example_notebook.ipynb
├── lithography.py
├── profile.npy
├── protocol_autolamella.yaml
├── protocol_lithography.yaml
├── protocol_slice_and_view.yaml
└── slice_and_view.py
├── external
└── application_files
│ ├── autolamella.xml
│ └── cryo_Pt_dep.xml
├── fibsem
├── __init__.py
├── acquire.py
├── alignment.py
├── calibration.py
├── chat
│ ├── .gitignore
│ ├── main.py
│ └── requirements.txt
├── config.py
├── config
│ ├── deposition.dbp
│ ├── microscope-configuration.yaml
│ ├── odemis-configuration.yaml
│ ├── positions.yaml
│ ├── protocol.yaml
│ ├── tescan-configuration.yaml
│ ├── tescan_manipulator.yaml
│ ├── tfs-aquilos2-configuration.yaml
│ ├── tfs-arctis-configuration.yaml
│ └── tfs-hydra-configuration.yaml
├── configuration.py
├── constants.py
├── conversions.py
├── db
│ ├── app.py
│ ├── config.yaml
│ ├── notebook.ipynb
│ ├── util.py
│ ├── v2
│ │ ├── app.py
│ │ ├── models.py
│ │ ├── notebook.ipynb
│ │ └── util.py
│ └── v3
│ │ ├── app.py
│ │ ├── models.py
│ │ ├── notebook.ipynb
│ │ └── util.py
├── detection
│ ├── __init__.py
│ ├── config-autolamella-liftout-hf-mega.yaml
│ ├── config-autolamella-liftout.yaml
│ ├── config-autolamella-serial-liftout-hf-mega.yaml
│ ├── config-autolamella-serial-liftout.yaml
│ ├── config-autolamella-waffle-v2-hf-mega.yaml
│ ├── config-autolamella-waffle-v2.yaml
│ ├── config-autolamella-waffle.yml
│ ├── config-autoliftout-dm-embryo.yml
│ ├── detection.py
│ ├── evaluation.py
│ ├── run_evaluation.py
│ ├── test_image.tif
│ ├── test_image_2.tif
│ ├── test_images
│ │ ├── serial
│ │ │ ├── serial_liftout_mask_00.tif
│ │ │ ├── serial_liftout_mask_01.tif
│ │ │ ├── serial_liftout_mask_02.tif
│ │ │ ├── serial_liftout_mask_03.tif
│ │ │ ├── serial_liftout_mask_04.tif
│ │ │ ├── serial_liftout_mask_05.tif
│ │ │ ├── serial_liftout_mask_06.tif
│ │ │ └── serial_liftout_mask_07.tif
│ │ ├── test_needle_mask.tif
│ │ ├── test_needle_mask2.tif
│ │ └── test_needle_mask3.tif
│ └── utils.py
├── gis.py
├── imaging
│ ├── .gitkeep
│ ├── __init__.py
│ ├── autogamma.py
│ ├── masks.py
│ ├── spot.py
│ ├── tiled.py
│ └── utils.py
├── microscope.py
├── microscopes
│ ├── notebook.ipynb
│ ├── odemis_microscope.py
│ ├── simulator.py
│ └── tescan.py
├── milling
│ ├── __init__.py
│ ├── base.py
│ ├── config.py
│ ├── core.py
│ ├── milling_notebook.ipynb
│ ├── patterning
│ │ ├── patterns2.py
│ │ └── plotting.py
│ └── strategy
│ │ ├── __init__.py
│ │ ├── overtilt.py
│ │ └── standard.py
├── movement.py
├── segmentation
│ ├── README.md
│ ├── __init__.py
│ ├── _nnunet.py
│ ├── adaptive_model.py
│ ├── augmentation.ipynb
│ ├── config-autolamella-mega-v4-xl.yml
│ ├── config-autolamella-mega-v4.yml
│ ├── config-autolamella-mega-v5.yml
│ ├── config-autolamella-waffle4.yml
│ ├── config.py
│ ├── config.yml
│ ├── dataset.py
│ ├── docs
│ │ ├── example_napari.png
│ │ └── imgs
│ │ │ ├── combined
│ │ │ └── combined.jpg
│ │ │ ├── labelled
│ │ │ └── label.tif
│ │ │ └── raw
│ │ │ └── image.tif
│ ├── example.ipynb
│ ├── hf_segmentation_model.py
│ ├── huggingface.ipynb
│ ├── inference.py
│ ├── model.py
│ ├── models
│ │ └── .gitkeep
│ ├── nnunet_model.py
│ ├── onnx_model.py
│ ├── requirements.txt
│ ├── sam_model.py
│ ├── segmentation_config.yaml
│ ├── test_image.tif
│ ├── train.py
│ └── utils.py
├── structures.py
├── tools
│ ├── _parser.py
│ ├── _streamlit.py
│ ├── run_manipulator_calibration.py
│ ├── run_split_dataset.py
│ └── telemetry.py
├── transformations.py
├── ui
│ ├── .gitkeep
│ ├── FibsemCryoDepositionWidget.py
│ ├── FibsemCryoDepositionWidget_qt.py
│ ├── FibsemEmbeddedDetectionWidget.py
│ ├── FibsemFeatureLabellingUI.py
│ ├── FibsemImageSettingsWidget.py
│ ├── FibsemImageViewer.py
│ ├── FibsemLabellingUI.py
│ ├── FibsemManipulatorWidget.py
│ ├── FibsemMicroscopeConfigurationWidget.py
│ ├── FibsemMicroscopeConfigurationWidgetBase.py
│ ├── FibsemMillingStageEditorWidget.py
│ ├── FibsemMillingWidget.py
│ ├── FibsemMinimapWidget.py
│ ├── FibsemModelTrainingWidget.py
│ ├── FibsemMovementWidget.py
│ ├── FibsemSegmentationModelWidget.py
│ ├── FibsemSpotBurnWidget.py
│ ├── FibsemSystemSetupWidget.py
│ ├── FibsemUI.py
│ ├── __init__.py
│ ├── napari
│ │ ├── patterns.py
│ │ ├── properties.py
│ │ └── utilities.py
│ ├── qtdesigner_files
│ │ ├── FibsemCryoDepositionWidget.py
│ │ ├── FibsemCryoDepositionWidget.ui
│ │ ├── FibsemEmbeddedDetectionWidget.py
│ │ ├── FibsemEmbeddedDetectionWidget.ui
│ │ ├── FibsemExperimentWidget.ui
│ │ ├── FibsemFeatureDetectionUI.py
│ │ ├── FibsemFeatureDetectionUI.ui
│ │ ├── FibsemLabellingUI.py
│ │ ├── FibsemLabellingUI.ui
│ │ ├── FibsemManipulatorWidget.py
│ │ ├── FibsemManipulatorWidget.ui
│ │ ├── FibsemMicroscopeConfigurationWidget.py
│ │ ├── FibsemMicroscopeConfigurationWidget.ui
│ │ ├── FibsemMicroscopeConfigurationWidget2.ui
│ │ ├── FibsemMicroscopeConfigurationWidgetBase.py
│ │ ├── FibsemMicroscopeConfigurationWidgetBase.ui
│ │ ├── FibsemMillingWidget.py
│ │ ├── FibsemMillingWidget.ui
│ │ ├── FibsemMinimapWidget.py
│ │ ├── FibsemMinimapWidget.ui
│ │ ├── FibsemModelTrainingWidge.ui
│ │ ├── FibsemModelTrainingWidget.py
│ │ ├── FibsemModelTrainingWidget.ui
│ │ ├── FibsemMovementWidget.py
│ │ ├── FibsemMovementWidget.ui
│ │ ├── FibsemSegmentationModelWidget.py
│ │ ├── FibsemSegmentationModelWidget.ui
│ │ ├── FibsemSpotBurnWidget.py
│ │ ├── FibsemSpotBurnWidget.ui
│ │ ├── FibsemSystemSetupWidget.py
│ │ ├── FibsemSystemSetupWidget.ui
│ │ ├── FibsemUI.py
│ │ ├── FibsemUI.ui
│ │ ├── ImageSettingsWidget.py
│ │ ├── ImageSettingsWidget.ui
│ │ ├── detection_dialog.py
│ │ ├── detection_dialog.ui
│ │ ├── image_viewer.py
│ │ └── image_viewer.ui
│ ├── stylesheets.py
│ └── utils.py
├── util
│ ├── __init__.py
│ └── filename.py
├── utils.py
└── validation.py
├── mkdocs.yml
├── pyproject.toml
├── requirements-3.8.txt
├── requirements-headless.txt
├── requirements.txt
├── scripts
├── .gitkeep
├── arctis_notebook.ipynb
├── compat-notebook.ipynb
├── convert_to_nnunet_dataset.py
├── convert_to_onnx.py
├── export_nnunet_checkpoint.py
├── generate_segmentation_objects.py
├── install.bat
├── install.sh
├── notebook.ipynb
├── onnx_notebook.ipynb
├── onnx_pred.py
├── run.sh
├── run_ui.bat
├── shortcut.py
├── stack_Arctis_Script.ipynb
├── test-alignment-script.py
└── test_installation.py
└── tests
├── milling
├── test_base.py
└── test_patterns.py
├── tescan-test-notebook.ipynb
├── test_acquire.py
├── test_alignment.py
├── test_example.py
├── test_microscope.py
├── test_movement.py
├── test_structures.py
└── test_tescan.py
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: docs
2 | on:
3 | push:
4 | branches:
5 | - v0.2-stable
6 | pull_request:
7 | branches:
8 | - v0.2-release
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-python@v2
15 | with:
16 | python-version: 3.x
17 | - run: pip install mkdocs-material mkdocstrings-python
18 | - run: mkdocs gh-deploy --force
19 |
--------------------------------------------------------------------------------
/.github/workflows/python-package.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | name: Python package
5 |
6 | on:
7 | pull_request:
8 | branches: [ "v0.2-release" ]
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | python-version: ["3.9", "3.10"]
18 |
19 | steps:
20 | - uses: actions/checkout@v3
21 | - name: Set up Python ${{ matrix.python-version }}
22 | uses: actions/setup-python@v3
23 | with:
24 | python-version: ${{ matrix.python-version }}
25 | - name: Install dependencies
26 | run: |
27 | python -m pip install --upgrade pip
28 | python -m pip install -e .
29 | - name: Test with pytest
30 | run: |
31 | pytest
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.egg-info/*
3 | .vscode/*
4 | .coverage
5 | *.code-workspace
6 | *.log
7 | scratch/test_image.tif
8 | *.zarr/*
9 |
10 | fibsem/wandb/*
11 |
12 | *.json
13 | fibsem/segmentation/wandb/*
14 | fibsem/segmentation/models/*.pt*
15 | wandb/*
16 |
17 | build/*
18 | fibsem/segmentation/training/*
19 | scratch/training/*
20 | fibsem/testing.ipynb
21 | fibsem/_version.py
22 | demo_*/*
23 | scratch/figure/wd*/*
24 | fibsem/log/data/*
25 | test_images/test.tif
26 |
27 | fibsem/detection/notebook.ipynb
28 |
29 | media/*
30 | .DS_Store
31 | fibsem/segmentation/models/**/*.pt*
32 | fibsem/segmentation/models/*
33 | example/**/*.tif
34 | example/demo/*
35 | fibsem/chat/secret.txt
36 |
37 | dist/*
38 | scratch/tile-images/*
39 | fibsem/log/*
40 | fibsem/db/fibsem.db
41 | fibsem/notebook.ipynb
42 | scratch/health-monitor/*
43 | example/notebook.ipynb
44 | fibsem/segmentation/*config*.y*ml
45 | !fibsem/segmentation/segementation_config.yaml
46 |
47 | fibsem/config/*.yaml
48 | !fibsem/config/microscope-configuration.yaml
49 | !fibsem/config/tescan_manipulator.yaml
50 | !fibsem/config/tfs*.yaml
51 | !fibsem/config/odemis*.yaml
52 | !fibsem/config/tescan-*.yaml
53 |
54 | **/wandb/*
55 | tests/notebook.ipynb
56 | fibsem/db/v2/notebook.ipynb
57 | scripts/segformer-*/*
58 | *.onnx
59 | scripts/**/*.jpeg
60 | scripts/**/*.png
61 | fibsem/ui/test_qt.py
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 DeMarcoLab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include README.md
3 |
4 | recursive-exclude * __pycache__
5 | recursive-exclude * *.py[co]
6 | recursive-exclude * *.ui
--------------------------------------------------------------------------------
/example/autolamella.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | import logging
4 | import os
5 | from dataclasses import dataclass
6 | from pathlib import Path
7 | from pprint import pprint
8 |
9 | import numpy as np
10 | from fibsem import acquire, alignment, milling, utils
11 | from fibsem.milling.base import get_milling_stages
12 | from fibsem.structures import BeamType, MicroscopeState, FibsemImage, FibsemStagePosition
13 | from typing import List
14 |
15 | @dataclass
16 | class Lamella:
17 | state: MicroscopeState
18 | reference_image: FibsemImage
19 | path: Path
20 |
21 | def main():
22 |
23 | PROTOCOL_PATH = os.path.join(os.path.dirname(__file__), "protocol_autolamella.yaml")
24 | microscope, settings = utils.setup_session(protocol_path=PROTOCOL_PATH)
25 |
26 | # move to the milling angle
27 | stage_position = FibsemStagePosition(
28 | r=np.deg2rad(settings.protocol["stage_rotation"]),
29 | t=np.deg2rad(settings.protocol["stage_tilt"])
30 | )
31 | microscope.move_stage_absolute(stage_position) # do need a safe version?
32 |
33 | # take a reference image
34 | settings.image.filename = "grid_reference"
35 | settings.image.beam_type = BeamType.ION
36 | settings.image.hfw = 900e-6
37 | settings.image.save = True
38 | acquire.take_reference_images(microscope, settings.image)
39 |
40 | # select positions
41 | experiment: List[Lamella] = []
42 | lamella_no = 1
43 | settings.image.hfw = 80e-6
44 | base_path = settings.image.path
45 |
46 | while True:
47 | response = input(f"""Move to the desired position.
48 | Do you want to select another lamella? [y]/n {len(experiment)} selected so far.""")
49 |
50 | # store lamella information
51 | if response.lower() in ["", "y", "yes"]:
52 |
53 | # set filepaths
54 | path = os.path.join(base_path, f"{lamella_no:02d}")
55 | settings.image.path = path
56 | settings.image.filename = f"ref_lamella"
57 | acquire.take_reference_images(microscope, settings.image)
58 |
59 | lamella = Lamella(
60 | state=microscope.get_microscope_state(),
61 | reference_image=acquire.new_image(microscope, settings.image),
62 | path = path
63 | )
64 | experiment.append(lamella)
65 | lamella_no += 1
66 | else:
67 | break
68 |
69 | # sanity check
70 | if len(experiment) == 0:
71 | logging.info(f"No lamella positions selected. Exiting.")
72 | return
73 |
74 | # mill (rough, thin, polish)
75 | workflow_stages = ["rough", "thin", "polish"]
76 | for stage_no, stage_name in enumerate(workflow_stages):
77 |
78 | logging.info(f"Starting milling stage {stage_no}")
79 |
80 | lamella: Lamella
81 | for lamella_no, lamella in enumerate(experiment):
82 |
83 | logging.info(f"Starting lamella {lamella_no:02d}")
84 |
85 | # return to lamella
86 | microscope.set_microscope_state(lamella.state)
87 |
88 | # realign
89 | alignment.beam_shift_alignment_v2(microscope, lamella.reference_image)
90 |
91 | if stage_no == 0:
92 | microexpansion_stage = get_milling_stages("microexpansion", settings.protocol)
93 | milling.mill_stage(microscope, microexpansion_stage[0])
94 |
95 | # get trench milling pattern, and mill
96 | trench_stage = get_milling_stages("lamella", settings.protocol)[stage_no]
97 | milling.mill_stage(microscope, trench_stage)
98 |
99 | # retake reference image
100 | settings.image.path = lamella.path
101 | settings.image.filename = f"ref_mill_stage_{stage_no:02d}"
102 | lamella.reference_image = acquire.new_image(microscope, settings.image)
103 |
104 | if stage_no == 3:
105 | # take final reference images
106 | settings.image.filename = f"ref_final"
107 | acquire.take_reference_images(microscope, settings.image)
108 |
109 | logging.info(f"Finished autolamella: {settings.protocol['name']}")
110 |
111 |
112 | if __name__ == "__main__":
113 | main()
114 |
--------------------------------------------------------------------------------
/example/example.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | from fibsem import utils, acquire
4 |
5 | import matplotlib.pyplot as plt
6 | import matplotlib
7 | matplotlib.use('TkAgg', force=True) # Activate 'agg' backend for off-screen plotting.
8 |
9 |
10 | def main():
11 |
12 | # connect to microscope
13 | microscope, settings = utils.setup_session(manufacturer="Demo", ip_address="localhost")
14 |
15 | # take image with both beams
16 | eb_image, ib_image = acquire.take_reference_images(microscope, settings.image)
17 |
18 | # show images
19 | fig, ax = plt.subplots(1, 2, figsize=(10, 5))
20 | ax[0].imshow(eb_image.data, cmap="gray")
21 | ax[0].set_title("Electron Beam Image")
22 | ax[0].axis("off")
23 | ax[1].imshow(ib_image.data, cmap="gray")
24 | ax[1].set_title("Ion Beam Image")
25 | ax[1].axis("off")
26 | plt.show()
27 |
28 |
29 | if __name__ == "__main__":
30 | main()
31 |
--------------------------------------------------------------------------------
/example/example_imaging.py:
--------------------------------------------------------------------------------
1 | import matplotlib
2 | import matplotlib.pyplot as plt
3 |
4 | from fibsem import acquire, utils
5 | from fibsem.structures import BeamType
6 | import logging
7 |
8 | matplotlib.use('TkAgg', force=True) # Activate 'agg' backend for off-screen plotting.
9 |
10 |
11 | """
12 | This script will take an image with the electron beam, an image with the ion beam, and an image with both beams.
13 | The images are then displayed in a matplotlib figure.
14 |
15 | The settings for images are stored in the settings.image struct, and can be modified before taking an image.
16 |
17 | For more detail on the settings, see the documentation for the ImageSettings class.
18 |
19 | """
20 |
21 | def main():
22 |
23 | # connect to the microscope
24 | microscope, settings = utils.setup_session(manufacturer="Demo", ip_address="localhost")
25 |
26 | # info about ImageSettings
27 | logging.info(f"\nAcquiring Images Example:")
28 | logging.info(f"The current image settings are: \n{settings.image}")
29 |
30 | # take an image with the electron beam
31 | settings.image.beam_type = BeamType.ELECTRON
32 | eb_image = acquire.new_image(microscope, settings.image)
33 |
34 | # take an image with the ion beam
35 | settings.image.beam_type = BeamType.ION
36 | ib_image = acquire.new_image(microscope, settings.image)
37 |
38 | # take an image with both beams with increased hfw
39 | settings.image.hfw = 400e-6
40 | ref_eb_image, ref_ib_image = acquire.take_reference_images(microscope, settings.image)
41 |
42 | # show images
43 |
44 | fig, ax = plt.subplots(2, 2, figsize=(10, 7))
45 | ax[0][0].imshow(eb_image.data, cmap="gray")
46 | ax[0][0].set_title("Electron Image 01")
47 | ax[0][1].imshow(ib_image.data, cmap="gray")
48 | ax[0][1].set_title("Ion Image 01")
49 | ax[1][0].imshow(ref_eb_image.data, cmap="gray")
50 | ax[1][0].set_title("Electron Image 02 (Reference)")
51 | ax[1][1].imshow(ref_ib_image.data, cmap="gray")
52 | ax[1][1].set_title("Ion Image 02 (Reference)")
53 | plt.show()
54 |
55 |
56 | if __name__ == "__main__":
57 | main()
58 |
--------------------------------------------------------------------------------
/example/example_milling.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from fibsem import milling, utils
4 | from fibsem.structures import (
5 | FibsemCircleSettings,
6 | FibsemLineSettings,
7 | FibsemMillingSettings,
8 | FibsemRectangleSettings,
9 | )
10 | from fibsem.milling import FibsemMillingStage, MillingAlignment
11 | from fibsem.milling.patterning.patterns2 import RectanglePattern, Point
12 |
13 | """
14 | This script demonstrates how to use the milling module to mill a rectangle and two lines.
15 |
16 | The script will:
17 | - connect to the microscope
18 | - setup milling
19 | - draw a rectangle and two lines
20 | - run milling
21 | - finish milling (restore ion beam current)
22 |
23 | """
24 |
25 | def main():
26 |
27 | # connect to microscope
28 | microscope, settings = utils.setup_session(manufacturer="Demo", ip_address="localhost")
29 |
30 |
31 | # setup milling stage (settings and alignment)
32 | stage = FibsemMillingStage(
33 | milling=FibsemMillingSettings(
34 | milling_current=2.0e-9,
35 | milling_voltage=30.0e3,
36 | hfw=100e-6,
37 | application_file="Si",
38 | patterning_mode="Serial",
39 | ),
40 | alignment=MillingAlignment(
41 | enabled=False
42 | )
43 | )
44 |
45 | # rectangle
46 | rectangle_shape = FibsemRectangleSettings(
47 | width = 10.0e-6,
48 | height = 10.0e-6,
49 | depth = 2.0e-6,
50 | rotation = 0.0,
51 | centre_x = 0.0,
52 | centre_y = 0.0,
53 | )
54 |
55 | # circle
56 | circle_shape = FibsemCircleSettings(
57 | radius = 10.0e-6,
58 | depth = 2.0e-6,
59 | centre_x = 10e-6,
60 | centre_y = 10e-6,
61 | )
62 |
63 | # line pattern
64 | line_shape = FibsemLineSettings(
65 | start_x = 0.0,
66 | start_y = 0.0,
67 | end_x = 10.0e-6,
68 | end_y = 10.0e-6,
69 | depth = 1.0e-6,
70 | )
71 |
72 | logging.info(f"""\nMilling Pattern Example: """)
73 | logging.info(f"The current milling settings are: \n{settings.milling}")
74 | logging.info(f"The current rectangle pattern is \n{rectangle_shape}")
75 | logging.info(f"The current circle pattern ins is \n{circle_shape}")
76 | logging.info(f"The current line pattern is \n{line_shape}")
77 | logging.info("---------------------------------- Milling ----------------------------------\n")
78 | # setup patterns in a list
79 | patterns = [rectangle_shape, circle_shape, line_shape]
80 |
81 | milling.setup_milling(microscope, stage)
82 |
83 | # draw patterns
84 | milling.draw_patterns(microscope, patterns)
85 |
86 | # run milling
87 | milling.run_milling(microscope, stage.milling.milling_current, milling_voltage=stage.milling.milling_voltage)
88 |
89 | # finish milling
90 | milling.finish_milling(microscope, microscope.system.ion.beam.beam_current)
91 |
92 |
93 | rect = RectanglePattern(
94 | width=10e-6,
95 | height=10e-6,
96 | depth=2e-6,
97 | rotation=0,
98 | point=Point(0, 0)
99 | )
100 | stage.pattern = rect
101 |
102 | milling.mill_stages(microscope, stage)
103 |
104 | if __name__ == "__main__":
105 | main()
106 |
--------------------------------------------------------------------------------
/example/example_movement.py:
--------------------------------------------------------------------------------
1 | from fibsem import utils
2 | from fibsem.structures import FibsemStagePosition
3 | import numpy as np
4 | import logging
5 |
6 |
7 | """
8 | This script demonstrates how to get the current stage position, and how to move the stage to a new position.
9 |
10 | The basic movement methods are absolute_move and relative_move.
11 | - Relative move moves the stage by a certain amount in the current coordinate system.
12 | - Absolute move moves the stage to a new position in the absolute coordinate system.
13 |
14 | This script will move the stage by 20um in the x direction (relative move), and then move back to the original position (absolute move).
15 |
16 | Additional movement methods are available in the core api:
17 | - Stable Move: the stage moves along the sample plane, accounting for stage tilt, and shuttle pre-tilt
18 | - Vertical Move: the stage moves vertically in the chamber, regardless of tilt orientation
19 |
20 | """
21 |
22 | def main():
23 |
24 | # connect to microscope
25 | microscope, settings = utils.setup_session(manufacturer="Demo", ip_address="localhost")
26 |
27 | # info about ImageSettings
28 | logging.info("---------------------------------- Current Position ----------------------------------\n")
29 |
30 | # get current position
31 | intial_position = microscope.get_stage_position()
32 | logging.info(f"\nStage Movement Example:")
33 | logging.info(f"Current stage position: {intial_position}")
34 |
35 |
36 | logging.info("\n---------------------------------- Relative Movement ----------------------------------\n")
37 |
38 | #### Moving to a relative position ####
39 | relative_move = FibsemStagePosition(x=20e-6, # metres
40 | y=0, # metres
41 | z=0.0, # metres
42 | r=np.deg2rad(0), # radians
43 | t=np.deg2rad(0)) # radians
44 |
45 | input(f"Press Enter to move by: {relative_move} (Relative)")
46 |
47 | # move by relative position
48 | microscope.move_stage_relative(relative_move)
49 | current_position = microscope.get_stage_position()
50 | logging.info(f"After move stage position: {current_position}")
51 |
52 |
53 | logging.info("\n---------------------------------- Absolute Movement ----------------------------------\n")
54 |
55 | #### Moving to an absolute position ####
56 | stage_position = intial_position # move back to initial position
57 |
58 | # uncomment this if you want to move to a different position
59 | # be careful to define a safe position to move too
60 | # relative_move = FibsemStagePosition(x=0, # metres
61 | # y=0, # metres
62 | # z=0.0, # metres
63 | # r=np.deg2rad(0), # radians
64 | # t=np.deg2rad(0)) # radians
65 |
66 | input(f"Press Enter to move to: {stage_position} (Absolute)")
67 |
68 | # move to absolute position
69 | microscope.move_stage_absolute(stage_position)
70 | current_position = microscope.get_stage_position()
71 | logging.info(f"After move stage position: {current_position}")
72 |
73 |
74 | logging.info("---------------------------------- End Example ----------------------------------")
75 |
76 |
77 | if __name__ == "__main__":
78 | main()
--------------------------------------------------------------------------------
/example/lithography.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pprint import pprint
3 |
4 | import numpy as np
5 | from autoscript_sdb_microscope_client.structures import (
6 | BitmapPatternDefinition, StagePosition)
7 | from fibsem import acquire, milling, movement, utils
8 | # from fibsem.ui import windows
9 | from fibsem.structures import BeamType
10 | from PIL import Image
11 |
12 | BASE_PATH = os.path.dirname(__file__)
13 |
14 | def save_profile_to_bmp(arr: np.ndarray, fname: str = "profile.bmp"):
15 |
16 | # scale values to int
17 | arr = (arr / np.max(arr)) * 255
18 | arr = arr.astype(np.uint8)
19 |
20 | # save profile
21 | Image.fromarray(arr).convert("RGB").save(fname)
22 |
23 |
24 | def main():
25 |
26 | PROTOCOL_PATH = os.path.join(BASE_PATH, "protocol_lithography.yaml")
27 | microscope, settings = utils.setup_session(protocol_path=PROTOCOL_PATH)
28 |
29 | # lens plane
30 | microscope.system.electron.column_tilt = 0
31 |
32 | # move to the milling angle
33 | # stage_position = StagePosition(
34 | # r=np.deg2rad(settings.protocol["stage_rotation"]),
35 | # t=np.deg2rad(settings.protocol["stage_tilt"]),
36 | # )
37 | # movement.safe_absolute_stage_movement(microscope, stage_position)
38 |
39 | microscope.move_flat_to_beam(BeamType.ION)
40 |
41 | # eucentric, select position
42 | # windows.ask_user_movement(
43 | # microscope,
44 | # settings,
45 | # msg_type="eucentric",
46 | # msg="Select a position to mill the pattern.",
47 | # )
48 |
49 | # lens profile files
50 | npy_path = os.path.join(BASE_PATH, settings.protocol["profile"])
51 | bmp_path = os.path.join(BASE_PATH, "profile.bmp")
52 |
53 | # load milling properties
54 | profile = np.load(npy_path)
55 | pixel_size = settings.protocol["pixelsize"]
56 | lens_height = settings.protocol["milling"]["height"]
57 | lens_width = settings.protocol["milling"]["width"]
58 |
59 | # save profile to bmp
60 | save_profile_to_bmp(profile, bmp_path)
61 |
62 | # load bmp pattern
63 | bitmap_pattern = BitmapPatternDefinition()
64 | bitmap_pattern.load(bmp_path)
65 |
66 | # milling setup
67 | milling.setup_milling(
68 | microscope, application_file=settings.protocol["application_file"]
69 | )
70 |
71 | # surface milling
72 | microscope.patterning.create_bitmap(
73 | center_x=0,
74 | centre_y=0,
75 | width=lens_width,
76 | height=lens_height,
77 | depth=settings.protocol["initial_depth"],
78 | bitmap_pattern_definition=bitmap_pattern,
79 | )
80 |
81 | # mill bitmap
82 | microscope.patterning.create_bitmap(
83 | center_x=0,
84 | centre_y=0,
85 | width=lens_width,
86 | height=lens_height,
87 | depth=settings.protocol["milling"]["milling_depth"],
88 | bitmap_pattern_definition=bitmap_pattern,
89 | )
90 | milling.run_milling(microscope, settings.protocol["milling"]["milling_current"])
91 | milling.finish_milling(microscope)
92 |
93 |
94 | if __name__ == "__main__":
95 | main()
96 |
--------------------------------------------------------------------------------
/example/profile.npy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/example/profile.npy
--------------------------------------------------------------------------------
/example/protocol_autolamella.yaml:
--------------------------------------------------------------------------------
1 |
2 | name: autolamella_demo
3 | stage_rotation: 50
4 | stage_tilt: 20
5 |
6 |
7 | fiducial:
8 | length: 10.e-6
9 | width: 2.e-6
10 | depth: 2.e-6
11 | milling_voltage: 30.0e+3
12 | milling_current: 2.e-9
13 | hfw: 150.0e-6
14 | application_file: autolamella
15 |
16 | lamella:
17 | stages:
18 | - lamella_width: 10.e-6
19 | lamella_height: 5.e-6
20 | trench_height: 10.e-6
21 | depth: 2.e-6
22 | offset: 5.e-6
23 | size_ratio: 1.0
24 | milling_voltage: 30.0e+3
25 | milling_current: 2.e-9
26 | cleaning_cross_section: True
27 | hfw: 150.0e-6
28 | application_file: autolamella
29 | - lamella_width: 10.e-6
30 | lamella_height: 5.e-6
31 | trench_height: 1.e-6
32 | depth: 2.e-6
33 | offset: 0.5e-6
34 | size_ratio: 1.0
35 | hfw: 80.0e-6
36 | milling_voltage: 30.0e+3
37 | milling_current: 0.2e-9
38 | cleaning_cross_section: True
39 | application_file: autolamella
40 | - lamella_width: 10.e-6
41 | lamella_height: 5.e-6
42 | trench_height: 0.5e-6
43 | depth: 0.4e-6
44 | offset: 0.e-6
45 | size_ratio: 1.0
46 | milling_current: 60.e-12
47 | milling_voltage: 30.0e+3
48 | hfw: 80.0e-6
49 | cleaning_cross_section: True
50 | application_file: autolamella
51 |
52 | microexpansion:
53 | height: 10.0e-6
54 | width: 2.0e-6
55 | depth: 5.0e-6
56 | distance: 10.0e-6
57 | hfw: 150.0e-6
58 | milling_voltage: 30.0e+3
59 | milling_current: 2.e-9
60 | application_file: autolamella
61 |
--------------------------------------------------------------------------------
/example/protocol_lithography.yaml:
--------------------------------------------------------------------------------
1 |
2 | name: lens_milling_demo
3 | stage_rotation: 230
4 | stage_tilt: 52
5 | application_file: Si
6 | plasma_gas: Xenon
7 |
8 | profile: profile.npy
9 | pixelsize: 1.e-6
10 | milling:
11 | width: 50.e-6
12 | height: 30.0e-6
13 | milling_current: 60.e-9
14 | initial_depth: 600.e-9
15 | milling_depth: 5.0e-6
16 |
17 |
--------------------------------------------------------------------------------
/example/protocol_slice_and_view.yaml:
--------------------------------------------------------------------------------
1 |
2 | name: slice_and_view_demo
3 | steps: 10
4 | step_size: 150.e-9
5 | milling:
6 | width: 100.e-6
7 | height: 1.e-6
8 | depth: 5.e-6
9 | milling_current: 2.e-9
10 | scan_direction: BottomToTop
11 | cross_section: Rectangle
12 |
--------------------------------------------------------------------------------
/example/slice_and_view.py:
--------------------------------------------------------------------------------
1 | # slice and view
2 |
3 | import os
4 | import logging
5 | from pprint import pprint
6 |
7 | import fibsem
8 | from fibsem import acquire, milling, utils, alignment
9 | from fibsem.structures import BeamType, ImageSettings, FibsemRectangleSettings
10 | import numpy as np
11 |
12 | def main():
13 |
14 | PROTOCOL_PATH = os.path.join(os.path.dirname(__file__), "protocol_slice_and_view.yaml")
15 | microscope, settings = utils.setup_session(protocol_path = PROTOCOL_PATH)
16 |
17 | # setup for milling
18 | milling.setup_milling(microscope = microscope,
19 | patterning_mode = "Serial",
20 | mill_settings = settings.milling)
21 |
22 | pattern_settings = FibsemRectangleSettings(
23 | width = settings.protocol["milling"]["width"],
24 | height = settings.protocol["milling"]["height"],
25 | depth = settings.protocol["milling"]["depth"],
26 | scan_direction= settings.protocol["milling"]["scan_direction"],
27 | cross_section= settings.protocol["milling"]["cross_section"]
28 | )
29 |
30 | # angle correction
31 | microscope.set("angular_correction_tilt_correction", True)
32 | microscope.set("angular_correction_angle", np.deg2rad(-38))
33 |
34 | # update image settings
35 | settings.image.filename = "reference"
36 | settings.image.save = True
37 | settings.image.beam_type = BeamType.ELECTRON
38 |
39 | eb_image, ib_image = acquire.take_reference_images(microscope, settings.image)
40 | ref_eb, ref_ib = None, None
41 |
42 | for slice_idx in range(int(settings.protocol["steps"])):
43 |
44 | # slice
45 | logging.info("------------------------ SLICE ------------------------")
46 | milling_settings = settings.milling
47 |
48 | patterns = milling.draw_pattern(microscope, pattern_settings)
49 | # estimated_milling_time = milling.estimate_milling_time_in_seconds([patterns])
50 | # logging.info(f"Estimated milling time: {estimated_milling_time}")
51 | milling.run_milling(microscope, milling_current=milling_settings.milling_current)
52 | milling.finish_milling(microscope, settings.system.ion.current)
53 |
54 | # view
55 | logging.info("------------------------ VIEW ------------------------")
56 | settings.image.filename = f"slice_{slice_idx:04d}"
57 | eb_image = acquire.new_image(microscope, settings.image)
58 |
59 |
60 | # align
61 | if ref_eb is not None:
62 | alignment.align_using_reference_images(microscope, ref_eb, eb_image)
63 | ref_eb = eb_image
64 |
65 |
66 | # update patterns
67 | pattern_settings.centre_y += settings.protocol["step_size"]
68 |
69 |
70 | if __name__ == "__main__":
71 | main()
72 |
--------------------------------------------------------------------------------
/external/application_files/autolamella.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 | XTUI, iFast
10 |
11 |
12 |
13 |
14 |
15 | Ion
16 |
17 |
18 |
19 |
22 |
23 | Line, Circle, Rectangle, Bitmap, Streamfile, Polygon, CCS, RCS
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 1.000E-6
35 |
36 |
37 |
38 |
39 |
40 | 98
41 |
42 |
43 |
44 |
46 |
47 | 0.00000000038
48 |
49 |
50 |
51 |
62 |
63 | 0
64 |
65 |
66 |
67 |
68 |
69 | 0
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/external/application_files/cryo_Pt_dep.xml:
--------------------------------------------------------------------------------
1 |
2 | Line
3 | Electron
4 | Pt cryo
5 | 1:0.2
6 | 1:cryo
7 | 1E-07
8 | -90
9 | 5E-11
10 | 0
11 | 0
12 | 0
13 | 0
14 | 0
15 | 1
16 |
--------------------------------------------------------------------------------
/fibsem/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | try:
3 | import importlib.metadata
4 | __version__ = importlib.metadata.version('fibsem')
5 | except ModuleNotFoundError:
6 | __version__ = "unknown"
7 |
8 |
--------------------------------------------------------------------------------
/fibsem/chat/.gitignore:
--------------------------------------------------------------------------------
1 | secret.txt
2 | documents/*
3 | .chroma/*
4 | *.pdf
5 | *.mp4
6 | *.docx
--------------------------------------------------------------------------------
/fibsem/chat/main.py:
--------------------------------------------------------------------------------
1 | #
2 | from langchain.document_loaders import UnstructuredPDFLoader
3 | from langchain.indexes import VectorstoreIndexCreator
4 | from langchain.llms import OpenAI
5 | import os
6 |
7 | # import OpenAI API key from secret.txt
8 | with open("secret.txt", "r") as f:
9 | os.environ["OPENAI_API_KEY"] = f.read()
10 |
11 | def create_index(filenames: list[str]):
12 | # TODO: make a list of files to index
13 | llm = OpenAI(openai_api_key="OPENAI_API_KEY")
14 | loaders = [UnstructuredPDFLoader(fname) for fname in filenames]
15 | index = VectorstoreIndexCreator().from_loaders(loaders)
16 |
17 | return index
18 |
19 |
20 | # TODO: select files
21 | # TODO: tool use
22 |
23 | # persist
24 | # https://python.langchain.com/en/latest/modules/indexes/vectorstores/examples/chroma.html
25 |
26 | # enter an endless loop with user input
27 | def main():
28 | print("Welcome to FIBSEM Chat! Enter your question, or type 'quit' to exit.")
29 |
30 | # load user manual, index
31 | filenames = ["documents/OnlineManualHeliosHydra.pdf"]
32 | # filenames = ["documents/autoliftout.pdf", "documents/supplementary.pdf"]
33 |
34 | print(f"Creating index from {filenames}.")
35 | index = create_index(filenames=filenames)
36 | print(f"Index created from {filenames}.\n")
37 |
38 | while True:
39 |
40 | q = input("Enter a question: ")
41 |
42 | if q in ["quit", "exit"]:
43 | print("\nGoodbye!")
44 | break
45 |
46 | print("Thinking...")
47 |
48 | # run query, show result
49 | ret = index.query(q)
50 |
51 | print("\n\nResponse: " + ret)
52 | print("-"*50)
53 |
54 |
55 | if __name__ == "__main__":
56 | main()
57 |
--------------------------------------------------------------------------------
/fibsem/chat/requirements.txt:
--------------------------------------------------------------------------------
1 | langchain
2 | openai
3 | jupyter
4 | unstructured
5 | chromadb
6 | Cython
7 | tiktoken
8 | pdf2image
--------------------------------------------------------------------------------
/fibsem/config/deposition.dbp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/config/deposition.dbp
--------------------------------------------------------------------------------
/fibsem/config/positions.yaml:
--------------------------------------------------------------------------------
1 | - coordinate_system: RAW
2 | name: grid02-pre-tilt-35-deg-electron
3 | r: 0.8551927515811997
4 | t: 0.6108583337307305
5 | x: 0.0028004166666666668
6 | y: 0.004053833333333333
7 | z: 0.03175649434156379
8 | - coordinate_system: RAW
9 | name: grid02-pre-tilt-35-deg-ion
10 | r: -2.286298510029761
11 | t: 0.29669765745887133
12 | x: -0.00368525
13 | y: -0.0023315
14 | z: 0.03184661136831276
15 | - coordinate_system: RAW
16 | name: grid01-pre-tilt-35-deg-ion
17 | r: -2.286298510029761
18 | t: 0.29669765745887133
19 | x: 0.0024960833333333332
20 | y: -0.002562333333333333
21 | z: 0.03186551568930041
22 | - coordinate_system: RAW
23 | name: grid01-pre-tilt-35-deg-electron
24 | r: 0.8551927515811997
25 | t: 0.6108470164082682
26 | x: -0.003231
27 | y: 0.00415
28 | z: 0.03186721965020576
29 | - coordinate_system: Raw
30 | name: autoliftout-pre-tilt-35-deg-grid-01-lamella
31 | r: -2.28636643635382
32 | t: 0.2967058527613453
33 | x: 0.0023149166666666665
34 | y: -0.0019699166666666667
35 | z: 0.031954218106995884
36 | - coordinate_system: Raw
37 | name: autoliftout-pre-tilt-35-deg-grid-02-landing
38 | r: -2.286298510029761
39 | t: 0.5585004974916625
40 | x: -0.003933166666666666
41 | y: -0.0023545
42 | z: 0.031787004886831276
43 | - coordinate_system: RAW
44 | name: autoliftout-serial-pre-tilt-35-deg-grid-01-lamella
45 | r: -2.2863324731917913
46 | t: 0.2967019502363577
47 | x: 0.00238175
48 | y: -0.0025256666666666665
49 | z: 0.031839988425925926
50 | - coordinate_system: RAW
51 | name: autoliftout-serial-pre-tilt-35-deg-grid-02-lamella
52 | r: -2.2863324731917913
53 | t: 0.2967019502363577
54 | x: -0.00364925
55 | y: -0.0025468333333333332
56 | z: 0.031784593621399175
57 | - coordinate_system: RAW
58 | name: autoliftout-serial-pre-tilt-35-deg-grid-01-landing
59 | r: 0.8551927515811997
60 | t: 0.45378482501352835
61 | x: -0.0034078333333333335
62 | y: 0.004113083333333333
63 | z: 0.031839988425925926
64 | - coordinate_system: RAW
65 | name: autoliftout-serial-pre-tilt-35-deg-grid-02-landing
66 | r: 0.8551927515811997
67 | t: 0.4537750687010593
68 | x: 0.0029505833333333333
69 | y: 0.004088416666666666
70 | z: 0.03178491512345679
71 | - coordinate_system: RAW
72 | name: arctis-grid-01-electron
73 | r: 0
74 | t: -3.141592653589793
75 | x: 0
76 | y: 0
77 | z: 0
78 | - coordinate_system: RAW
79 | name: arctis-grid-01-ion
80 | r: 0
81 | t: -2.234021442552742
82 | x: 0
83 | y: 0
84 | z: 0
85 |
--------------------------------------------------------------------------------
/fibsem/config/protocol.yaml:
--------------------------------------------------------------------------------
1 | # default protocol
2 |
3 | name: fibsem
4 | version: 0.1.0
5 | description: protocol for fibsem
6 |
7 |
8 | milling:
9 | milling_current: 2.e-9
10 | milling_voltage: 30000
11 | application_file: Si
12 | hfw: 150.0e-6
13 | spot_size: 5.0e-8
14 | rate: 3.0e-3
15 | dwell_time: 1.e-6
16 | preset: "30 keV; 2.5 nA" # TESCAN only
17 |
18 | patterns:
19 | Rectangle:
20 | width: 10.0e-6
21 | height: 5.0e-6
22 | depth: 1.0e-6
23 | rotation: 0.0
24 | scan_direction: TopToBottom
25 | passes: 0 # means auto
26 | time: 0 # means auto
27 | Line:
28 | start_x: -10.0e-6
29 | start_y: 0.0
30 | end_x: 10.0e-6
31 | end_y: 0
32 | depth: 1.0e-6
33 | Circle:
34 | radius: 5.0e-6
35 | depth: 1.0e-6
36 | Trench:
37 | width: 10.0e-6
38 | spacing: 5.0e-6
39 | upper_trench_height: 5.0e-6
40 | lower_trench_height: 5.0e-6
41 | depth: 2.0e-6
42 | time: 0 # means auto
43 | fillet: 0 # no fillet radius
44 | Horseshoe:
45 | width: 40.0e-6
46 | spacing: 10.0e-6
47 | upper_trench_height: 10.0e-6
48 | lower_trench_height: 10.0e-6
49 | side_width: 5.0e-6
50 | side_offset: 0.0
51 | depth: 10.0e-6
52 | scan_direction: TopToBottom
53 | HorseshoeVertical:
54 | depth: 4.0e-6
55 | height: 5.0e-05
56 | width: 2.0e-05
57 | scan_direction: TopToBottom
58 | side_trench_width: 5.0e-06
59 | top_trench_height: 10.0e-6
60 | inverted: False
61 | SerialSection:
62 | section_thickness: 4.0e-6
63 | section_width: 50.0e-6
64 | section_depth: 20.0e-6
65 | side_width: 10.0e-6
66 | side_depth: 40.0e-6
67 | side_height: 10.0e-6
68 | inverted: false
69 | RectangleOffset:
70 | width: 10.0e-6
71 | height: 5.0e-6
72 | depth: 1.0e-6
73 | scan_direction: TopToBottom
74 | cross_section: Rectangle
75 | offset: 0.0e-6
76 | inverted: false
77 | Undercut:
78 | height: 10.0e-6
79 | width: 10.0e-6
80 | depth: 5.0e-6
81 | trench_width: 2.0e-6
82 | rhs_height: 10.0e-6
83 | h_offset: 5.0e-6
84 | Fiducial:
85 | height: 10.0e-6
86 | width: 1.0e-6
87 | depth: 5.0e-6
88 | rotation: 45.0
89 | ArrayPattern:
90 | height: 2.0e-6
91 | width: 2.0e-6
92 | depth: 5.0e-6
93 | n_columns: 5
94 | n_rows: 5
95 | pitch_vertical: 5.0e-6
96 | pitch_horizontal: 5.0e-6
97 | rotation: 0.0
98 | passes: 1
99 | scan_direction: "TopToBottom"
100 | MicroExpansion:
101 | height: 15.0e-6
102 | width: 0.5e-6
103 | depth: 1.0e-6
104 | distance: 10.0e-6
105 | WaffleNotch:
106 | vheight: 2.0e-6
107 | vwidth: 0.5e-6
108 | hwidth: 2.0e-6
109 | hheight: 0.5e-6
110 | depth: 1.0e-6
111 | distance: 2.0e-6
112 | Clover:
113 | radius: 10.0e-6
114 | depth: 5.0e-6
115 | TriForce:
116 | height: 10.0e-6
117 | width: 1.0e-6
118 | depth: 5.0e-6
119 | BitmapPattern:
120 | width: 10.0e-6
121 | height: 10.0e-6
122 | depth: 1.0e-6
123 | rotation: 0.0
124 | path:
125 |
126 | Annulus:
127 | thickness: 2.0e-6
128 | radius: 10.0e-6
129 | depth: 1.0e-6
130 | Trapezoid:
131 | inner_width: 10.0e-6
132 | outer_width: 20.0e-6
133 | trench_height: 5.0e-6
134 | depth: 1.0e-6
135 | distance: 1.0e-6
136 | n_rectangles: 10
137 | overlap: 0.0
138 | scan_direction: "TopToBottom"
139 | type: "Trapezoid"
140 |
--------------------------------------------------------------------------------
/fibsem/config/tescan_manipulator.yaml:
--------------------------------------------------------------------------------
1 | positions:
2 | calibrated: false
3 | parking:
4 | x: -0.008918395
5 | y: 0.0006548000000000001
6 | z: -0.004848865
7 | standby:
8 | x: 0.0
9 | y: 0.0
10 | z: 0.00545
11 | working:
12 | x: 0.0
13 | y: 0.0
14 | z: 0.00585
15 | # nb: these were for the AMBER simulator, real numbers might be different
16 |
17 | # TODO: refactor this to match Retract, Park, Eucentric terminology
--------------------------------------------------------------------------------
/fibsem/constants.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | MICRON_TO_METRE = 1e-6
3 | METRE_TO_MICRON = 1.0 / MICRON_TO_METRE
4 |
5 | MILLIMETRE_TO_METRE = 1e-3
6 | METRE_TO_MILLIMETRE = 1.0 / MILLIMETRE_TO_METRE
7 |
8 | SI_TO_KILO = 1e-3
9 | KILO_TO_SI = 1/ SI_TO_KILO
10 |
11 | SI_TO_MILLI = 1e3
12 | MILLI_TO_SI = 1 / SI_TO_MILLI
13 |
14 | SI_TO_MICRO = 1e6
15 | MICRO_TO_SI = 1 / SI_TO_MICRO
16 |
17 | SI_TO_NANO = 1e9
18 | NANO_TO_SI = 1 / SI_TO_NANO
19 |
20 | SI_TO_PICO = 1e12
21 | PICO_TO_SI = 1 / SI_TO_PICO
22 |
23 | RADIANS_TO_DEGREES = 180.0 / np.pi
24 |
25 | DEGREES_TO_RADIANS = np.pi / 180.0
26 |
27 | TO_PERCENTAGES = 100.0
28 |
29 | FROM_PERCENTAGES = 1.0 / TO_PERCENTAGES
30 |
31 | DEGREE_SYMBOL = "°"
--------------------------------------------------------------------------------
/fibsem/db/config.yaml:
--------------------------------------------------------------------------------
1 | name: fibsem.db
2 |
3 | tables:
4 | - projects
5 | - experiments
6 | - users
7 | - samples
8 | -
--------------------------------------------------------------------------------
/fibsem/db/v2/app.py:
--------------------------------------------------------------------------------
1 |
2 | from fastapi import FastAPI
3 | from fastapi.responses import HTMLResponse
4 |
5 | from fibsem.db.v2.models import Base, User, Project, Sample, Instrument, Experiment, Lamella, Step, Detection, Interaction
6 | from fibsem.db.v2 import util
7 | from fibsem import config as cfg
8 |
9 |
10 | app = FastAPI()
11 |
12 | @app.get("/")
13 | async def root():
14 | return {"message": "Hello World"}
15 |
16 | # create a connection to the database
17 | engine = util.create_connection()
18 |
19 | # create a session
20 | session = util.create_session(engine)
21 |
22 | # return all users
23 | @app.get("/users")
24 | async def get_users():
25 | users = session.query(User).all()
26 | return users
27 |
28 | @app.get("/users/{user_id}")
29 | async def get_user(user_id: int):
30 | user = session.query(User).filter(User.id == user_id).first()
31 | return user
32 |
33 | # return all projects
34 | @app.get("/projects")
35 | async def get_projects():
36 | projects = session.query(Project).all()
37 | return projects
38 |
39 | @app.get("/projects/{project_id}")
40 | async def get_project(project_id: int):
41 | project = session.query(Project).filter(Project.id == project_id).first()
42 | return project
43 |
44 |
45 | # return all samples
46 | @app.get("/samples")
47 | async def get_samples():
48 | samples = session.query(Sample).all()
49 | return samples
50 |
51 | @app.get("/samples/{sample_id}")
52 | async def get_sample(sample_id: int):
53 | sample = session.query(Sample).filter(Sample.id == sample_id).first()
54 | return sample
55 |
56 |
57 | @app.get("/experiments")
58 | async def get_experiments():
59 | experiments = session.query(Experiment).all()
60 |
61 | # drop the .data attribute
62 | for experiment in experiments:
63 | experiment.data = None
64 |
65 | return experiments
66 |
67 | @app.get("/instruments")
68 | async def get_instruments():
69 | instruments = session.query(Instrument).all()
70 | return instruments
71 |
72 | @app.post("/user")
73 | async def create_user(user: dict):
74 | user = User.from_dict(user)
75 | user = await util.create_user(session, user)
76 | return {"status": "ok", "data": user}
77 |
78 |
79 | @app.post("/project")
80 | async def create_project(project: dict):
81 | project = Project.from_dict(project)
82 | project = await util.create_project(session, project)
83 | return {"status": "ok", "data": project}
84 |
85 |
86 | @app.post("/sample")
87 | async def create_sample(sample: dict):
88 | sample = Sample.from_dict(sample)
89 | sample = await util.create_sample(session, sample)
90 | return {"status": "ok", "data": sample}
91 |
92 |
93 | @app.put("/sample/{sample_id}")
94 | async def update_sample(sample_id: int, sample: dict):
95 | sample = Sample.from_dict(sample)
96 | sample = await util.update_sample(session, sample_id, sample)
97 | return {"status": "ok", "data": sample}
98 |
99 |
100 | # https://fastapi.tiangolo.com/tutorial/sql-databases/#__tabbed_1_2
--------------------------------------------------------------------------------
/fibsem/db/v2/util.py:
--------------------------------------------------------------------------------
1 |
2 | from sqlalchemy import create_engine
3 | from sqlalchemy.orm import sessionmaker
4 |
5 | from fibsem import config as cfg
6 | from fibsem.db.v2.models import Base, User, Project, Sample, Instrument, Experiment, Lamella, Step, Detection, Interaction
7 |
8 | def create_connection(database_path: str = cfg.DATABASE_PATH):
9 | # create a connection to the database
10 | engine = create_engine(f'sqlite:///{database_path}')
11 |
12 | return engine
13 |
14 | def create_database(engine):
15 | # create the database
16 | Base.metadata.create_all(engine)
17 |
18 | def create_session(engine):
19 | # create a session
20 | Session = sessionmaker(bind=engine)
21 | session = Session()
22 | return session
23 |
24 | async def create_user(session, user: User):
25 | session.add(user)
26 | session.commit()
27 | return user
28 |
29 | async def create_project(session, project: Project):
30 | session.add(project)
31 | session.commit()
32 | return project
33 |
34 | async def create_sample(session, sample: Sample):
35 | session.add(sample)
36 | session.commit()
37 | return sample
38 |
39 | async def create_instrument(session, instrument: Instrument):
40 | session.add(instrument)
41 | session.commit()
42 | return instrument
43 |
44 |
45 | async def create_experiment(session, experiment: Experiment):
46 | session.add(experiment)
47 | session.commit()
48 | return experiment
49 |
50 | async def create_lamella(session, lamella: Lamella):
51 | session.add(lamella)
52 | session.commit()
53 | return lamella
54 |
55 | def create_step(session, step: Step):
56 | session.add(step)
57 | session.commit()
58 | return step
59 |
60 | def create_detection(session, detection: Detection):
61 | session.add(detection)
62 | session.commit()
63 | return detection
64 |
65 | def create_interaction(session, interaction: Interaction):
66 | session.add(interaction)
67 | session.commit()
68 | return interaction
69 |
70 |
71 | def add_steps(session, steps: list):
72 | session.add_all(steps)
73 | session.commit()
74 | return steps
75 |
76 | def add_detections(session, detections: list):
77 | session.add_all(detections)
78 | session.commit()
79 | return detections
80 |
81 | def add_interactions(session, interactions: list):
82 | session.add_all(interactions)
83 | session.commit()
84 | return interactions
85 |
86 |
87 |
88 | def get_user(session, username: str):
89 | user = session.query(User).filter(User.username == username).first()
90 | session.close()
91 | return user
92 |
93 | def get_project(session, name: str):
94 | project = session.query(Project).filter(Project.name == name).first()
95 | session.close()
96 | return project
97 |
98 | def get_sample(session, name: str):
99 | sample = session.query(Sample).filter(Sample.name == name).first()
100 | session.close()
101 | return sample
102 |
103 | def get_instrument(session, name: str):
104 | instrument = session.query(Instrument).filter(Instrument.name == name).first()
105 | session.close()
106 | return instrument
107 |
108 | def get_projects(session):
109 | projects = session.query(Project).all()
110 | session.close()
111 | return projects
112 |
113 | def get_samples(session):
114 | samples = session.query(Sample).all()
115 | session.close()
116 | return samples
117 |
118 | def get_instruments(session):
119 | instruments = session.query(Instrument).all()
120 | session.close()
121 | return instruments
122 |
123 | def get_experiments(session):
124 | experiments = session.query(Experiment).all()
125 | session.close()
126 | return experiments
127 |
128 | def get_users(session):
129 | """Query the User table"""
130 | users = session.query(User).all()
131 | session.close()
132 | return users
133 |
--------------------------------------------------------------------------------
/fibsem/db/v3/app.py:
--------------------------------------------------------------------------------
1 | import streamlit as st
2 |
3 | from fibsem import config as cfg
4 | from fibsem.db.v3.util import get_session, create_connection
5 |
6 | import pandas as pd
7 | st.set_page_config(page_title='Fibsem DB v3', page_icon=":microscope:", layout="wide")
8 |
9 | st.title('Fibsem DB v3')
10 |
11 |
12 | # connectto the database
13 | conn = st.connection("fibsem.db", type="sql", url=f'sqlite:///{cfg.DATABASE_PATH}', ttl=0)
14 |
15 |
16 |
17 | @st.cache_data(show_spinner=True)
18 | def get_user_data():
19 | return conn.query("SELECT * FROM user")
20 |
21 |
22 | # force refresh
23 | if st.button('Refresh'):
24 | get_user_data.clear()
25 |
26 | df = None
27 |
28 | df = get_user_data()
29 | st.data_editor(df)
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | # sidebar
40 | st.sidebar.title('Navigation')
41 | page = st.sidebar.radio('Go to', ('Home', 'Users', 'Samples', 'Instruments', 'Configurations'))
42 |
43 | @st.cache_resource
44 | def get_user_model():
45 | from fibsem.db.v3.models import User
46 | return User
47 |
48 | User = get_user_model()
49 |
50 | # create a form for adding users (using sqlmodel)
51 | if page == 'Users':
52 | st.header('Users')
53 | with st.form(key='add_user'):
54 | username = st.text_input('Username')
55 | name = st.text_input('Name')
56 | email = st.text_input('Email')
57 | password = st.text_input('Password', type='password')
58 | role = st.selectbox('Role', ['admin', 'user'])
59 | submit = st.form_submit_button('Add User')
60 |
61 |
62 | if submit:
63 | user = User(username=username, name=name, email=email, password=password,
64 | role=role)
65 | engine = create_connection(echo=True)
66 | with get_session(engine) as session:
67 | session.add(user)
68 | session.commit()
69 | st.toast(f'User {username} added successfully')
70 |
71 | # rerun
72 | st.rerun()
73 |
74 | # add a form to delete users
75 | with st.form(key='delete_user'):
76 | user_id = st.text_input('User ID')
77 | submit = st.form_submit_button('Delete User')
78 |
79 | if submit:
80 | engine = create_connection(echo=True)
81 | with get_session(engine) as session:
82 | user = session.get(User, user_id)
83 | session.delete(user)
84 | session.commit()
85 | st.toast(f'User {user_id} deleted successfully')
86 |
87 | # rerun
88 | st.rerun()
89 |
--------------------------------------------------------------------------------
/fibsem/db/v3/notebook.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "## FIBSEM Database (SQLModel Version)"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": null,
13 | "metadata": {},
14 | "outputs": [],
15 | "source": [
16 | "%load_ext autoreload\n",
17 | "%autoreload 2\n",
18 | "\n",
19 | "from fibsem.db.v3 import models\n",
20 | "\n",
21 | "user = models.User(username='patrick', \n",
22 | " name='Patrick Cleeve',\n",
23 | " email = 'patrick@openfibsem.org',\n",
24 | " password = 'password',\n",
25 | " role = 'admin')\n",
26 | "\n",
27 | "print(user.username)\n",
28 | "\n",
29 | "\n"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": null,
35 | "metadata": {},
36 | "outputs": [],
37 | "source": [
38 | "from sqlmodel import Session, SQLModel, create_engine, select\n",
39 | "\n",
40 | "from fibsem import config as cfg\n",
41 | "\n",
42 | "def create_connection(database_path: str = cfg.DATABASE_PATH, echo: bool = False):\n",
43 | " # create a connection to the database\n",
44 | " engine = create_engine(f'sqlite:///{database_path}', echo=echo)\n",
45 | "\n",
46 | " return engine\n",
47 | "\n",
48 | "def create_database(engine):\n",
49 | " # create the database\n",
50 | " SQLModel.metadata.create_all(engine)\n",
51 | "\n",
52 | "engine = create_connection(echo=True)\n",
53 | "create_database(engine)\n",
54 | "\n",
55 | "# with Session(engine) as session:\n",
56 | " # session.add(user)\n",
57 | " # session.commit()"
58 | ]
59 | },
60 | {
61 | "cell_type": "code",
62 | "execution_count": null,
63 | "metadata": {},
64 | "outputs": [],
65 | "source": [
66 | "with Session(engine) as session:\n",
67 | " statement = select(models.User)\n",
68 | " users = session.exec(statement)\n",
69 | " for user in users:\n",
70 | " print(user)"
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "execution_count": null,
76 | "metadata": {},
77 | "outputs": [],
78 | "source": []
79 | }
80 | ],
81 | "metadata": {
82 | "kernelspec": {
83 | "display_name": "fibsem",
84 | "language": "python",
85 | "name": "python3"
86 | },
87 | "language_info": {
88 | "codemirror_mode": {
89 | "name": "ipython",
90 | "version": 3
91 | },
92 | "file_extension": ".py",
93 | "mimetype": "text/x-python",
94 | "name": "python",
95 | "nbconvert_exporter": "python",
96 | "pygments_lexer": "ipython3",
97 | "version": "3.9.18"
98 | }
99 | },
100 | "nbformat": 4,
101 | "nbformat_minor": 2
102 | }
103 |
--------------------------------------------------------------------------------
/fibsem/db/v3/util.py:
--------------------------------------------------------------------------------
1 | from sqlmodel import Session, SQLModel, create_engine, select
2 | from fibsem import config as cfg
3 |
4 | def create_connection(database_path: str = cfg.DATABASE_PATH, echo: bool = False):
5 | # create a connection to the database
6 | engine = create_engine(f'sqlite:///{database_path}', echo=echo)
7 |
8 | return engine
9 |
10 | def create_database(engine):
11 | # create the database
12 | SQLModel.metadata.create_all(engine)
13 |
14 |
15 | def get_session(engine):
16 | # create a session
17 | return Session(engine)
18 |
19 |
20 | def get_all(model, session):
21 | # get all the records from the model
22 | return session.exec(select(model)).all()
23 |
24 |
25 | def get_by_id(model, session, id):
26 | return session.get(model, id)
27 |
28 |
29 |
--------------------------------------------------------------------------------
/fibsem/detection/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/__init__.py
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-liftout-hf-mega.yaml:
--------------------------------------------------------------------------------
1 | data_path: "/home/patrick/github/data/autolamella-paper/model-development/train/liftout/test/keypoints.csv" # test data csv
2 | images_path: "/home/patrick/github/data/autolamella-paper/model-development/train/liftout/test" # test data image directory
3 | save_path: "/home/patrick/github/data/autolamella-paper/model-development/eval/hf-mega-eval/liftout/keypoint-eval" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: autolamella-liftout-20240107.pt
7 | - checkpoint: autolamella-mega-20240107.pt
8 | - checkpoint: patrickcleeve/segformer-b1-autolamella-mega-1
9 |
10 | # plot
11 | thresholds:
12 | - 250
13 | - 100
14 | - 50
15 | - 25
16 | - 10
17 |
18 | # options
19 | run_eval: True
20 | plot_eval: True
21 |
22 | show_det_plot: False
23 | save_det_plot: True
24 | show_eval_plot: False
25 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-liftout.yaml:
--------------------------------------------------------------------------------
1 | data_path: "/home/patrick/github/data/autolamella-paper/model-development/train/liftout/test/keypoints.csv" # test data csv
2 | images_path: "/home/patrick/github/data/autolamella-paper/model-development/train/liftout/test" # test data image directory
3 | save_path: "/home/patrick/github/data/autolamella-paper/model-development/eval/liftout-v1/keypoint-eval-v2" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: autolamella-liftout-20240107.pt
7 | - checkpoint: autolamella-mega-20240107.pt
8 |
9 | # plot
10 | thresholds:
11 | - 250
12 | - 100
13 | - 50
14 | - 25
15 | - 10
16 |
17 | # options
18 | run_eval: True
19 | plot_eval: True
20 |
21 | show_det_plot: False
22 | save_det_plot: True
23 | show_eval_plot: False
24 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-serial-liftout-hf-mega.yaml:
--------------------------------------------------------------------------------
1 | data_path: "/home/patrick/github/data/autolamella-paper/model-development/train/serial-liftout/test/keypoints.csv" # test data csv
2 | images_path: "/home/patrick/github/data/autolamella-paper/model-development/train/serial-liftout/test" # test data image directory
3 | save_path: "/home/patrick/github/data/autolamella-paper/model-development/eval/hf-mega-eval/sl/keypoint-eval" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: autolamella-serial-liftout-20240107.pt
7 | - checkpoint: autolamella-mega-20240107.pt
8 | - checkpoint: patrickcleeve/segformer-b1-autolamella-mega-1
9 |
10 |
11 | # plot
12 | thresholds:
13 | - 250
14 | - 100
15 | - 50
16 | - 25
17 | - 10
18 |
19 | # options
20 | run_eval: True
21 | plot_eval: True
22 |
23 | show_det_plot: False
24 | save_det_plot: True
25 | show_eval_plot: False
26 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-serial-liftout.yaml:
--------------------------------------------------------------------------------
1 | data_path: "/home/patrick/github/data/autolamella-paper/model-development/train/serial-liftout/test/keypoints.csv" # test data csv
2 | images_path: "/home/patrick/github/data/autolamella-paper/model-development/train/serial-liftout/test" # test data image directory
3 | save_path: "/home/patrick/github/data/autolamella-paper/model-development/eval/serial-liftout-v1/keypoint-eval" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: autolamella-serial-liftout-20240107.pt
7 | - checkpoint: autolamella-mega-20240107.pt
8 |
9 |
10 | # plot
11 | thresholds:
12 | - 250
13 | - 100
14 | - 50
15 | - 25
16 | - 10
17 |
18 | # options
19 | run_eval: True
20 | plot_eval: True
21 |
22 | show_det_plot: False
23 | save_det_plot: True
24 | show_eval_plot: False
25 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-waffle-v2-hf-mega.yaml:
--------------------------------------------------------------------------------
1 | data_path: "/home/patrick/github/data/autolamella-paper/model-development/train/waffle/test/keypoints.csv" # test data csv
2 | images_path: "/home/patrick/github/data/autolamella-paper/model-development/train/waffle/test" # test data image directory
3 | save_path: "/home/patrick/github/data/autolamella-paper/model-development/eval/hf-mega-eval/waffle/keypoint-eval" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: autolamella-waffle-20240107.pt
7 | - checkpoint: autolamella-mega-20240107.pt
8 | - checkpoint: patrickcleeve/segformer-b1-autolamella-mega-1
9 |
10 |
11 | # plot
12 | thresholds:
13 | - 250
14 | - 100
15 | - 50
16 | - 25
17 | - 10
18 |
19 | # options
20 | run_eval: True
21 | plot_eval: True
22 |
23 | show_det_plot: False
24 | save_det_plot: True
25 | show_eval_plot: False
26 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-waffle-v2.yaml:
--------------------------------------------------------------------------------
1 | data_path: "/home/patrick/github/data/autolamella-paper/model-development/train/waffle/test/keypoints.csv" # test data csv
2 | images_path: "/home/patrick/github/data/autolamella-paper/model-development/train/waffle/test" # test data image directory
3 | save_path: "/home/patrick/github/data/autolamella-paper/model-development/eval/waffle-v1/keypoint-eval" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: autolamella-waffle-20240107.pt
7 | - checkpoint: autolamella-mega-20240107.pt
8 |
9 |
10 | # plot
11 | thresholds:
12 | - 250
13 | - 100
14 | - 50
15 | - 25
16 | - 10
17 |
18 | # options
19 | run_eval: True
20 | plot_eval: True
21 |
22 | show_det_plot: False
23 | save_det_plot: True
24 | show_eval_plot: False
25 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autolamella-waffle.yml:
--------------------------------------------------------------------------------
1 | data_path: "eval/autolamella-waffle/data.csv" # test data csv
2 | images_path: "eval/autolamella-waffle/images" # test images
3 | save_path: "eval/autolamella-waffle/eval" # save path for evaluation results
4 |
5 | checkpoints:
6 | - checkpoint: "openfibsem-baseline-34.pt"
7 | encoder: "resnet34"
8 | nc: 3
9 | - checkpoint: "autolamella-02-34.pt"
10 | encoder: "resnet34"
11 | nc: 3
12 | - checkpoint: "autolamella-04-34.pt"
13 | encoder: "resnet34"
14 | nc: 3
15 | - checkpoint: "autolamella-05-34.pt"
16 | encoder: "resnet34"
17 | nc: 3
18 |
19 | # plot
20 | thresholds:
21 | - 250
22 | - 100
23 | - 50
24 | - 25
25 | - 10
26 |
27 | # options
28 | run_eval: True
29 | plot_eval: True
30 |
31 | show_det_plot: False
32 | save_det_plot: True
33 | show_eval_plot: False
34 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/config-autoliftout-dm-embryo.yml:
--------------------------------------------------------------------------------
1 | data_path: "eval/dm-embryo/data.csv" # test data csv
2 | images_path: "eval/dm-embryo/images" # test data image directory
3 | save_path: "eval/dm-embryo/eval" # save path for evaluation results
4 |
5 | checkpoints: # list of checkpoints to evaluate
6 | - checkpoint: "openfibsem-01-18.pt"
7 | encoder: "resnet18"
8 | nc: 3
9 | - checkpoint: "openfibsem-02-18.pt"
10 | encoder: "resnet18"
11 | nc: 3
12 | - checkpoint: "openfibsem-03-18.pt"
13 | encoder: "resnet18"
14 | nc: 3
15 | - checkpoint: "openfibsem-baseline-34.pt"
16 | encoder: "resnet34"
17 | nc: 3
18 |
19 | # plot
20 | thresholds:
21 | - 250
22 | - 100
23 | - 50
24 | - 25
25 | - 10
26 |
27 | # options
28 | run_eval: False
29 | plot_eval: True
30 |
31 | show_det_plot: False
32 | save_det_plot: True
33 | show_eval_plot: False
34 | save_eval_plot: True
--------------------------------------------------------------------------------
/fibsem/detection/run_evaluation.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 |
4 | import pandas as pd
5 | import yaml
6 |
7 | from fibsem.detection import evaluation
8 |
9 | def main(config: dict):
10 |
11 | os.makedirs(config["save_path"], exist_ok=True)
12 |
13 | if config["run_eval"]:
14 | df_eval = evaluation.run_evaluation_v2(path=config["data_path"],
15 | image_path = config["images_path"],
16 | checkpoints=config["checkpoints"],
17 | plot=config["show_det_plot"],
18 | save=config["save_det_plot"],
19 | save_path=config["save_path"])
20 | else:
21 | df_eval = pd.read_csv(os.path.join(config["save_path"], "eval.csv"))
22 |
23 | if config["plot_eval"]:
24 | category_orders = {"checkpoint": df_eval["checkpoint"].unique().tolist(),
25 | "feature": sorted(df_eval["feature"].unique().tolist())}
26 | evaluation.plot_evaluation_data(df=df_eval,
27 | category_orders=category_orders,
28 | thresholds=config["thresholds"],
29 | show=config["show_eval_plot"],
30 | save=config["save_eval_plot"],
31 | save_path=config["save_path"])
32 |
33 |
34 |
35 | if __name__ == "__main__":
36 | # command line arguments
37 | parser = argparse.ArgumentParser()
38 | parser.add_argument(
39 | "--config",
40 | help="the directory containing the config file to use",
41 | dest="config",
42 | action="store",
43 | default=os.path.join(os.path.join(os.path.dirname(__file__), "config.yml")),
44 | )
45 | args = parser.parse_args()
46 | config_dir = args.config
47 |
48 | # NOTE: Setup your config.yml file
49 | with open(config_dir, "r") as f:
50 | config = yaml.safe_load(f)
51 |
52 | main(config=config)
--------------------------------------------------------------------------------
/fibsem/detection/test_image.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_image.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_image_2.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_image_2.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_00.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_00.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_01.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_01.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_02.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_02.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_03.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_03.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_04.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_04.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_05.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_05.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_06.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_06.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/serial/serial_liftout_mask_07.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/serial/serial_liftout_mask_07.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/test_needle_mask.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/test_needle_mask.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/test_needle_mask2.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/test_needle_mask2.tif
--------------------------------------------------------------------------------
/fibsem/detection/test_images/test_needle_mask3.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/detection/test_images/test_needle_mask3.tif
--------------------------------------------------------------------------------
/fibsem/imaging/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/imaging/.gitkeep
--------------------------------------------------------------------------------
/fibsem/imaging/__init__.py:
--------------------------------------------------------------------------------
1 | from fibsem.imaging.spot import *
2 | from fibsem.imaging.autogamma import *
3 | from fibsem.imaging.masks import *
4 | from fibsem.imaging.tiled import *
5 | from fibsem.imaging.utils import *
--------------------------------------------------------------------------------
/fibsem/imaging/spot.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import time
3 | from typing import List, Optional
4 |
5 | from fibsem.microscope import FibsemMicroscope
6 | from fibsem.structures import BeamType, Point
7 |
8 | SLEEP_TIME = 1
9 |
10 | def run_spot_burn(microscope: FibsemMicroscope,
11 | coordinates: List[Point],
12 | exposure_time: float,
13 | milling_current: float,
14 | beam_type: BeamType = BeamType.ION,
15 | parent_ui: Optional['FibsemSpotBurnWidget'] = None)-> None:
16 | """Run a spot burner job on the microscope. Exposes the specified coordinates for a the specified
17 | time at the specified current.
18 | Args:
19 | microscope: The microscope object.
20 | coordinates: List of points to burn. (0 - 1 in image coordinates)
21 | exposure_time: Time to expose each point in seconds.
22 | milling_current: Current to use for the spot.
23 | beam_type: The type of beam to use. (Default: BeamType.ION)
24 | parent_ui: The parent UI object to emit progress signals. (Default: None)
25 | Returns:
26 | None
27 | """
28 | # - TODO: support cancelling the task
29 | # - QUERY: do we need to set the full frame scanning mode each time, or only at the end?
30 |
31 | total_estimated_time = len(coordinates) * exposure_time
32 | total_remaining_time = total_estimated_time
33 |
34 | # emit initial progress signal
35 | if parent_ui is not None:
36 | parent_ui.spot_burn_progress_signal.emit(
37 | {
38 | "current_point": 0,
39 | "total_points": len(coordinates),
40 | "remaining_time": exposure_time,
41 | "total_remaining_time": total_remaining_time,
42 | "total_estimated_time": total_estimated_time,
43 | }
44 | )
45 |
46 | # set the beam current to the milling current
47 | imaging_current = microscope.get_beam_current(beam_type=beam_type)
48 | microscope.set_beam_current(current=milling_current, beam_type=beam_type)
49 |
50 | for i, pt in enumerate(coordinates, 1):
51 | logging.info(f'burning spot {i}: {pt}, exposure time: {exposure_time}, milling current: {milling_current}')
52 |
53 | microscope.blank(beam_type=beam_type)
54 | microscope.set_spot_scanning_mode(point=pt, beam_type=beam_type)
55 | microscope.unblank(beam_type=beam_type)
56 |
57 | # countdown for the exposure time, emit progress signal
58 | remaining_time = exposure_time
59 | while remaining_time > 0:
60 | time.sleep(SLEEP_TIME)
61 | remaining_time -= SLEEP_TIME
62 | total_remaining_time -= SLEEP_TIME
63 | if parent_ui is not None:
64 | parent_ui.spot_burn_progress_signal.emit(
65 | {
66 | "current_point": i,
67 | "total_points": len(coordinates),
68 | "remaining_time": remaining_time,
69 | "total_remaining_time": total_remaining_time,
70 | "total_estimated_time": total_estimated_time,
71 | }
72 | )
73 | microscope.set_full_frame_scanning_mode(beam_type=beam_type)
74 |
75 | # emit finished signal
76 | if parent_ui is not None:
77 | parent_ui.spot_burn_progress_signal.emit({"finished": True})
78 |
79 | microscope.set_beam_current(current=imaging_current, beam_type=beam_type)
80 | return
81 |
--------------------------------------------------------------------------------
/fibsem/imaging/utils.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from fibsem.structures import Point, FibsemImage
3 | from PIL import Image
4 |
5 |
6 | def create_distance_map_px(w: int, h: int) -> np.ndarray:
7 | x = np.arange(0, w)
8 | y = np.arange(0, h)
9 |
10 | X, Y = np.meshgrid(x, y)
11 | distance = np.sqrt(((w / 2) - X) ** 2 + ((h / 2) - Y) ** 2)
12 |
13 | return distance
14 |
15 |
16 | def measure_brightness(img: FibsemImage) -> float:
17 |
18 | return np.mean(img.data)
19 |
20 |
21 | def rotate_image(image: FibsemImage):
22 | """Rotate the AdornedImage 180 degrees."""
23 | data = np.rot90(np.rot90(np.copy(image.data)))
24 | reference = FibsemImage(data=data, metadata=image.metadata)
25 | return reference
26 |
27 |
28 | def normalise_image(img: FibsemImage) -> np.ndarray:
29 | """Normalise the image"""
30 | return (img.data - np.mean(img.data)) / np.std(img.data)
31 |
32 |
33 | def cosine_stretch(img: FibsemImage, tilt_degrees: float):
34 | """Apply a cosine stretch to an image based on the relative tilt.
35 |
36 | This is required when aligning images with different tilts to ensure features are the same size.
37 |
38 | Args:
39 | img (AdornedImage): _description_
40 | tilt_degrees (float): _description_
41 |
42 | Returns:
43 | _type_: _description_
44 | """
45 | # note: do smaller version for negative tilt??
46 |
47 | tilt = np.deg2rad(tilt_degrees)
48 |
49 | shape = int(img.data.shape[0] / np.cos(tilt)), int(img.data.shape[1] / np.cos(tilt))
50 |
51 | # cosine stretch
52 | # larger
53 | resized_img = np.asarray(
54 | Image.fromarray(img.data).resize(size=(shape[1], shape[0]))
55 | )
56 |
57 | # crop centre out?
58 | c = Point(resized_img.shape[1] // 2, resized_img.shape[0] // 2)
59 | dy, dx = img.data.shape[0] // 2, img.data.shape[1] // 2
60 | scaled_img = resized_img[c.y - dy : c.y + dy, c.x - dx : c.x + dx]
61 |
62 | return FibsemImage(data=scaled_img, metadata=img.metadata)
63 |
64 |
65 | def apply_image_mask(img: FibsemImage, mask: np.ndarray) -> np.ndarray:
66 |
67 | return normalise_image(img) * mask
68 |
--------------------------------------------------------------------------------
/fibsem/milling/__init__.py:
--------------------------------------------------------------------------------
1 | from fibsem.milling.base import (
2 | FibsemMillingStage,
3 | MillingStrategy,
4 | MillingAlignment,
5 | get_milling_stages,
6 | get_protocol_from_stages,
7 | get_strategy,
8 | estimate_milling_time,
9 | estimate_total_milling_time,
10 | )
11 | from fibsem.milling.core import (
12 | draw_pattern,
13 | draw_patterns,
14 | finish_milling,
15 | mill_stages,
16 | run_milling,
17 | setup_milling,
18 | )
19 | from fibsem.milling.patterning.plotting import draw_milling_patterns as plot_milling_patterns
20 |
--------------------------------------------------------------------------------
/fibsem/milling/config.py:
--------------------------------------------------------------------------------
1 | # sputtering rates, from microscope application files
2 | MILLING_SPUTTER_RATE = {
3 | 20e-12: 6.85e-3, # 30kv
4 | 0.2e-9: 6.578e-2, # 30kv
5 | 0.74e-9: 3.349e-1, # 30kv
6 | 0.89e-9: 3.920e-1, # 20kv
7 | 2.0e-9: 9.549e-1, # 30kv
8 | 2.4e-9: 1.309, # 20kv
9 | 6.2e-9: 2.907, # 20kv
10 | 7.6e-9: 3.041, # 30kv
11 | 28.0e-9: 1.18e1, # 30 kv
12 | }
13 |
--------------------------------------------------------------------------------
/fibsem/milling/strategy/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import typing
3 | from functools import cache
4 |
5 | from fibsem.milling.base import MillingStrategy
6 | from fibsem.milling.strategy.standard import StandardMillingStrategy
7 | from fibsem.milling.strategy.overtilt import OvertiltTrenchMillingStrategy
8 |
9 |
10 | DEFAULT_STRATEGY = StandardMillingStrategy
11 | DEFAULT_STRATEGY_NAME = DEFAULT_STRATEGY.name
12 | BUILTIN_STRATEGIES: typing.Dict[str, type[MillingStrategy]] = {
13 | StandardMillingStrategy.name: StandardMillingStrategy,
14 | OvertiltTrenchMillingStrategy.name: OvertiltTrenchMillingStrategy,
15 | }
16 | REGISTERED_STRATEGIES: typing.Dict[str, type[MillingStrategy]] = {}
17 |
18 |
19 | def get_strategies() -> typing.Dict[str, type[MillingStrategy]]:
20 | # This order means that builtins > registered > plugins if there are any name clashes
21 | return {**_get_plugin_strategies(), **REGISTERED_STRATEGIES, **BUILTIN_STRATEGIES}
22 |
23 |
24 | def get_strategy_names() -> typing.List[str]:
25 | return list(get_strategies().keys())
26 |
27 |
28 | def register_strategy(strategy_cls: type[MillingStrategy]) -> None:
29 | global REGISTERED_STRATEGIES
30 | REGISTERED_STRATEGIES[strategy_cls.name] = strategy_cls
31 |
32 |
33 | @cache
34 | def _get_plugin_strategies() -> typing.Dict[str, type[MillingStrategy]]:
35 | """
36 | Import new strategies and append them to the list here
37 |
38 | The plugin logic is based on:
39 | https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-package-metadata
40 | """
41 | import sys
42 |
43 | if sys.version_info < (3, 10):
44 | from importlib_metadata import entry_points
45 | else:
46 | from importlib.metadata import entry_points
47 |
48 | strategies: typing.Dict[str, type[MillingStrategy]] = {}
49 | for strategy_entry_point in entry_points(group="fibsem.strategies"):
50 | try:
51 | strategy = strategy_entry_point.load()
52 | if not issubclass(strategy, MillingStrategy):
53 | raise TypeError(
54 | f"'{strategy_entry_point.value}' is not a subclass of MillingStrategy"
55 | )
56 | logging.info("Loaded strategy '%s'", strategy.name)
57 | strategies[strategy.name] = strategy
58 | except TypeError as e:
59 | logging.warning("Invalid strategy found: %s", str(e))
60 | except Exception:
61 | logging.error(
62 | "Unexpected error raised while attempting to import strategy from '%s'",
63 | strategy_entry_point.value,
64 | exc_info=True,
65 | )
66 | return strategies
67 |
--------------------------------------------------------------------------------
/fibsem/milling/strategy/standard.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from dataclasses import dataclass
3 |
4 | from fibsem.microscope import FibsemMicroscope
5 | from fibsem.milling import (draw_patterns, run_milling,
6 | setup_milling)
7 | from fibsem.milling.base import (FibsemMillingStage, MillingStrategy,
8 | MillingStrategyConfig)
9 |
10 | import time
11 |
12 | @dataclass
13 | class StandardMillingConfig(MillingStrategyConfig):
14 | """Configuration for standard milling strategy"""
15 | pass
16 |
17 |
18 | @dataclass
19 | class StandardMillingStrategy(MillingStrategy):
20 | """Basic milling strategy that mills continuously until completion"""
21 | name: str = "Standard"
22 | fullname: str = "Standard Milling"
23 |
24 | def __init__(self, config: StandardMillingConfig = None):
25 | self.config = config or StandardMillingConfig()
26 |
27 | def to_dict(self):
28 | return {"name": self.name, "config": self.config.to_dict()}
29 |
30 | @staticmethod
31 | def from_dict(d: dict) -> "StandardMillingStrategy":
32 | config=StandardMillingConfig.from_dict(d.get("config", {}))
33 | return StandardMillingStrategy(config=config)
34 |
35 | def run(
36 | self,
37 | microscope: FibsemMicroscope,
38 | stage: FibsemMillingStage,
39 | asynch: bool = False,
40 | parent_ui = None,
41 | ) -> None:
42 | logging.info(f"Running {self.name} Milling Strategy for {stage.name}")
43 | setup_milling(microscope, milling_stage=stage, ref_image=stage.ref_image)
44 |
45 | draw_patterns(microscope, stage.pattern.define())
46 |
47 | estimated_time = microscope.estimate_milling_time()
48 | logging.info(f"Estimated time for {stage.name}: {estimated_time:.2f} seconds")
49 |
50 | if parent_ui:
51 | parent_ui.milling_progress_signal.emit({"msg": f"Running {stage.name}...",
52 | "progress":
53 | {"started": True,
54 | "start_time": time.time(),
55 | "estimated_time": estimated_time,
56 | "name": stage.name}
57 | })
58 |
59 | run_milling(
60 | microscope=microscope,
61 | milling_current=stage.milling.milling_current,
62 | milling_voltage=stage.milling.milling_voltage,
63 | asynch=asynch,
64 | )
65 |
--------------------------------------------------------------------------------
/fibsem/movement.py:
--------------------------------------------------------------------------------
1 |
2 | import numpy as np
3 |
4 |
5 | def rotation_angle_is_larger(angle1: float, angle2: float, atol: float = 90) -> bool:
6 | """Check the rotation angles are large
7 |
8 | Args:
9 | angle1 (float): angle1 (radians)
10 | angle2 (float): angle2 (radians)
11 | atol : tolerance (degrees)
12 |
13 | Returns:
14 | bool: rotation angle is larger than atol
15 | """
16 |
17 | return angle_difference(angle1, angle2) > (np.deg2rad(atol))
18 |
19 |
20 | def rotation_angle_is_smaller(angle1: float, angle2: float, atol: float = 5) -> bool:
21 | """Check the rotation angles are large
22 |
23 | Args:
24 | angle1 (float): angle1 (radians)
25 | angle2 (float): angle2 (radians)
26 | atol : tolerance (degrees)
27 |
28 | Returns:
29 | bool: rotation angle is smaller than atol
30 | """
31 |
32 | return angle_difference(angle1, angle2) < (np.deg2rad(atol))
33 |
34 |
35 | def angle_difference(angle1: float, angle2: float) -> float:
36 | """Return the difference between two angles, accounting for greater than 360, less than 0 angles
37 |
38 | Args:
39 | angle1 (float): angle1 (radians)
40 | angle2 (float): angle2 (radians)
41 |
42 | Returns:
43 | float: _description_
44 | """
45 | angle1 %= 2 * np.pi
46 | angle2 %= 2 * np.pi
47 |
48 | large_angle = np.max([angle1, angle2])
49 | small_angle = np.min([angle1, angle2])
50 |
51 | return min((large_angle - small_angle), ((2 * np.pi + small_angle - large_angle)))
52 |
--------------------------------------------------------------------------------
/fibsem/segmentation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/__init__.py
--------------------------------------------------------------------------------
/fibsem/segmentation/adaptive_model.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import numpy as np
3 | import torch
4 | from adaptive_polish.dl_segmentation import sem_lamella_segmentor as sgm
5 | from fibsem.segmentation.utils import decode_segmap_v2, download_checkpoint
6 |
7 |
8 | class AdaptiveSegmentationModel:
9 | def __init__(
10 | self,
11 | checkpoint: str = None,
12 | mode: str = "eval",
13 | num_classes: int = 4,
14 | ) -> None:
15 | super().__init__()
16 | self.checkpoint: str = checkpoint
17 | self.mode = mode
18 | self.num_classes = num_classes
19 | self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
20 | self.model = self.load_model_v2(checkpoint=checkpoint)
21 | self.device = self.model._device
22 |
23 | def load_model_v2(self, checkpoint: str):
24 | model = sgm.cSEMLamellaSegmentor(model_path=checkpoint)
25 | logging.debug(f"Loaded {self.__class__}, model from {checkpoint}")
26 | return model
27 |
28 | def pre_process(self, img: np.ndarray) -> torch.Tensor:
29 | """Pre-process the image for inference"""
30 | return img
31 |
32 | def inference(self, img: np.ndarray, rgb: bool = True) -> np.ndarray:
33 | """Run model inference on the input image"""
34 |
35 | # NOTE: currently all pre-processing is done in adaptive_polish.sem_lamella_segmentor.cSEMLamellaSegmentor
36 | # TODO: migrate pre-processing to this class
37 | masks = self.model.get_prediction(img)
38 | # decode to rgb
39 | if rgb:
40 | masks = self.postprocess(masks)
41 | return masks
42 |
43 | def postprocess(self, masks: np.ndarray) -> np.ndarray:
44 | return decode_segmap_v2(masks)
45 |
--------------------------------------------------------------------------------
/fibsem/segmentation/config-autolamella-mega-v4-xl.yml:
--------------------------------------------------------------------------------
1 | data_paths: [
2 | /home/patrick/github/data/autolamella/train,
3 | /home/patrick/github/data/liftout/training/train-relabelled/images,
4 | /home/patrick/github/data/liftout/train-new/train,
5 | /home/patrick/github/data/liftout/train-waffle/train,
6 | /home/patrick/github/data/liftout/serial-liftout/data/train
7 | ]
8 | label_paths: [
9 | /home/patrick/github/data/autolamella/train/labels,
10 | /home/patrick/github/data/liftout/training/train-relabelled/labels,
11 | /home/patrick/github/data/liftout/train-new/train/labels,
12 | /home/patrick/github/data/liftout/train-waffle/train/labels,
13 | /home/patrick/github/data/liftout/serial-liftout/data/train/labels
14 | ]
15 | save_path: /home/patrick/github/fibsem/fibsem/segmentation/models/autolamella/mega/base/v4/xl
16 | checkpoint: null
17 | encoder: "resnet50"
18 | epochs: 50
19 | batch_size: 2
20 | num_classes: 5
21 | lr: 3.0e-4
22 | wandb: true
23 | wandb_project: "autolamella-mega"
24 | wandb_entity: "demarcolab"
25 | # re-labelled data, fixed numerical bug in pre-processing
--------------------------------------------------------------------------------
/fibsem/segmentation/config-autolamella-mega-v4.yml:
--------------------------------------------------------------------------------
1 | data_paths: [
2 | /home/patrick/github/data/autolamella/train,
3 | /home/patrick/github/data/liftout/training/train-relabelled/images,
4 | /home/patrick/github/data/liftout/train-new/train,
5 | /home/patrick/github/data/liftout/train-waffle/train,
6 | /home/patrick/github/data/liftout/serial-liftout/data/train
7 | ]
8 | label_paths: [
9 | /home/patrick/github/data/autolamella/train/labels,
10 | /home/patrick/github/data/liftout/training/train-relabelled/labels,
11 | /home/patrick/github/data/liftout/train-new/train/labels,
12 | /home/patrick/github/data/liftout/train-waffle/train/labels,
13 | /home/patrick/github/data/liftout/serial-liftout/data/train/labels
14 | ]
15 | save_path: /home/patrick/github/fibsem/fibsem/segmentation/models/autolamella/mega/base/v4
16 | checkpoint: null
17 | encoder: "resnet34"
18 | epochs: 50
19 | batch_size: 4
20 | num_classes: 5
21 | lr: 3.0e-4
22 | wandb: true
23 | wandb_project: "autolamella-mega"
24 | wandb_entity: "demarcolab"
25 | # re-labelled data, fixed numerical bug in pre-processing
--------------------------------------------------------------------------------
/fibsem/segmentation/config-autolamella-mega-v5.yml:
--------------------------------------------------------------------------------
1 | data_paths: [
2 | /home/patrick/github/data/autolamella-paper/model-development/train/autolamella-waffle/train,
3 | /home/patrick/github/data/autolamella-paper/model-development/train/autoliftout-04/train-relabelled/images,
4 | /home/patrick/github/data/autolamella-paper/model-development/train/autoliftout-04/train-new/train,
5 | /home/patrick/github/data/autolamella-paper/model-development/train/autoliftout-04/train-waffle/train,
6 | /home/patrick/github/data/autolamella-paper/model-development/train/serial-liftout/train
7 | ]
8 | label_paths: [
9 | /home/patrick/github/data/autolamella-paper/model-development/train/autolamella-waffle/train/labels,
10 | /home/patrick/github/data/autolamella-paper/model-development/train/autoliftout-04/train-relabelled/labels,
11 | /home/patrick/github/data/autolamella-paper/model-development/train/autoliftout-04/train-new/train/labels,
12 | /home/patrick/github/data/autolamella-paper/model-development/train/autoliftout-04/train-waffle/train/labels,
13 | /home/patrick/github/data/autolamella-paper/model-development/train/serial-liftout/train/labels
14 | ]
15 | save_path: /home/patrick/github/fibsem/fibsem/segmentation/models/autolamella/mega/base/v5
16 | checkpoint: null
17 | encoder: "resnet34"
18 | epochs: 50
19 | split: 0.1
20 | batch_size: 2
21 | num_classes: 6
22 | lr: 3.0e-4
23 | train_log_freq: 32
24 | val_log_freq: 32
25 | wandb: true
26 | wandb_project: "autolamella-mega"
27 | wandb_entity: "openfibsem"
28 |
29 |
30 |
--------------------------------------------------------------------------------
/fibsem/segmentation/config-autolamella-waffle4.yml:
--------------------------------------------------------------------------------
1 | data_path: /home/patrick/github/data/autolamella/train
2 | label_path: /home/patrick/github/data/autolamella/train/labels
3 | save_path: /home/patrick/github/fibsem/fibsem/segmentation/models/autolamella/v4
4 | checkpoint: null
5 | encoder: "resnet34"
6 | epochs: 20
7 | batch_size: 4
8 | num_classes: 3
9 | lr: 3.0e-4
10 | wandb: true
11 | wandb_project: "autolamella"
12 | wandb_entity: "demarcolab"
13 |
--------------------------------------------------------------------------------
/fibsem/segmentation/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | from typing import List
3 | CLASS_CONFIG_PATH = os.path.join(os.path.dirname(__file__), "segmentation_config.yaml")
4 |
5 | import yaml
6 | with open(CLASS_CONFIG_PATH) as f:
7 | CLASS_CONFIG = yaml.load(f, Loader=yaml.FullLoader)
8 |
9 | CLASS_COLORS = CLASS_CONFIG["CLASS_COLORS"]
10 | CLASS_LABELS = CLASS_CONFIG["CLASS_LABELS"]
11 |
12 | import matplotlib.colors as mcolors
13 | def convert_color_names_to_rgb(color_names: List[str]):
14 | if isinstance(color_names, dict):
15 | color_names = color_names.values()
16 | rgb_colors = [mcolors.to_rgb(color) for color in color_names]
17 | # Convert to 0-255 scale
18 | rgb_colors = [(int(r*255), int(g*255), int(b*255)) for r, g, b in rgb_colors]
19 | return rgb_colors
20 |
21 | # map color names to rgb values
22 | CLASS_COLORS_RGB = convert_color_names_to_rgb(CLASS_COLORS.values())
23 |
24 |
25 | def get_colormap():
26 | return CLASS_COLORS_RGB
27 |
28 | def get_class_labels():
29 | return CLASS_LABELS
30 |
31 | CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.yml")
--------------------------------------------------------------------------------
/fibsem/segmentation/config.yml:
--------------------------------------------------------------------------------
1 | # data
2 | data_paths: [/path/to/data, /path/to/second/data] # paths to image data (multiple supported)
3 | label_paths: [/path/to/data/labels, /path/to/second/data/labels] # paths to label data (multiple supported)
4 | save_path: /path/to/save/checkpoints # path to save checkpoints (checkpointed each epoch)
5 | checkpoint: null # checkpoint to resume from
6 |
7 | # model
8 | encoder: "resnet34" # segmentation model encoder (imagenet)
9 | num_classes: 6 # number of classes
10 |
11 | # training
12 | epochs: 50 # number of epochs
13 | split: 0.1 # train / val split
14 | batch_size: 4 # batch size
15 | lr: 3.0e-4 # initial learning rate
16 | apply_transforms: true # apply data augmentation
17 |
18 | # logging
19 | train_log_freq: 32 # frequency to log training images
20 | val_log_freq: 32 # frequency to log validation images
21 |
22 | # wandb
23 | wandb: true # enable wandb logging
24 | wandb_project: "autolamella-mega" # wandb project
25 | wandb_entity: "openfibsem" # wandb user / org
26 | model_type: "mega-model" # model type note (descriptive only)
27 | note: "notes about this specific training run" # additional trianing note (descriptive only)
--------------------------------------------------------------------------------
/fibsem/segmentation/docs/example_napari.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/docs/example_napari.png
--------------------------------------------------------------------------------
/fibsem/segmentation/docs/imgs/combined/combined.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/docs/imgs/combined/combined.jpg
--------------------------------------------------------------------------------
/fibsem/segmentation/docs/imgs/labelled/label.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/docs/imgs/labelled/label.tif
--------------------------------------------------------------------------------
/fibsem/segmentation/docs/imgs/raw/image.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/docs/imgs/raw/image.tif
--------------------------------------------------------------------------------
/fibsem/segmentation/hf_segmentation_model.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import torch
3 |
4 | from PIL import Image
5 | from transformers import SegformerImageProcessor, SegformerForSemanticSegmentation
6 | from torch import nn
7 | from fibsem.segmentation import config as scfg
8 | from fibsem.segmentation.utils import decode_segmap_v2
9 |
10 | class SegmentationModelHuggingFace:
11 | """HuggingFace model for semantic segmentation"""
12 | def __init__(
13 | self,
14 | checkpoint: str = None,
15 | ) -> None:
16 | super().__init__()
17 |
18 | self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
19 | self.colormap = scfg.get_colormap()
20 |
21 | self.checkpoint = checkpoint
22 | if checkpoint is not None:
23 | self.load_model(checkpoint=checkpoint)
24 | self.num_classes = 6 # TODO: tmp required, remove
25 |
26 | def load_model(self, checkpoint: str) -> None:
27 | """Load the model, and optionally load a checkpoint"""
28 |
29 | self.processor = SegformerImageProcessor.from_pretrained(checkpoint)
30 | self.model = SegformerForSemanticSegmentation.from_pretrained(checkpoint)
31 |
32 | return self.model
33 |
34 | def pre_process(self, img: np.ndarray) -> np.ndarray:
35 | """Pre-process the image for model inference"""
36 |
37 | # assume image is 2D grayscale
38 | image = np.asarray(Image.fromarray(np.asarray(img)).convert("RGB"))
39 | inputs = self.processor(images=image, return_tensors="pt")
40 |
41 | return inputs
42 |
43 | def inference(self, img: np.ndarray, rgb: bool = True) -> np.ndarray:
44 | """Run model inference on the input image"""
45 |
46 | inputs = self.pre_process(img)
47 | outputs = self.model(**inputs)
48 | logits = outputs.logits # shape (batch_size, num_labels, height/4, width/4)
49 |
50 | # First, rescale logits to original image size
51 | upsampled_logits = nn.functional.interpolate(
52 | logits,
53 | size=img.shape, # (height, width)
54 | mode='bilinear',
55 | align_corners=False
56 | )
57 |
58 | # Second, apply argmax on the class dimension
59 | masks = upsampled_logits.argmax(dim=1).detach().cpu().numpy()
60 |
61 | # convert to rgb image
62 | if rgb:
63 | masks = decode_segmap_v2(masks[0], self.colormap) # 2d only
64 | else:
65 | if masks.ndim>=3:
66 | masks = masks[0] # return 2d
67 | return masks
68 |
69 | def postprocess(self, masks):
70 | """Convert the model output to a rgb class map"""
71 | if masks.ndim == 3:
72 | masks = masks[0]
73 | return decode_segmap_v2(masks, self.colormap)
74 |
75 |
--------------------------------------------------------------------------------
/fibsem/segmentation/inference.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import argparse
4 |
5 | # import matplotlib.pyplot as plt
6 | import numpy as np
7 | import segmentation_models_pytorch as smp
8 | from fibsem.segmentation import dataset, utils, validate_config
9 | import torch
10 | import os
11 | import wandb
12 | from PIL import Image
13 | import yaml
14 | import zarr
15 | import glob
16 | import tifffile as tff
17 | import time
18 |
19 | def inference(images, output_dir, model, model_path, device, WANDB=False):
20 | """Helper function for performing inference with the model"""
21 | # Load the model
22 | model.load_state_dict(torch.load(model_path))
23 | model = model.to(device)
24 | # model.eval()
25 |
26 | # Inference
27 | with torch.no_grad(): # TODO: move this down to only wrap the model inference
28 | vol = tff.imread(os.path.join(images, "*.tif*"), aszarr=True) # loading folder of .tif into zarr array)
29 | zarr_set = zarr.open(vol)
30 |
31 | filenames = sorted(glob.glob(os.path.join(images, "*.tif*")))
32 |
33 | for img, fname in zip(zarr_set, filenames):
34 | img = torch.tensor(np.asarray(img)).unsqueeze(0)
35 | img = img.to(device)
36 | outputs = model(img[None, :, :, :].float())
37 | output_mask = utils.decode_output(outputs)
38 |
39 | output = Image.fromarray(output_mask)
40 | path = os.path.join(output_dir, os.path.basename(fname).split(".")[0])
41 |
42 | # if not os.path.exists(path):
43 | os.makedirs(path, exist_ok=True)
44 |
45 | input_img = Image.fromarray(img.detach().cpu().squeeze().numpy())
46 | input_img.save(os.path.join(path, "input.tif"))
47 | output.save(os.path.join(path, "output.tif")) # or 'test.tif'
48 |
49 | if WANDB:
50 | img_base = img.detach().cpu().squeeze().numpy()
51 | img_rgb = np.dstack((img_base, img_base, img_base))
52 |
53 | wb_img = wandb.Image(img_rgb, caption="Input Image")
54 | wb_mask = wandb.Image(output_mask, caption="Output Mask")
55 | wandb.log({"image": wb_img, "mask": wb_mask})
56 |
57 | return outputs, output_mask
58 |
59 | if __name__ == "__main__":
60 | # command line arguments
61 | parser = argparse.ArgumentParser()
62 | parser.add_argument(
63 | "--config",
64 | help="the directory containing the config file to use",
65 | dest="config",
66 | action="store",
67 | default=os.path.join("fibsem", "segmentation", "config.yml"),
68 | )
69 | args = parser.parse_args()
70 | config_dir = args.config
71 |
72 | # NOTE: Setup your config.yml file
73 | with open(config_dir, 'r') as f:
74 | config = yaml.safe_load(f)
75 |
76 | print("Validating config file.")
77 | validate_config.validate_config(config, "inference")
78 |
79 | # directories
80 | data_path = config["inference"]["data_dir"]
81 | model_weights = config["inference"]["model_dir"]
82 | output_dir = config["inference"]["output_dir"]
83 |
84 | # other parameters
85 | cuda = config["inference"]["cuda"]
86 | WANDB = config["inference"]["wandb"]
87 |
88 |
89 | model = smp.Unet(
90 | encoder_name=config["inference"]["encoder"],
91 | encoder_weights="imagenet",
92 | in_channels=1, # grayscale images
93 | classes=config["inference"]["num_classes"], # background, needle, lamella
94 | )
95 |
96 | if WANDB:
97 | # weights and biases setup
98 | wandb.init(project=config["inference"]["wandb_project"], entity=config["inference"]["wandb_entity"])
99 |
100 | device = torch.device("cuda:0" if torch.cuda.is_available() and cuda else "cpu")
101 |
102 | inference(data_path, output_dir, model, model_weights, device, WANDB)
103 |
--------------------------------------------------------------------------------
/fibsem/segmentation/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/models/.gitkeep
--------------------------------------------------------------------------------
/fibsem/segmentation/nnunet_model.py:
--------------------------------------------------------------------------------
1 |
2 | from typing import Optional, List
3 |
4 | import numpy as np
5 | import torch
6 |
7 | from fibsem.segmentation.utils import decode_segmap_v2
8 |
9 | from pathlib import Path
10 | from fibsem.segmentation import config as scfg
11 | import os
12 | from huggingface_hub import hf_hub_download
13 |
14 |
15 | from fibsem.segmentation import _nnunet as nnunet
16 |
17 | # TODO: actually implement this
18 | from abc import ABC
19 | def SegmentationModelBase(ABC):
20 |
21 | def __init__(self, checkpoint: str):
22 | pass
23 |
24 |
25 | def load_model(self, checkpoint: str) -> SegmentationModelBase:
26 | """Load the model, and optionally load a checkpoint"""
27 | raise NotImplementedError
28 |
29 | def inference(self, img: np.ndarray, rgb: bool = True) -> np.ndarray:
30 | """Run model inference on the input image"""
31 | raise NotImplementedError
32 |
33 | class SegmentationModelNNUnet:
34 | def __init__(
35 | self,
36 | checkpoint: str = None,
37 | ) -> None:
38 | super().__init__()
39 |
40 | self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
41 | self.colormap = scfg.get_colormap()
42 |
43 | self.checkpoint = checkpoint
44 | if checkpoint is not None:
45 | self.load_model(checkpoint=checkpoint)
46 |
47 | def load_model(self, checkpoint: str) -> None:
48 | """Load the model, and optionally load a checkpoint"""
49 |
50 | self.model = nnunet.load_model(path=checkpoint, device=self.device)
51 |
52 | return self.model
53 |
54 | def pre_process(self, img: np.ndarray) -> np.ndarray:
55 | """Pre-process the image for model inference"""
56 |
57 | # convert to 4D
58 | if img.ndim == 2:
59 | img = img[np.newaxis, np.newaxis, :, :]
60 | elif img.ndim == 3:
61 | img = img[np.newaxis, :, :, :]
62 | elif img.ndim == 4:
63 | img = img[:, :, :, :]
64 | else:
65 | raise ValueError(f"Invalid image shape: {img.shape}")
66 |
67 | # TODO: also do dtype conversions
68 | if not isinstance(img.dtype, np.float32):
69 | img = img.astype(np.float32)
70 |
71 | return img
72 |
73 | def inference(self, img: np.ndarray, rgb: bool = True) -> np.ndarray:
74 | """Run model inference on the input image"""
75 |
76 | img = self.pre_process(img)
77 |
78 | masks, scores = nnunet.inference(self.model, img)
79 |
80 | # convert to rgb image
81 | if rgb:
82 | masks = decode_segmap_v2(masks[0], self.colormap) # 2d only
83 | else:
84 | if masks.ndim>=3:
85 | masks = masks[0] # return 2d
86 | return masks
87 |
88 | def postprocess(self, masks):
89 | """Convert the model output to a rgb class map"""
90 | if masks.ndim == 3:
91 | masks = masks[0]
92 | return decode_segmap_v2(masks, self.colormap)
93 |
94 |
--------------------------------------------------------------------------------
/fibsem/segmentation/requirements.txt:
--------------------------------------------------------------------------------
1 | segment-anything @ git+https://github.com/facebookresearch/segment-anything.git
2 | opencv-python
3 | pycocotools
4 | matplotlib
5 | onnxruntime
6 | onnx
--------------------------------------------------------------------------------
/fibsem/segmentation/sam_model.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import torch
3 | from transformers import SamModel, SamProcessor
4 | from typing import List, Tuple
5 |
6 | class SamModelWrapper:
7 | def __init__(self, checkpoint: str = "facebook/sam-vit-base", device: str = None):
8 | if device is None:
9 | device = "cuda" if torch.cuda.is_available() else "cpu"
10 | self.device = device
11 |
12 | self.checkpoint = checkpoint
13 | self.model: SamModel = SamModel.from_pretrained(checkpoint).to(self.device)
14 | self.processor: SamProcessor = SamProcessor.from_pretrained(checkpoint)
15 |
16 | def __call__(
17 | self,
18 | image: np.ndarray,
19 | points: List[List[List[int]]] = None,
20 | labels: List[List[bool]] = None,
21 | input_masks=None,
22 | multimask_output: bool = False,
23 | ):
24 | inputs = self.processor(
25 | image,
26 | input_points=points,
27 | input_labels=labels,
28 | return_tensors="pt",
29 | input_masks=input_masks,
30 | multimask_output=multimask_output,
31 | ).to(self.device)
32 | # TODO: multi-mask output doesn't seem to work?
33 | with torch.no_grad():
34 | outputs = self.model(**inputs)
35 | return outputs, inputs
36 |
37 | # to mimic the functionality of sam predictor
38 | def predict(
39 | self,
40 | image: np.ndarray,
41 | points: List[List[List[int]]] = None,
42 | labels: List[List[bool]] = None,
43 | input_masks: np.ndarray = None,
44 | multimask_output: bool = False,
45 | ) -> Tuple[np.ndarray, float, np.ndarray]:
46 | outputs, inputs = self(image, points, labels, input_masks, multimask_output)
47 | masks: torch.Tensor = self.processor.image_processor.post_process_masks(
48 | outputs.pred_masks.cpu(),
49 | inputs["original_sizes"].cpu(),
50 | inputs["reshaped_input_sizes"].cpu(),
51 | )
52 | scores: torch.Tensor = outputs.iou_scores
53 | logits: torch.Tensor = outputs.pred_masks
54 |
55 | # get best mask
56 | idx = np.argmax(scores.detach().cpu().numpy())
57 | mask = masks[0][0][idx].detach().cpu().numpy()
58 | score = scores[0][0][idx].detach().cpu().numpy()
59 | logits = logits[0][0][idx].detach().cpu().unsqueeze(0).numpy()
60 |
61 | return mask, score, logits
62 |
63 | def __repr__(self):
64 | return f"SamModelWrapper({self.checkpoint}, {self.device})"
65 |
--------------------------------------------------------------------------------
/fibsem/segmentation/segmentation_config.yaml:
--------------------------------------------------------------------------------
1 | CLASS_COLORS:
2 | 0: "black"
3 | 1: "red"
4 | 2: "green"
5 | 3: "cyan"
6 | 4: "yellow"
7 | 5: "magenta"
8 | 6: "purple"
9 | 7: "white"
10 | 8: "orange"
11 | 9: "blue"
12 | 10: "pink"
13 | 11: "gold"
14 | 12: "salmon"
15 | 13: "olive"
16 | 14: "maroon"
17 | 15: "navy"
18 | 16: "teal"
19 | 17: "lime"
20 | 18: "aqua"
21 | 19: "silver"
22 | 20: "brown"
23 | 21: "indigo"
24 | 22: "violet"
25 | 23: "turquoise"
26 | 24: "tan"
27 | 25: "orchid"
28 | 26: "gray"
29 | 27: "khaki"
30 | 28: "coral"
31 | 29: "crimson"
32 | 30: "plum"
33 | 31: "lavender"
34 | 32: "darkgreen"
35 | 33: "darkblue"
36 | 34: "darkred"
37 | 35: "darkgray"
38 | 36: "darkorange"
39 |
40 | CLASS_LABELS: # autolamella
41 | 0: "background"
42 | 1: "lamella"
43 | 2: "manipulator"
44 | 3: "landing_post"
45 | 4: "copper_adaptor"
46 | 5: "volume_block"
47 |
48 |
49 | # CLASS_LABELS: # spacetomo
50 | # 0: "background"
51 | # 1: "white"
52 | # 2: "black"
53 | # 3: "crack"
54 | # 4: "coating"
55 | # 5: "cell"
56 | # 6: "cellwall"
57 | # 7: "nucleus"
58 | # 8: "vacuole"
59 | # 9: "mitos"
60 | # 10: "lipiddroplets"
61 | # 11: "vesicles"
62 | # 12: "multivesicles"
63 | # 13: "membranes"
64 | # 14: "dynabeads"
65 | # 15: "ice"
66 | # 16: "cryst"
67 |
--------------------------------------------------------------------------------
/fibsem/segmentation/test_image.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/segmentation/test_image.tif
--------------------------------------------------------------------------------
/fibsem/tools/_parser.py:
--------------------------------------------------------------------------------
1 |
2 | import pandas as pd
3 | import numpy as np
4 |
5 | def _parse_health_monitor_data(path: str) -> pd.DataFrame:
6 | # PATH = "health-monitor/data2.csv"
7 |
8 | df = pd.read_csv(path, skiprows=5)
9 |
10 | # display(df)
11 |
12 |
13 | # header = system
14 | # row 0 = subsystem
15 | # row 1 = component
16 | # row 2 = parameter
17 | # row 3 = unit
18 | # row 4 = type
19 |
20 | # col 0 = date
21 | # col 1 = timestamp
22 |
23 | df_headers = df.iloc[0:5, 2:]
24 | df_data = df.iloc[6:, :]
25 |
26 | systems = df_headers.columns.values
27 | subsystems = df_headers.iloc[0, :].values
28 | components = df_headers.iloc[1, :].values
29 | parameters = df_headers.iloc[2, :].values
30 | units = df_headers.iloc[3, :].values
31 | types = df_headers.iloc[4, :].values
32 |
33 | type_map = {
34 | "Int": np.uint8,
35 | "Float": np.float32,
36 | "String": str,
37 | "Boolean": bool,
38 | "DateTime": np.datetime64,
39 | }
40 |
41 | new_columns = ["Date", "Time"]
42 | new_columns_type = ["datetime64", "datetime64"]
43 | for subsystem, component, parameter, unit, type in zip(subsystems, components, parameters, units, types):
44 | new_columns.append(f"{subsystem}.{component}.{parameter} ({unit})")
45 | new_columns_type.append(type_map[type])
46 |
47 | df_data.columns = new_columns
48 |
49 | # replace all values of "NaN" with np.nan
50 | df_data = df_data.replace("NaN", np.nan)
51 |
52 | # drop columns with all NaN values
53 | # df_data = df_data.dropna(axis=0, how="all")
54 | # df_data = df_data.astype(dict(zip(new_columns, new_columns_type)))
55 |
56 | # combine date and time columns
57 | df_data["datetime"] = pd.to_datetime(df_data["Date"] + " " + df_data["Time"])
58 |
59 | # set timezone to Aus/Sydney for datetime column
60 | # df_data["datetime"] = df_data["datetime"].dt.tz_localize("UTC").dt.tz_convert("Australia/Sydney")
61 |
62 | # drop Date and Time columns
63 | df_data = df_data.drop(columns=["Date", "Time"])
64 |
65 |
66 | # print duplicate columns
67 | # drop duplicate columns
68 | df_data = df_data.loc[:,~df_data.columns.duplicated()]
69 | print(df_data.columns[df_data.columns.duplicated()])
70 |
71 |
72 | return df_data
73 |
--------------------------------------------------------------------------------
/fibsem/tools/_streamlit.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import threading
3 | import time
4 |
5 | def run_streamlit_simple(script_path: str, port: int = 8501, streamlit_args: list = None) -> subprocess.Popen:
6 | """Simple version to run Streamlit app in background thread."""
7 |
8 | def start_streamlit():
9 | cmd = [
10 | "streamlit", "run", script_path,
11 | "--server.port", str(port),
12 | "--server.headless", "false",
13 | "--browser.gatherUsageStats", "false",
14 | ]
15 |
16 | # Add any additional arguments for the streamlit script
17 | if streamlit_args:
18 | cmd.append("--") # Separator between streamlit args and script args
19 | cmd.extend(streamlit_args)
20 |
21 | subprocess.run(cmd)
22 |
23 | # Start in daemon thread so it stops when main program exits
24 | thread = threading.Thread(target=start_streamlit, daemon=True)
25 | thread.start()
26 |
27 | # Give it time to start
28 | time.sleep(2)
29 |
30 | return thread
31 |
32 | # Usage
33 | def main():
34 |
35 | script_path = "/home/patrick/github/autolamella/autolamella/tools/review.py"
36 | experiment_path = "/home/patrick/github/autolamella/autolamella/log/AutoLamella-2025-05-30-17-56"
37 |
38 | # Start Streamlit app
39 | streamlit_args = ["--experiment_path", experiment_path]
40 | thread = run_streamlit_simple(script_path=script_path,
41 | port=8502,
42 | streamlit_args=streamlit_args)
43 |
44 | # Your main application continues here
45 | print("Streamlit is running in background...")
46 |
47 | # Keep main thread alive
48 | try:
49 | while True:
50 | time.sleep(1)
51 | except KeyboardInterrupt:
52 | print("Exiting...")
53 |
54 | if __name__ == "__main__":
55 | main()
--------------------------------------------------------------------------------
/fibsem/tools/run_manipulator_calibration.py:
--------------------------------------------------------------------------------
1 | from fibsem import utils, calibration, acquire
2 |
3 | from fibsem.structures import BeamType, FibsemManipulatorPosition
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 | import sys
7 |
8 | def main():
9 | microscope, settings = utils.setup_session()
10 |
11 | # if argv 1 is "debug", run in debug mode
12 | _DEBUG = False
13 | if len(sys.argv) > 1:
14 | if sys.argv[1] == "debug":
15 | _DEBUG = True
16 |
17 | if _DEBUG:
18 | microscope.set("scan_rotation", np.deg2rad(0), beam_type=BeamType.ION)
19 | microscope.move_manipulator_to_position_offset(offset=FibsemManipulatorPosition(), name="EUCENTRIC")
20 | microscope.move_manipulator_corrected(dx=-50e-6, dy=-150e-6, beam_type=BeamType.ELECTRON)
21 |
22 | settings.image.autocontrast = True
23 | settings.image.hfw = 900e-6
24 | eb_image, ib_image = acquire.take_reference_images(microscope, settings.image)
25 |
26 | fig, ax = plt.subplots(1, 2, figsize=(10, 5))
27 | ax[0].imshow(eb_image.data, cmap="gray")
28 | ax[1].imshow(ib_image.data, cmap="gray")
29 |
30 | plt.show()
31 |
32 | calibration._calibrate_manipulator_thermo(microscope, settings, None)
33 |
34 | if __name__ == "__main__":
35 | main()
--------------------------------------------------------------------------------
/fibsem/tools/run_split_dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import glob
3 | import pandas as pd
4 | from pathlib import Path
5 | import argparse
6 |
7 | def _split_dataset(DATA_PATH: Path, OUTPUT_PATH: Path):
8 |
9 | TRAIN_PATH = os.path.join(OUTPUT_PATH, "train" )
10 | TEST_PATH = os.path.join(OUTPUT_PATH, "test")
11 |
12 | # make dirs
13 | os.makedirs(os.path.join(TRAIN_PATH, "labels"), exist_ok=True)
14 | os.makedirs(os.path.join(TEST_PATH, "labels"), exist_ok=True)
15 |
16 | df = pd.read_csv(os.path.join(DATA_PATH, "data.csv"))
17 | df["path"] = df["image"].apply(lambda x: os.path.join(DATA_PATH, f"{x}.tif"))
18 | df["mask_path"] = df["image"].apply(lambda x: os.path.join(DATA_PATH, "mask", f"{x}.tif"))
19 |
20 | print(f"total: {len(df)} images")
21 |
22 | # corrected: did the user overwrite the model prediction
23 | # False: the model prediction was correct -> test set
24 | # True: the model prediction was incorrect -> train set
25 |
26 | # split the data into train and test
27 | df_train = df[df["corrected"] == True]
28 | df_test = df[df["corrected"] == False]
29 |
30 | print(f"train: {len(df_train)} images")
31 | print(f"test: {len(df_test)} images")
32 |
33 | response = input(f"Move data to {OUTPUT_PATH}? [y/n]")
34 |
35 | if response == "y":
36 | # move the images and masks to the correct folder
37 | for path, mask_path in zip(df_train["path"].unique(), df_train["mask_path"].unique()):
38 | os.rename(path, os.path.join(TRAIN_PATH, os.path.basename(path)))
39 | os.rename(mask_path, os.path.join(TRAIN_PATH, "labels", os.path.basename(mask_path)))
40 |
41 | for path, mask_path in zip(df_test["path"].unique(), df_test["mask_path"].unique()):
42 | os.rename(path, os.path.join(TEST_PATH, os.path.basename(path)))
43 | os.rename(mask_path, os.path.join(TEST_PATH, "labels", os.path.basename(mask_path)))
44 |
45 |
46 | def main():
47 | parser = argparse.ArgumentParser()
48 | parser.add_argument("--data_path", type=str, dest="data_path", action="store", help="Path to the data folder")
49 | parser.add_argument("--output_path", type=str, dest="output_path", action="store", help="Path to the output folder")
50 |
51 | args = parser.parse_args()
52 |
53 | _split_dataset(args.data_path, args.output_path)
54 |
55 |
56 | if __name__ == "__main__":
57 | main()
--------------------------------------------------------------------------------
/fibsem/transformations.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Optional
3 |
4 | import numpy as np
5 |
6 | from fibsem.microscope import FibsemMicroscope
7 | from fibsem.structures import FibsemStagePosition
8 |
9 |
10 | def convert_milling_angle_to_stage_tilt(
11 | milling_angle: float, pretilt: float, column_tilt: float = np.deg2rad(52)
12 | ) -> float:
13 | """Convert the milling angle to the stage tilt angle, based on pretilt and column tilt.
14 | milling_angle = 90 - column_tilt + stage_tilt - pretilt
15 | stage_tilt = milling_angle - 90 + pretilt + column_tilt
16 | Args:
17 | milling_angle: milling angle (radians)
18 | pretilt: pretilt angle (radians)
19 | column_tilt: column tilt angle (radians)
20 | Returns:
21 | stage_tilt: stage tilt (radians)"""
22 |
23 | stage_tilt = milling_angle + column_tilt + pretilt - np.deg2rad(90)
24 |
25 | return stage_tilt
26 |
27 |
28 | def convert_stage_tilt_to_milling_angle(
29 | stage_tilt: float, pretilt: float, column_tilt: float = np.deg2rad(52)
30 | ) -> float:
31 | """Convert the stage tilt angle to the milling angle, based on pretilt and column tilt.
32 | milling_angle = 90 - column_tilt + stage_tilt - pretilt
33 | Args:
34 | stage_tilt: stage tilt (radians)
35 | pretilt: pretilt angle (radians)
36 | column_tilt: column tilt angle (radians)
37 | Returns:
38 | milling_angle: milling angle (radians)"""
39 |
40 | milling_angle = np.deg2rad(90) - column_tilt + stage_tilt - pretilt
41 |
42 | return milling_angle
43 |
44 |
45 | def get_stage_tilt_from_milling_angle(
46 | microscope: FibsemMicroscope, milling_angle: float
47 | ) -> float:
48 | """Get the stage tilt angle from the milling angle, based on pretilt and column tilt.
49 | Args:
50 | microscope (FibsemMicroscope): microscope connection
51 | milling_angle (float): milling angle (radians)
52 | Returns:
53 | float: stage tilt angle (radians)
54 | """
55 | pretilt = np.deg2rad(microscope.system.stage.shuttle_pre_tilt)
56 | column_tilt = np.deg2rad(microscope.system.ion.column_tilt)
57 | stage_tilt = convert_milling_angle_to_stage_tilt(
58 | milling_angle, pretilt, column_tilt
59 | )
60 | logging.debug(
61 | f"milling_angle: {np.rad2deg(milling_angle):.2f} deg, "
62 | f"pretilt: {np.rad2deg(pretilt)} deg, "
63 | f"stage_tilt: {np.rad2deg(stage_tilt):.2f} deg"
64 | )
65 | return stage_tilt
66 |
67 | def is_close_to_milling_angle(
68 | microscope: FibsemMicroscope, milling_angle: float, atol: float = np.deg2rad(2)
69 | ) -> bool:
70 | """Check if the stage tilt is close to the milling angle, within a tolerance.
71 | Args:
72 | microscope (FibsemMicroscope): microscope connection
73 | milling_angle (float): milling angle (radians)
74 | atol (float): tolerance in radians
75 | Returns:
76 | bool: True if the stage tilt is within the tolerance of the milling angle
77 | """
78 | current_stage_tilt = microscope.get_stage_position().t
79 | pretilt = np.deg2rad(microscope.system.stage.shuttle_pre_tilt)
80 | column_tilt = np.deg2rad(microscope.system.ion.column_tilt)
81 | stage_tilt = convert_milling_angle_to_stage_tilt(
82 | milling_angle, pretilt=pretilt, column_tilt=column_tilt
83 | )
84 | logging.info(
85 | f"The current stage tilt is {np.rad2deg(stage_tilt):.2f} deg, "
86 | f"the stage tilt for the milling angle is {np.rad2deg(stage_tilt):.2f} deg"
87 | )
88 | return np.isclose(stage_tilt, current_stage_tilt, atol=atol)
89 |
90 | # TODO: move inside the microscope class
91 | def move_to_milling_angle(
92 | microscope: FibsemMicroscope,
93 | milling_angle: float,
94 | rotation: Optional[float] = None,
95 | ) -> bool:
96 | """Move the stage to the milling angle, based on the current pretilt and column tilt."""
97 |
98 | if rotation is None:
99 | rotation = microscope.system.stage.rotation_reference
100 |
101 | # calculate the stage tilt from the milling angle
102 | stage_tilt = get_stage_tilt_from_milling_angle(microscope, milling_angle)
103 | stage_position = FibsemStagePosition(t=stage_tilt, r=rotation)
104 | microscope.safe_absolute_stage_movement(stage_position)
105 |
106 | is_close = is_close_to_milling_angle(microscope, milling_angle)
107 | return is_close
--------------------------------------------------------------------------------
/fibsem/ui/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/ui/.gitkeep
--------------------------------------------------------------------------------
/fibsem/ui/FibsemCryoDepositionWidget.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import napari
3 | import napari.utils.notifications
4 | from PyQt5 import QtWidgets
5 | from fibsem import config as cfg
6 | from fibsem import constants, conversions, utils
7 | from fibsem.microscope import FibsemMicroscope, ThermoMicroscope
8 | from fibsem.microscopes.tescan import TescanMicroscope
9 | from fibsem.microscopes.simulator import DemoMicroscope
10 | from fibsem.structures import MicroscopeSettings, FibsemGasInjectionSettings
11 |
12 | from fibsem.ui.qtdesigner_files import FibsemCryoDepositionWidget
13 | from fibsem import gis
14 |
15 |
16 | class FibsemCryoDepositionWidget(FibsemCryoDepositionWidget.Ui_Dialog, QtWidgets.QDialog):
17 | def __init__(
18 | self,
19 | microscope: FibsemMicroscope = None,
20 | settings: MicroscopeSettings = None,
21 | parent=None,
22 | ):
23 | super(FibsemCryoDepositionWidget, self).__init__(parent=parent)
24 | self.setupUi(self)
25 | self.setWindowTitle("Cryo Deposition")
26 |
27 | self.microscope = microscope
28 | self.settings = settings
29 |
30 | self.setup_connections()
31 |
32 | def setup_connections(self):
33 |
34 | self.pushButton_run_sputter.clicked.connect(self.run_sputter)
35 |
36 | positions = utils.load_yaml(cfg.POSITION_PATH)
37 | self.comboBox_stage_position.addItems(["Current Position"] + [p["name"] for p in positions])
38 | available_ports = self.microscope.get_available_values("gis_ports")
39 | self.comboBox_port.addItems([str(p) for p in available_ports])
40 |
41 |
42 | # TODO: show / hide based on gis / multichem available
43 | multichem_available = self.microscope.is_available("gis_multichem")
44 | self.lineEdit_gas.setVisible(multichem_available)
45 | self.label_gas.setVisible(multichem_available)
46 | self.lineEdit_insert_position.setVisible(multichem_available)
47 | self.label_insert_position.setVisible(multichem_available)
48 | self.comboBox_port.setVisible(not multichem_available) # gis only
49 | self.label_port.setVisible(not multichem_available) # gis only
50 |
51 | def _get_protocol_from_ui(self):
52 |
53 | protocol = {
54 | "port": self.comboBox_port.currentText(),
55 | "gas": self.lineEdit_gas.text(),
56 | "insert_position": self.lineEdit_insert_position.text(),
57 | "duration": self.doubleSpinBox_duration.value(),
58 | "name": self.comboBox_stage_position.currentText(),
59 |
60 | }
61 |
62 | return protocol
63 |
64 | # TODO: thread this, add progress bar, feedback
65 | def run_sputter(self):
66 |
67 | gdict = self._get_protocol_from_ui()
68 | gis_settings = FibsemGasInjectionSettings.from_dict(gdict)
69 |
70 | if gdict["name"] == "Current Position":
71 | gdict["name"] = None
72 |
73 | gis.cryo_deposition_v2(self.microscope, gis_settings, name=gdict["name"])
74 |
75 |
76 | def main():
77 |
78 | viewer = napari.Viewer(ndisplay=2)
79 | microscope, settings = utils.setup_session()
80 | cryo_sputter_widget = FibsemCryoDepositionWidget(microscope, settings)
81 | viewer.window.add_dock_widget(
82 | cryo_sputter_widget, area="right", add_vertical_stretch=False
83 | )
84 | napari.run()
85 |
86 |
87 | if __name__ == "__main__":
88 | main()
--------------------------------------------------------------------------------
/fibsem/ui/FibsemMicroscopeConfigurationWidget.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import napari
3 | import napari.utils.notifications
4 | import fibsem
5 | from PyQt5 import QtWidgets
6 | from fibsem import config as cfg
7 | from fibsem.ui.qtdesigner_files import FibsemMicroscopeConfigurationWidget as FibsemMicroscopeConfigurationWidgetUI
8 | from fibsem.ui.FibsemMicroscopeConfigurationWidgetBase import FibsemMicroscopeConfigurationWidgetBase
9 |
10 |
11 | class FibsemMicroscopeConfigurationWidget(FibsemMicroscopeConfigurationWidgetUI.Ui_MainWindow, QtWidgets.QMainWindow):
12 | def __init__(
13 | self,
14 | path: str = None,
15 | viewer: napari.Viewer = None,
16 | parent=None,
17 | ):
18 | super().__init__(parent=parent)
19 | self.setupUi(self)
20 | self.setWindowTitle("Microscope Configuration")
21 |
22 | self.viewer = viewer
23 |
24 | self.configuration_widget = FibsemMicroscopeConfigurationWidgetBase(path)
25 | # add to layout
26 | self.gridLayout.addWidget(self.configuration_widget)
27 |
28 | self.setup_connections()
29 |
30 | def setup_connections(self):
31 |
32 | # actions
33 | self.actionSave_Configuration.triggered.connect(self.configuration_widget.save_configuration)
34 | self.actionLoad_Configuration.triggered.connect(self.configuration_widget.load_configuration_from_file)
35 |
36 |
37 | def main():
38 |
39 | # parse arguments
40 | parser = argparse.ArgumentParser(f"Microscope Configuration UI")
41 | parser.add_argument("--config", type=str, default=cfg.DEFAULT_CONFIGURATION_PATH, help="Path to microscope configuration file")
42 | args = parser.parse_args()
43 |
44 | # widget viewer
45 | viewer = napari.Viewer(ndisplay=2)
46 | microscope_configuration = FibsemMicroscopeConfigurationWidget(path=args.config, viewer=viewer)
47 | viewer.window.add_dock_widget(
48 | microscope_configuration,
49 | area="right",
50 | add_vertical_stretch=False,
51 | name=f"OpenFIBSEM v{fibsem.__version__} Microscope Configuration",
52 | )
53 | napari.run()
54 |
55 |
56 | if __name__ == "__main__":
57 | main()
--------------------------------------------------------------------------------
/fibsem/ui/__init__.py:
--------------------------------------------------------------------------------
1 | from fibsem.ui.FibsemImageSettingsWidget import FibsemImageSettingsWidget
2 | from fibsem.ui.FibsemMovementWidget import FibsemMovementWidget
3 | from fibsem.ui.FibsemSystemSetupWidget import FibsemSystemSetupWidget
4 | from fibsem.ui.FibsemMillingWidget import FibsemMillingWidget
5 | from fibsem.ui.FibsemCryoDepositionWidget import FibsemCryoDepositionWidget
6 | from fibsem.ui.FibsemMinimapWidget import FibsemMinimapWidget
7 | from fibsem.ui.FibsemManipulatorWidget import FibsemManipulatorWidget
8 | from fibsem.ui.FibsemSpotBurnWidget import FibsemSpotBurnWidget
9 | try:
10 | from fibsem.ui.FibsemEmbeddedDetectionWidget import FibsemEmbeddedDetectionUI
11 | DETECTION_AVAILABLE = True
12 | except ImportError:
13 | DETECTION_AVAILABLE = False
14 | import logging
15 | logging.debug("Could not import FibsemEmbeddedDetectionWidget")
16 |
17 |
--------------------------------------------------------------------------------
/fibsem/ui/napari/properties.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | # IMAGING
4 |
5 |
6 | ALIGNMENT_LAYER_PROPERTIES = {
7 | "name": "alignment_area",
8 | "shape_type": "rectangle",
9 | "edge_color": "lime",
10 | "edge_width": 20,
11 | "face_color": "transparent",
12 | "opacity": 0.5,
13 | "metadata": {"type": "alignment"},
14 | }
15 |
16 | IMAGE_TEXT_LAYER_PROPERTIES = {
17 | "name": "label",
18 | "text": {
19 | "string": ["ELECTRON BEAM", "ION BEAM"],
20 | "color": "white"
21 | },
22 | "size": 20,
23 | "edge_width": 7,
24 | "edge_width_is_relative": False,
25 | "edge_color": "transparent",
26 | "face_color": "transparent",
27 | }
28 |
29 | IMAGING_CROSSHAIR_LAYER_PROPERTIES = {
30 | "name": "crosshair",
31 | "shape_type": "line",
32 | "edge_width": 5,
33 | "edge_color": "yellow",
34 | "face_color": "yellow",
35 | "opacity": 0.8,
36 | "blending": "translucent",
37 | }
38 |
39 | IMAGING_SCALEBAR_LAYER_PROPERTIES = {
40 | "name": "scalebar",
41 | "shape_type": "line",
42 | "edge_width": 5,
43 | "edge_color": "yellow",
44 | "face_color": "yellow",
45 | "opacity": 0.8,
46 | "blending": "translucent",
47 | "text": {
48 | "color":"white",
49 | "translation": np.array([-20, 0]),
50 | "opacity": 1,
51 | "sze": 20,
52 | },
53 | }
54 |
55 | IMAGING_RULER_LAYER_PROPERTIES = {
56 | "name": "ruler",
57 | "size": 20,
58 | "face_color": "lime",
59 | "edge_color": "white",
60 | "text": {
61 | "color": "white",
62 | "translation": np.array([-20, 0]),
63 | "size": 10,
64 | },
65 | "line-layer": {
66 | "name": "ruler_line",
67 | "shape_type": "line",
68 | "edge_width": 5,
69 | "edge_color": "lime",
70 | },
71 | }
72 |
73 | RULER_LAYER_NAME = IMAGING_RULER_LAYER_PROPERTIES["name"]
74 | RULER_LINE_LAYER_NAME = IMAGING_RULER_LAYER_PROPERTIES["line-layer"]["name"]
75 |
76 | # MILLING
77 |
78 |
79 | ## MINIMAP
80 |
81 | OVERVIEW_IMAGE_LAYER_PROPERTIES = {
82 | "name": "overview-image",
83 | "colormap": "gray",
84 | "blending": "additive",
85 | "median_filter_size": 3,
86 | }
87 |
88 | GRIDBAR_IMAGE_LAYER_PROPERTIES = {
89 | "name": "gridbar-image",
90 | "spacing": 100,
91 | "width": 20,
92 | }
93 |
94 | CORRELATION_IMAGE_LAYER_PROPERTIES = {
95 | "name": "correlation-image",
96 | "colormap": "green",
97 | "blending": "translucent",
98 | "opacity": 0.2,
99 | "colours": ["green", "cyan", "magenta", "red", "yellow"],
100 | }
101 |
102 | STAGE_POSITION_SHAPE_LAYER_PROPERTIES = {
103 | "name": "stage-positions",
104 | "shape_type": "line",
105 | "edge_width": 5,
106 | "edge_color": "yellow",
107 | "face_color": "yellow",
108 | "opacity": 0.8,
109 | "blending": "translucent",
110 | "text": {
111 | "string": [],
112 | "color": "white",
113 | "size": 15,
114 | "translation": np.array([-50, 0]), # text shown 50px above the point
115 | },
116 | "saved_color": "lime",
117 | }
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemCryoDepositionWidget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemCryoDepositionWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.9
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(416, 221)
18 | self.gridLayout = QtWidgets.QGridLayout(Dialog)
19 | self.gridLayout.setObjectName("gridLayout")
20 | self.label_duration = QtWidgets.QLabel(Dialog)
21 | self.label_duration.setObjectName("label_duration")
22 | self.gridLayout.addWidget(self.label_duration, 5, 0, 1, 1)
23 | self.comboBox_stage_position = QtWidgets.QComboBox(Dialog)
24 | self.comboBox_stage_position.setObjectName("comboBox_stage_position")
25 | self.gridLayout.addWidget(self.comboBox_stage_position, 1, 1, 1, 1)
26 | self.label_title = QtWidgets.QLabel(Dialog)
27 | font = QtGui.QFont()
28 | font.setPointSize(12)
29 | font.setBold(True)
30 | font.setWeight(75)
31 | self.label_title.setFont(font)
32 | self.label_title.setObjectName("label_title")
33 | self.gridLayout.addWidget(self.label_title, 0, 0, 1, 2)
34 | self.label_stage_position = QtWidgets.QLabel(Dialog)
35 | self.label_stage_position.setObjectName("label_stage_position")
36 | self.gridLayout.addWidget(self.label_stage_position, 1, 0, 1, 1)
37 | self.lineEdit_gas = QtWidgets.QLineEdit(Dialog)
38 | self.lineEdit_gas.setObjectName("lineEdit_gas")
39 | self.gridLayout.addWidget(self.lineEdit_gas, 4, 1, 1, 1)
40 | self.doubleSpinBox_duration = QtWidgets.QDoubleSpinBox(Dialog)
41 | self.doubleSpinBox_duration.setMaximum(1000.0)
42 | self.doubleSpinBox_duration.setProperty("value", 30.0)
43 | self.doubleSpinBox_duration.setObjectName("doubleSpinBox_duration")
44 | self.gridLayout.addWidget(self.doubleSpinBox_duration, 5, 1, 1, 1)
45 | self.label_port = QtWidgets.QLabel(Dialog)
46 | self.label_port.setObjectName("label_port")
47 | self.gridLayout.addWidget(self.label_port, 3, 0, 1, 1)
48 | self.pushButton_run_sputter = QtWidgets.QPushButton(Dialog)
49 | self.pushButton_run_sputter.setObjectName("pushButton_run_sputter")
50 | self.gridLayout.addWidget(self.pushButton_run_sputter, 7, 0, 1, 2)
51 | self.label_gas = QtWidgets.QLabel(Dialog)
52 | self.label_gas.setObjectName("label_gas")
53 | self.gridLayout.addWidget(self.label_gas, 4, 0, 1, 1)
54 | self.label_insert_position = QtWidgets.QLabel(Dialog)
55 | self.label_insert_position.setObjectName("label_insert_position")
56 | self.gridLayout.addWidget(self.label_insert_position, 6, 0, 1, 1)
57 | self.lineEdit_insert_position = QtWidgets.QLineEdit(Dialog)
58 | self.lineEdit_insert_position.setObjectName("lineEdit_insert_position")
59 | self.gridLayout.addWidget(self.lineEdit_insert_position, 6, 1, 1, 1)
60 | self.comboBox_port = QtWidgets.QComboBox(Dialog)
61 | self.comboBox_port.setObjectName("comboBox_port")
62 | self.gridLayout.addWidget(self.comboBox_port, 3, 1, 1, 1)
63 |
64 | self.retranslateUi(Dialog)
65 | QtCore.QMetaObject.connectSlotsByName(Dialog)
66 |
67 | def retranslateUi(self, Dialog):
68 | _translate = QtCore.QCoreApplication.translate
69 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
70 | self.label_duration.setText(_translate("Dialog", "Duration (s)"))
71 | self.label_title.setText(_translate("Dialog", "Cryo Deposition"))
72 | self.label_stage_position.setText(_translate("Dialog", "Stage Position"))
73 | self.lineEdit_gas.setText(_translate("Dialog", "Pt cryo"))
74 | self.label_port.setText(_translate("Dialog", "Port"))
75 | self.pushButton_run_sputter.setText(_translate("Dialog", "Run Cryo Deposition"))
76 | self.label_gas.setText(_translate("Dialog", "Gas"))
77 | self.label_insert_position.setText(_translate("Dialog", "Insert Position"))
78 | self.lineEdit_insert_position.setText(_translate("Dialog", "cryo"))
79 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemCryoDepositionWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 416
10 | 221
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 | -
18 |
19 |
20 | Duration (s)
21 |
22 |
23 |
24 | -
25 |
26 |
27 | -
28 |
29 |
30 |
31 | 12
32 | 75
33 | true
34 |
35 |
36 |
37 | Cryo Deposition
38 |
39 |
40 |
41 | -
42 |
43 |
44 | Stage Position
45 |
46 |
47 |
48 | -
49 |
50 |
51 | Pt cryo
52 |
53 |
54 |
55 | -
56 |
57 |
58 | 1000.000000000000000
59 |
60 |
61 | 30.000000000000000
62 |
63 |
64 |
65 | -
66 |
67 |
68 | Port
69 |
70 |
71 |
72 | -
73 |
74 |
75 | Run Cryo Deposition
76 |
77 |
78 |
79 | -
80 |
81 |
82 | Gas
83 |
84 |
85 |
86 | -
87 |
88 |
89 | Insert Position
90 |
91 |
92 |
93 | -
94 |
95 |
96 | cryo
97 |
98 |
99 |
100 | -
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemEmbeddedDetectionWidget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemEmbeddedDetectionWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.11
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(472, 525)
18 | self.gridLayout = QtWidgets.QGridLayout(Form)
19 | self.gridLayout.setObjectName("gridLayout")
20 | self.label_model = QtWidgets.QLabel(Form)
21 | self.label_model.setObjectName("label_model")
22 | self.gridLayout.addWidget(self.label_model, 1, 0, 1, 2)
23 | self.label_title = QtWidgets.QLabel(Form)
24 | font = QtGui.QFont()
25 | font.setPointSize(9)
26 | font.setBold(True)
27 | font.setWeight(75)
28 | self.label_title.setFont(font)
29 | self.label_title.setObjectName("label_title")
30 | self.gridLayout.addWidget(self.label_title, 0, 0, 1, 2)
31 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
32 | self.gridLayout.addItem(spacerItem, 4, 0, 1, 2)
33 | self.label_info = QtWidgets.QLabel(Form)
34 | self.label_info.setMinimumSize(QtCore.QSize(0, 0))
35 | self.label_info.setText("")
36 | self.label_info.setWordWrap(True)
37 | self.label_info.setObjectName("label_info")
38 | self.gridLayout.addWidget(self.label_info, 2, 0, 1, 2)
39 | self.label_instructions = QtWidgets.QLabel(Form)
40 | self.label_instructions.setWordWrap(True)
41 | self.label_instructions.setObjectName("label_instructions")
42 | self.gridLayout.addWidget(self.label_instructions, 3, 0, 1, 2)
43 |
44 | self.retranslateUi(Form)
45 | QtCore.QMetaObject.connectSlotsByName(Form)
46 |
47 | def retranslateUi(self, Form):
48 | _translate = QtCore.QCoreApplication.translate
49 | Form.setWindowTitle(_translate("Form", "Form"))
50 | self.label_model.setText(_translate("Form", "Model:"))
51 | self.label_title.setText(_translate("Form", "Feature Detection"))
52 | self.label_instructions.setText(_translate("Form", "Drag to move the features, when finished press confirm."))
53 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemEmbeddedDetectionWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 472
10 | 525
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | Model:
21 |
22 |
23 |
24 | -
25 |
26 |
27 |
28 | 9
29 | 75
30 | true
31 |
32 |
33 |
34 | Feature Detection
35 |
36 |
37 |
38 | -
39 |
40 |
41 | Qt::Vertical
42 |
43 |
44 |
45 | 20
46 | 40
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 | 0
56 | 0
57 |
58 |
59 |
60 |
61 |
62 |
63 | true
64 |
65 |
66 |
67 | -
68 |
69 |
70 | Drag to move the features, when finished press confirm.
71 |
72 |
73 | true
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemExperimentWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 345
10 | 179
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | -
21 |
22 |
23 | -
24 |
25 |
26 | Name
27 |
28 |
29 |
30 | -
31 |
32 |
33 | Qt::Horizontal
34 |
35 |
36 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
37 |
38 |
39 | false
40 |
41 |
42 |
43 | -
44 |
45 |
46 |
47 | 75
48 | true
49 |
50 |
51 |
52 | Experiment
53 |
54 |
55 |
56 | -
57 |
58 |
59 | Sample
60 |
61 |
62 |
63 | -
64 |
65 |
66 | -
67 |
68 |
69 | User
70 |
71 |
72 |
73 | -
74 |
75 |
76 | Project
77 |
78 |
79 |
80 | -
81 |
82 |
83 |
84 |
85 |
86 | lineEdit_exp_name
87 | lineEdit_exp_user
88 | lineEdit_exp_sample
89 | lineEdit_exp_project
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemFeatureDetectionUI.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 429
10 | 648
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 | -
22 |
23 |
24 |
25 | 12
26 | 75
27 | true
28 |
29 |
30 |
31 | OpenFIBSEM Keypoint Labelling
32 |
33 |
34 |
35 | -
36 |
37 |
38 | Edit
39 |
40 |
41 |
42 | -
43 |
44 |
45 | Features
46 |
47 |
48 |
49 | -
50 |
51 |
52 | Add
53 |
54 |
55 |
56 | -
57 |
58 |
59 | Qt::Vertical
60 |
61 |
62 |
63 | 20
64 | 40
65 |
66 |
67 |
68 |
69 | -
70 |
71 |
72 | Instructions
73 |
74 |
75 | true
76 |
77 |
78 |
79 | -
80 |
81 |
82 | Qt::AlignCenter
83 |
84 |
85 | 999999999
86 |
87 |
88 |
89 | -
90 |
91 |
92 | Current Image (Total: N)
93 |
94 |
95 |
96 |
97 |
98 |
116 |
117 |
118 |
119 | Load Image Directory
120 |
121 |
122 |
123 |
124 | Load CSV
125 |
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemMicroscopeConfigurationWidget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemMicroscopeConfigurationWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.9
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_MainWindow(object):
15 | def setupUi(self, MainWindow):
16 | MainWindow.setObjectName("MainWindow")
17 | MainWindow.resize(632, 930)
18 | self.centralwidget = QtWidgets.QWidget(MainWindow)
19 | self.centralwidget.setObjectName("centralwidget")
20 | self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
21 | self.gridLayout.setObjectName("gridLayout")
22 | self.label_configuration_title = QtWidgets.QLabel(self.centralwidget)
23 | font = QtGui.QFont()
24 | font.setPointSize(12)
25 | font.setBold(True)
26 | font.setWeight(75)
27 | self.label_configuration_title.setFont(font)
28 | self.label_configuration_title.setObjectName("label_configuration_title")
29 | self.gridLayout.addWidget(self.label_configuration_title, 0, 0, 1, 1)
30 | MainWindow.setCentralWidget(self.centralwidget)
31 | self.menubar = QtWidgets.QMenuBar(MainWindow)
32 | self.menubar.setGeometry(QtCore.QRect(0, 0, 632, 22))
33 | self.menubar.setObjectName("menubar")
34 | self.menuFile = QtWidgets.QMenu(self.menubar)
35 | self.menuFile.setObjectName("menuFile")
36 | MainWindow.setMenuBar(self.menubar)
37 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
38 | self.statusbar.setObjectName("statusbar")
39 | MainWindow.setStatusBar(self.statusbar)
40 | self.actionLoad_Configuration = QtWidgets.QAction(MainWindow)
41 | self.actionLoad_Configuration.setObjectName("actionLoad_Configuration")
42 | self.actionSave_Configuration = QtWidgets.QAction(MainWindow)
43 | self.actionSave_Configuration.setObjectName("actionSave_Configuration")
44 | self.menuFile.addAction(self.actionLoad_Configuration)
45 | self.menuFile.addAction(self.actionSave_Configuration)
46 | self.menubar.addAction(self.menuFile.menuAction())
47 |
48 | self.retranslateUi(MainWindow)
49 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
50 |
51 | def retranslateUi(self, MainWindow):
52 | _translate = QtCore.QCoreApplication.translate
53 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
54 | self.label_configuration_title.setText(_translate("MainWindow", "OpenFIBSEM: Microscope Configuration"))
55 | self.menuFile.setTitle(_translate("MainWindow", "File"))
56 | self.actionLoad_Configuration.setText(_translate("MainWindow", "Load Configuration"))
57 | self.actionSave_Configuration.setText(_translate("MainWindow", "Save Configuration"))
58 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemMicroscopeConfigurationWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 632
10 | 930
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
22 | 12
23 | 75
24 | true
25 |
26 |
27 |
28 | OpenFIBSEM: Microscope Configuration
29 |
30 |
31 |
32 |
33 |
34 |
52 |
53 |
54 |
55 | Load Configuration
56 |
57 |
58 |
59 |
60 | Save Configuration
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemModelTrainingWidge.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 300
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | Data Path
21 |
22 |
23 |
24 | -
25 |
26 |
27 | Num Classes
28 |
29 |
30 |
31 | -
32 |
33 |
34 | Encoder
35 |
36 |
37 |
38 | -
39 |
40 |
41 |
42 | 10
43 |
44 |
45 |
46 | Training
47 |
48 |
49 |
50 | -
51 |
52 |
53 | Label Path
54 |
55 |
56 |
57 | -
58 |
59 |
60 | Checkpoint
61 |
62 |
63 |
64 | -
65 |
66 |
67 | -
68 |
69 |
70 | Train Model
71 |
72 |
73 |
74 | -
75 |
76 |
77 | Info
78 |
79 |
80 |
81 | -
82 |
83 |
84 | Qt::Vertical
85 |
86 |
87 |
88 | 20
89 | 40
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 | -
98 |
99 |
100 | -
101 |
102 |
103 | -
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemSegmentationModelWidget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemSegmentationModelWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.9
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(456, 352)
18 | self.gridLayout = QtWidgets.QGridLayout(Form)
19 | self.gridLayout.setObjectName("gridLayout")
20 | self.checkpoint_seg_button = QtWidgets.QToolButton(Form)
21 | self.checkpoint_seg_button.setObjectName("checkpoint_seg_button")
22 | self.gridLayout.addWidget(self.checkpoint_seg_button, 2, 2, 1, 1)
23 | self.label_model_type = QtWidgets.QLabel(Form)
24 | self.label_model_type.setObjectName("label_model_type")
25 | self.gridLayout.addWidget(self.label_model_type, 1, 0, 1, 1)
26 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
27 | self.gridLayout.addItem(spacerItem, 4, 0, 1, 2)
28 | self.label_header_model = QtWidgets.QLabel(Form)
29 | font = QtGui.QFont()
30 | font.setBold(True)
31 | font.setWeight(75)
32 | self.label_header_model.setFont(font)
33 | self.label_header_model.setObjectName("label_header_model")
34 | self.gridLayout.addWidget(self.label_header_model, 0, 0, 1, 2)
35 | self.label_checkpoint = QtWidgets.QLabel(Form)
36 | self.label_checkpoint.setObjectName("label_checkpoint")
37 | self.gridLayout.addWidget(self.label_checkpoint, 2, 0, 1, 1)
38 | self.comboBox_model_type = QtWidgets.QComboBox(Form)
39 | self.comboBox_model_type.setObjectName("comboBox_model_type")
40 | self.gridLayout.addWidget(self.comboBox_model_type, 1, 1, 1, 1)
41 | self.lineEdit_checkpoint = QtWidgets.QLineEdit(Form)
42 | self.lineEdit_checkpoint.setObjectName("lineEdit_checkpoint")
43 | self.gridLayout.addWidget(self.lineEdit_checkpoint, 2, 1, 1, 1)
44 | self.pushButton_load_model = QtWidgets.QPushButton(Form)
45 | self.pushButton_load_model.setObjectName("pushButton_load_model")
46 | self.gridLayout.addWidget(self.pushButton_load_model, 3, 0, 1, 2)
47 |
48 | self.retranslateUi(Form)
49 | QtCore.QMetaObject.connectSlotsByName(Form)
50 |
51 | def retranslateUi(self, Form):
52 | _translate = QtCore.QCoreApplication.translate
53 | Form.setWindowTitle(_translate("Form", "Form"))
54 | self.checkpoint_seg_button.setText(_translate("Form", "..."))
55 | self.label_model_type.setText(_translate("Form", "Model"))
56 | self.label_header_model.setText(_translate("Form", "Segmentation Model"))
57 | self.label_checkpoint.setText(_translate("Form", "Checkpoint"))
58 | self.pushButton_load_model.setText(_translate("Form", "Load Model"))
59 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemSegmentationModelWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 456
10 | 352
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | ...
21 |
22 |
23 |
24 | -
25 |
26 |
27 | Model
28 |
29 |
30 |
31 | -
32 |
33 |
34 | Qt::Vertical
35 |
36 |
37 |
38 | 20
39 | 40
40 |
41 |
42 |
43 |
44 | -
45 |
46 |
47 |
48 | 75
49 | true
50 |
51 |
52 |
53 | Segmentation Model
54 |
55 |
56 |
57 | -
58 |
59 |
60 | Checkpoint
61 |
62 |
63 |
64 | -
65 |
66 |
67 | -
68 |
69 |
70 | -
71 |
72 |
73 | Load Model
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemSpotBurnWidget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemSpotBurnWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.11
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(332, 202)
18 | self.gridLayout_2 = QtWidgets.QGridLayout(Form)
19 | self.gridLayout_2.setObjectName("gridLayout_2")
20 | self.label_information = QtWidgets.QLabel(Form)
21 | self.label_information.setText("")
22 | self.label_information.setObjectName("label_information")
23 | self.gridLayout_2.addWidget(self.label_information, 4, 1, 1, 2)
24 | self.label_beam_current = QtWidgets.QLabel(Form)
25 | self.label_beam_current.setObjectName("label_beam_current")
26 | self.gridLayout_2.addWidget(self.label_beam_current, 3, 1, 1, 1)
27 | self.label_exposure_time = QtWidgets.QLabel(Form)
28 | self.label_exposure_time.setObjectName("label_exposure_time")
29 | self.gridLayout_2.addWidget(self.label_exposure_time, 2, 1, 1, 1)
30 | self.doubleSpinBox_exposure_time = QtWidgets.QDoubleSpinBox(Form)
31 | self.doubleSpinBox_exposure_time.setObjectName("doubleSpinBox_exposure_time")
32 | self.gridLayout_2.addWidget(self.doubleSpinBox_exposure_time, 2, 2, 1, 1)
33 | self.comboBox_beam_current = QtWidgets.QComboBox(Form)
34 | self.comboBox_beam_current.setObjectName("comboBox_beam_current")
35 | self.gridLayout_2.addWidget(self.comboBox_beam_current, 3, 2, 1, 1)
36 | self.pushButton_run_spot_burn = QtWidgets.QPushButton(Form)
37 | self.pushButton_run_spot_burn.setObjectName("pushButton_run_spot_burn")
38 | self.gridLayout_2.addWidget(self.pushButton_run_spot_burn, 6, 1, 1, 2)
39 | self.progressBar = QtWidgets.QProgressBar(Form)
40 | self.progressBar.setProperty("value", 24)
41 | self.progressBar.setObjectName("progressBar")
42 | self.gridLayout_2.addWidget(self.progressBar, 5, 1, 1, 2)
43 | self.label_title = QtWidgets.QLabel(Form)
44 | self.label_title.setObjectName("label_title")
45 | self.gridLayout_2.addWidget(self.label_title, 1, 1, 1, 2)
46 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
47 | self.gridLayout_2.addItem(spacerItem, 7, 1, 1, 2)
48 |
49 | self.retranslateUi(Form)
50 | QtCore.QMetaObject.connectSlotsByName(Form)
51 |
52 | def retranslateUi(self, Form):
53 | _translate = QtCore.QCoreApplication.translate
54 | Form.setWindowTitle(_translate("Form", "Form"))
55 | self.label_beam_current.setText(_translate("Form", "Beam Current"))
56 | self.label_exposure_time.setText(_translate("Form", "Exposure Time"))
57 | self.pushButton_run_spot_burn.setText(_translate("Form", "Run Spot Burn"))
58 | self.label_title.setText(_translate("Form", "Spot Burn"))
59 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemSpotBurnWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 332
10 | 202
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 |
24 | -
25 |
26 |
27 | Beam Current
28 |
29 |
30 |
31 | -
32 |
33 |
34 | Exposure Time
35 |
36 |
37 |
38 | -
39 |
40 |
41 | -
42 |
43 |
44 | -
45 |
46 |
47 | Run Spot Burn
48 |
49 |
50 |
51 | -
52 |
53 |
54 | 24
55 |
56 |
57 |
58 | -
59 |
60 |
61 | Spot Burn
62 |
63 |
64 |
65 | -
66 |
67 |
68 | Qt::Vertical
69 |
70 |
71 |
72 | 20
73 | 40
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemSystemSetupWidget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemSystemSetupWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.9
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(570, 883)
18 | Form.setMinimumSize(QtCore.QSize(0, 450))
19 | font = QtGui.QFont()
20 | font.setPointSize(10)
21 | Form.setFont(font)
22 | self.gridLayout = QtWidgets.QGridLayout(Form)
23 | self.gridLayout.setObjectName("gridLayout")
24 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
25 | self.gridLayout.addItem(spacerItem, 4, 0, 1, 3)
26 | self.comboBox_configuration = QtWidgets.QComboBox(Form)
27 | self.comboBox_configuration.setObjectName("comboBox_configuration")
28 | self.gridLayout.addWidget(self.comboBox_configuration, 0, 1, 1, 1)
29 | self.pushButton_connect_to_microscope = QtWidgets.QPushButton(Form)
30 | font = QtGui.QFont()
31 | font.setPointSize(10)
32 | self.pushButton_connect_to_microscope.setFont(font)
33 | self.pushButton_connect_to_microscope.setObjectName("pushButton_connect_to_microscope")
34 | self.gridLayout.addWidget(self.pushButton_connect_to_microscope, 1, 0, 1, 3)
35 | self.toolButton_import_configuration = QtWidgets.QToolButton(Form)
36 | self.toolButton_import_configuration.setObjectName("toolButton_import_configuration")
37 | self.gridLayout.addWidget(self.toolButton_import_configuration, 0, 2, 1, 1)
38 | self.pushButton_apply_configuration = QtWidgets.QPushButton(Form)
39 | font = QtGui.QFont()
40 | font.setPointSize(10)
41 | self.pushButton_apply_configuration.setFont(font)
42 | self.pushButton_apply_configuration.setObjectName("pushButton_apply_configuration")
43 | self.gridLayout.addWidget(self.pushButton_apply_configuration, 3, 0, 1, 3)
44 | self.label_configuration = QtWidgets.QLabel(Form)
45 | self.label_configuration.setObjectName("label_configuration")
46 | self.gridLayout.addWidget(self.label_configuration, 0, 0, 1, 1)
47 | self.label_connection_information = QtWidgets.QLabel(Form)
48 | font = QtGui.QFont()
49 | font.setPointSize(10)
50 | self.label_connection_information.setFont(font)
51 | self.label_connection_information.setObjectName("label_connection_information")
52 | self.gridLayout.addWidget(self.label_connection_information, 5, 0, 1, 3)
53 |
54 | self.retranslateUi(Form)
55 | QtCore.QMetaObject.connectSlotsByName(Form)
56 |
57 | def retranslateUi(self, Form):
58 | _translate = QtCore.QCoreApplication.translate
59 | Form.setWindowTitle(_translate("Form", "Form"))
60 | self.pushButton_connect_to_microscope.setText(_translate("Form", "Connect to Microscope"))
61 | self.toolButton_import_configuration.setText(_translate("Form", "..."))
62 | self.pushButton_apply_configuration.setText(_translate("Form", "Apply Microscope Configuration"))
63 | self.label_configuration.setText(_translate("Form", "Configuration"))
64 | self.label_connection_information.setText(_translate("Form", "No Connected"))
65 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemSystemSetupWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 570
10 | 883
11 |
12 |
13 |
14 |
15 | 0
16 | 450
17 |
18 |
19 |
20 |
21 | 10
22 |
23 |
24 |
25 | Form
26 |
27 |
28 | -
29 |
30 |
31 | Qt::Vertical
32 |
33 |
34 |
35 | 20
36 | 40
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 | -
45 |
46 |
47 |
48 | 10
49 |
50 |
51 |
52 | Connect to Microscope
53 |
54 |
55 |
56 | -
57 |
58 |
59 | ...
60 |
61 |
62 |
63 | -
64 |
65 |
66 |
67 | 10
68 |
69 |
70 |
71 | Apply Microscope Configuration
72 |
73 |
74 |
75 | -
76 |
77 |
78 | Configuration
79 |
80 |
81 |
82 | -
83 |
84 |
85 |
86 | 10
87 |
88 |
89 |
90 | No Connected
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemUI.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'FibsemUI.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.9
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_MainWindow(object):
15 | def setupUi(self, MainWindow):
16 | MainWindow.setObjectName("MainWindow")
17 | MainWindow.resize(378, 531)
18 | self.centralwidget = QtWidgets.QWidget(MainWindow)
19 | self.centralwidget.setObjectName("centralwidget")
20 | self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
21 | self.gridLayout.setObjectName("gridLayout")
22 | self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
23 | self.tabWidget.setDocumentMode(False)
24 | self.tabWidget.setTabsClosable(False)
25 | self.tabWidget.setTabBarAutoHide(False)
26 | self.tabWidget.setObjectName("tabWidget")
27 | self.gridLayout.addWidget(self.tabWidget, 1, 0, 1, 1)
28 | self.label_title = QtWidgets.QLabel(self.centralwidget)
29 | font = QtGui.QFont()
30 | font.setPointSize(16)
31 | font.setBold(True)
32 | font.setWeight(75)
33 | self.label_title.setFont(font)
34 | self.label_title.setObjectName("label_title")
35 | self.gridLayout.addWidget(self.label_title, 0, 0, 1, 1)
36 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
37 | self.gridLayout.addItem(spacerItem, 2, 0, 1, 1)
38 | MainWindow.setCentralWidget(self.centralwidget)
39 | self.menubar = QtWidgets.QMenuBar(MainWindow)
40 | self.menubar.setGeometry(QtCore.QRect(0, 0, 378, 22))
41 | self.menubar.setObjectName("menubar")
42 | self.menuTools = QtWidgets.QMenu(self.menubar)
43 | self.menuTools.setObjectName("menuTools")
44 | MainWindow.setMenuBar(self.menubar)
45 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
46 | self.statusbar.setObjectName("statusbar")
47 | MainWindow.setStatusBar(self.statusbar)
48 | self.actionCurrent_alignment = QtWidgets.QAction(MainWindow)
49 | self.actionCurrent_alignment.setObjectName("actionCurrent_alignment")
50 | self.actionManipulator_Positions_Calibration = QtWidgets.QAction(MainWindow)
51 | self.actionManipulator_Positions_Calibration.setObjectName("actionManipulator_Positions_Calibration")
52 | self.actionMinimap = QtWidgets.QAction(MainWindow)
53 | self.actionMinimap.setObjectName("actionMinimap")
54 | self.actionOpen_Minimap = QtWidgets.QAction(MainWindow)
55 | self.actionOpen_Minimap.setObjectName("actionOpen_Minimap")
56 | self.actionImage_Viewer = QtWidgets.QAction(MainWindow)
57 | self.actionImage_Viewer.setObjectName("actionImage_Viewer")
58 | self.menuTools.addAction(self.actionManipulator_Positions_Calibration)
59 | self.menuTools.addAction(self.actionOpen_Minimap)
60 | self.menubar.addAction(self.menuTools.menuAction())
61 |
62 | self.retranslateUi(MainWindow)
63 | self.tabWidget.setCurrentIndex(-1)
64 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
65 |
66 | def retranslateUi(self, MainWindow):
67 | _translate = QtCore.QCoreApplication.translate
68 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
69 | self.label_title.setText(_translate("MainWindow", "OpenFIBSEM"))
70 | self.menuTools.setTitle(_translate("MainWindow", "Tools"))
71 | self.actionCurrent_alignment.setText(_translate("MainWindow", "Beam Current Alignment"))
72 | self.actionManipulator_Positions_Calibration.setText(_translate("MainWindow", "Manipulator Callibration"))
73 | self.actionMinimap.setText(_translate("MainWindow", "Minimap"))
74 | self.actionOpen_Minimap.setText(_translate("MainWindow", "Open Minimap"))
75 | self.actionImage_Viewer.setText(_translate("MainWindow", "Image Viewer"))
76 |
--------------------------------------------------------------------------------
/fibsem/ui/qtdesigner_files/FibsemUI.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 378
10 | 531
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 | -1
22 |
23 |
24 | false
25 |
26 |
27 | false
28 |
29 |
30 | false
31 |
32 |
33 |
34 | -
35 |
36 |
37 |
38 | 16
39 | 75
40 | true
41 |
42 |
43 |
44 | OpenFIBSEM
45 |
46 |
47 |
48 | -
49 |
50 |
51 | Qt::Vertical
52 |
53 |
54 |
55 | 20
56 | 40
57 |
58 |
59 |
60 |
61 |
62 |
63 |
81 |
82 |
83 |
84 | Beam Current Alignment
85 |
86 |
87 |
88 |
89 | Manipulator Callibration
90 |
91 |
92 |
93 |
94 | Minimap
95 |
96 |
97 |
98 |
99 | Open Minimap
100 |
101 |
102 |
103 |
104 | Image Viewer
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/fibsem/ui/stylesheets.py:
--------------------------------------------------------------------------------
1 | # style sheets
2 | # TODO: TEMPLATE THIS CSS
3 | GREEN_PUSHBUTTON_STYLE = """
4 | QPushButton {
5 | background-color: green;
6 | }
7 | QPushButton:hover {
8 | background-color: rgba(0, 255, 0, 125);
9 | }"""
10 |
11 | RED_PUSHBUTTON_STYLE = """
12 | QPushButton {
13 | background-color: red;
14 | }
15 | QPushButton:hover {
16 | background-color: rgba(255, 0, 0, 125);
17 | }"""
18 |
19 | BLUE_PUSHBUTTON_STYLE = """
20 | QPushButton {
21 | background-color: blue;
22 | }
23 | QPushButton:hover {
24 | background-color: rgba(0, 0, 255, 125);
25 | }"""
26 |
27 | YELLOW_PUSHBUTTON_STYLE = """
28 | QPushButton {
29 | background-color: yellow;
30 | }
31 | QPushButton:hover {
32 | background-color: rgba(255, 255, 0, 125);
33 | }"""
34 |
35 | WHITE_PUSHBUTTON_STYLE = """
36 | QPushButton {
37 | background-color: white;
38 | color: black;
39 | }
40 | QPushButton:hover {
41 | background-color: rgba(255, 255, 255, 125);
42 | }"""
43 |
44 | GRAY_PUSHBUTTON_STYLE = """
45 | QPushButton {
46 | background-color: gray;
47 | }
48 | QPushButton:hover {
49 | background-color: rgba(125, 125, 125, 125);
50 | }"""
51 |
52 | ORANGE_PUSHBUTTON_STYLE = """
53 | QPushButton {
54 | background-color: orange;
55 | color: black;
56 | }
57 | QPushButton:hover {
58 | background-color: rgba(255, 125, 0, 125);
59 | }"""
60 |
61 | DISABLED_PUSHBUTTON_STYLE = """
62 | QPushButton {
63 | background-color: none;
64 | }
65 | """
66 |
67 | PROGRESS_BAR_GREEN_STYLE = "QProgressBar::chunk {background-color: green;}"
68 | PROGRESS_BAR_BLUE_STYLE = "QProgressBar::chunk {background-color: blue;}"
69 |
--------------------------------------------------------------------------------
/fibsem/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/fibsem/util/__init__.py
--------------------------------------------------------------------------------
/fibsem/util/filename.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | def _get_extension(filename: str) -> str:
4 | if filename.endswith(".ome.tiff"): # special case for OME-TIFF files (double extension)
5 | return ".ome.tiff"
6 | else:
7 | return os.path.splitext(filename)[1]
8 |
9 | def _get_basename(filename: str) -> str:
10 | return filename.removesuffix(_get_extension(filename))
11 |
12 | def _get_basename_and_extension(filename: str) -> tuple:
13 | return _get_basename(filename), _get_extension(filename)
14 |
15 | def get_unique_filename(filename):
16 | if not os.path.exists(filename):
17 | return filename
18 |
19 | basename, ext = _get_basename_and_extension(filename)
20 | idx = 1
21 | while os.path.exists(filename):
22 | filename = f"{basename}-{idx}{ext}"
23 | idx += 1
24 |
25 | return filename
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: FIBSEM Docs
2 |
3 | theme:
4 | name: "material"
5 |
6 | plugins:
7 | - mkdocstrings
8 |
9 | nav:
10 | - index.md
11 | - started.md
12 | - automation.md
13 | - ml.md
14 | - examples.md
15 | - roadmap.md
16 | - reference.md
17 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "fibsem"
7 | version = "0.4.1a0"
8 |
9 | description = "a universal api for fibsem control"
10 | authors = [
11 | {name = "Patrick Cleeve", email = "patrick@openfibsem.org"},
12 | ]
13 | readme = "README.md"
14 | requires-python = ">=3.8"
15 | license = {text = "MIT License"}
16 | classifiers = [
17 | "Programming Language :: Python :: 3.8",
18 | "License :: OSI Approved :: MIT License",
19 | "Operating System :: OS Independent",
20 | "Framework :: napari",
21 | ]
22 |
23 | dependencies = [
24 | "tifffile>=2021.7.2",
25 | "numpy>=1.23.5,<2.0.0",
26 | "scipy>=1.10.0",
27 | "opencv-python-headless>=4.7.0.72", # TODO: test headless dependency
28 | "scikit-image>=0.19.3",
29 | "matplotlib>=3.7.0",
30 | "tqdm>=4.65.0",
31 | "pytest>=7.2.2",
32 | "petname>=2.6",
33 | "pandas>=2.0.0",
34 | "pyyaml>=6.0",
35 | "psygnal"
36 | ]
37 |
38 | [project.urls]
39 | Homepage = "https://github.com/DeMarcoLab/fibsem"
40 | "Bug Tracker" = "https://github.com/DeMarcoLab/fibsem/issues"
41 |
42 | [project.optional-dependencies]
43 | ml = [
44 | "zarr>=2.13.6",
45 | "dask>=2023.3.0",
46 | "torch>=2.0.0,<=2.1.2",
47 | "torchvision>=0.15.1",
48 | "segmentation-models-pytorch>=0.3.2",
49 | "plotly>=5.14.1",
50 | "kaleido==0.2.0",
51 | "transformers>=4.36.2",
52 | ]
53 | ui = [
54 | "napari>=0.4.17,<=0.5.3",
55 | "pyqt5>=5.15.9",
56 | "matplotlib_scalebar>=0.8.1",
57 | ]
58 | odemis = [
59 | "pylibtiff"
60 | ]
61 |
62 | reporting = [
63 | "plotly>=5.14.1",
64 | "reportlab",
65 | "kaleido==0.2.0"
66 | ]
67 |
68 | [project.scripts]
69 | fibsem_ui = "fibsem.ui.FibsemUI:main"
70 | fibsem_label = "fibsem.ui.FibsemLabellingUI:main"
71 | fibsem-generate-config = "fibsem.configuration:gen_config_cli"
72 | fibsem-config-ui = "fibsem.ui.FibsemMicroscopeConfigurationWidget:main"
73 |
74 | [tool.setuptools]
75 | packages = ["fibsem"]
76 |
77 | [tool.setuptools.package-data]
78 | "*" = ["*.yaml"]
--------------------------------------------------------------------------------
/requirements-3.8.txt:
--------------------------------------------------------------------------------
1 | # zarr>=2.13.6
2 | # dask>=2023.3.0
3 | tifffile>=2021.7.2
4 | numpy>=1.23.5
5 | scipy>=1.10.0
6 | opencv-python>=4.7.0.72
7 | scikit-image>=0.19.3
8 | matplotlib>=3.7.0
9 | napari>=0.4.17
10 | pyqt5>=5.15.9
11 | torch>=2.0.0
12 | torchvision>=0.15.1
13 | segmentation-models-pytorch>=0.3.2
14 | tqdm>=4.65.0
15 | pytest>=7.2.2
16 | petname>=2.6
17 | # plotly>=5.14.1
18 | # kaleido==0.2.0
19 | matplotlib_scalebar>=0.8.1
20 | # transformers>=4.36.2
--------------------------------------------------------------------------------
/requirements-headless.txt:
--------------------------------------------------------------------------------
1 | tifffile>=2021.7.2
2 | numpy>=1.23.5,<2.0.0
3 | scipy>=1.10.0
4 | opencv-python>=4.7.0.72
5 | scikit-image>=0.19.3
6 | matplotlib>=3.7.0
7 | pytest>=7.2.2
8 | petname>=2.6
9 | pandas>=2.2.3
10 | pyyaml>=6.0
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | zarr>=2.13.6
2 | dask>=2023.3.0
3 | tifffile>=2021.7.2
4 | numpy>=1.23.5
5 | scipy>=1.10.0
6 | opencv-python>=4.7.0.72
7 | scikit-image>=0.19.3
8 | matplotlib>=3.7.0
9 | napari>=0.4.17
10 | pyqt5>=5.15.9
11 | torch>=2.0.0
12 | torchvision>=0.15.1
13 | segmentation-models-pytorch>=0.3.2
14 | tqdm>=4.65.0
15 | pytest>=7.2.2
16 | petname>=2.6
17 | plotly>=5.14.1
18 | kaleido==0.2.0
19 | matplotlib_scalebar>=0.8.1
20 | transformers>=4.36.2
--------------------------------------------------------------------------------
/scripts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeMarcoLab/fibsem/abfa2efafaba76dd24756afa5a2bdbe88158226f/scripts/.gitkeep
--------------------------------------------------------------------------------
/scripts/compat-notebook.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "## Python 3.8 Compatibility (Headless)"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": null,
13 | "metadata": {},
14 | "outputs": [],
15 | "source": [
16 | "%load_ext autoreload\n",
17 | "%autoreload 2\n",
18 | "\n",
19 | "\n",
20 | "from fibsem import utils, acquire\n",
21 | "import matplotlib.pyplot as plt\n",
22 | "from fibsem.structures import BeamType\n",
23 | "\n",
24 | "from fibsem.milling.patterning.plotting import draw_milling_patterns\n",
25 | "from fibsem.milling import get_milling_stages\n",
26 | "\n",
27 | "\n",
28 | "PROTOCOL_PATH = \"/home/patrick/github/autolamella/autolamella/protocol/protocol-odemis-on-grid.yaml\"\n",
29 | "\n",
30 | "microscope, settings = utils.setup_session(protocol_path=PROTOCOL_PATH)"
31 | ]
32 | },
33 | {
34 | "cell_type": "code",
35 | "execution_count": null,
36 | "metadata": {},
37 | "outputs": [],
38 | "source": []
39 | },
40 | {
41 | "cell_type": "code",
42 | "execution_count": null,
43 | "metadata": {},
44 | "outputs": [],
45 | "source": [
46 | "settings.image.hfw = 80e-6\n",
47 | "settings.image.beam_type = BeamType.ELECTRON\n",
48 | "\n",
49 | "image = acquire.acquire_image(microscope, settings.image)\n",
50 | "\n",
51 | "stages = get_milling_stages(\"lamella\", settings.protocol[\"milling\"])\n",
52 | "\n",
53 | "fig = draw_milling_patterns(image, stages)\n"
54 | ]
55 | },
56 | {
57 | "cell_type": "code",
58 | "execution_count": null,
59 | "metadata": {},
60 | "outputs": [],
61 | "source": []
62 | },
63 | {
64 | "cell_type": "markdown",
65 | "metadata": {},
66 | "source": [
67 | "## v0.4.0 Refactor "
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "metadata": {},
74 | "outputs": [],
75 | "source": [
76 | "%load_ext autoreload\n",
77 | "%autoreload 2\n",
78 | "\n",
79 | "# convert milling tasks to milling stages\n",
80 | "import os\n",
81 | "import time\n",
82 | "from fibsem.microscopes.odemis_microscope import add_odemis_path\n",
83 | "add_odemis_path()\n",
84 | "\n",
85 | "from odemis.acq.milling.tasks import load_milling_tasks\n",
86 | "from odemis.acq.milling.tasks import __file__ as milling_tasks_file\n",
87 | "from odemis.acq.milling.openfibsem import run_milling_tasks_openfibsem\n",
88 | "\n",
89 | "MILLING_TASKS_PATH = os.path.join(os.path.dirname(milling_tasks_file), \"milling_tasks.yaml\")\n",
90 | "milling_tasks = load_milling_tasks(MILLING_TASKS_PATH)\n"
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": null,
96 | "metadata": {},
97 | "outputs": [],
98 | "source": [
99 | "tasks = list(milling_tasks.values())[:2]\n",
100 | "f = run_milling_tasks_openfibsem(tasks)\n",
101 | "# time.sleep(10)\n",
102 | "# f.cancel()\n",
103 | "f.result()"
104 | ]
105 | }
106 | ],
107 | "metadata": {
108 | "kernelspec": {
109 | "display_name": "fibsem-headless",
110 | "language": "python",
111 | "name": "python3"
112 | },
113 | "language_info": {
114 | "codemirror_mode": {
115 | "name": "ipython",
116 | "version": 3
117 | },
118 | "file_extension": ".py",
119 | "mimetype": "text/x-python",
120 | "name": "python",
121 | "nbconvert_exporter": "python",
122 | "pygments_lexer": "ipython3",
123 | "version": "3.9.19"
124 | }
125 | },
126 | "nbformat": 4,
127 | "nbformat_minor": 2
128 | }
129 |
--------------------------------------------------------------------------------
/scripts/convert_to_nnunet_dataset.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from fibsem.segmentation._nnunet import convert_to_nnunet_dataset
3 |
4 | # Convert a fibsem dataset to nnunet format
5 |
6 | def main():
7 | parser = argparse.ArgumentParser(description="Convert a dataset to nnunet format")
8 | parser.add_argument("--data_path", type=str, help="path to data")
9 | parser.add_argument("--label_path", type=str, help="path to labels")
10 | parser.add_argument("--nnunet_data_path", type=str, help="path to nnunet data")
11 | parser.add_argument(
12 | "--label_map", type=str, help="label map", required=False, default=None
13 | )
14 | parser.add_argument(
15 | "--filetype", type=str, help="filetype", required=False, default=".tif"
16 | )
17 |
18 | args = parser.parse_args()
19 |
20 | if args.label_map is not None:
21 | print(args.label_map)
22 | # open text file, read each line, and add to list of labels, remove newline character
23 | with open(args.label_map, "r") as f:
24 | labels = [line.rstrip("\n") for line in f]
25 | args.label_map = labels
26 |
27 | # print(args.data_path)
28 | # print(args.label_path)
29 | # print(args.nnunet_data_path)
30 | # print(args.label_map)
31 | # print(args.filetype)
32 |
33 | # return
34 | convert_to_nnunet_dataset(
35 | data_path=args.data_path,
36 | label_path=args.label_path,
37 | nnunet_data_path=args.nnunet_data_path,
38 | label_map=args.label_map,
39 | filetype=args.filetype,
40 | )
41 |
42 |
43 | if __name__ == "__main__":
44 | main()
45 |
--------------------------------------------------------------------------------
/scripts/convert_to_onnx.py:
--------------------------------------------------------------------------------
1 | from fibsem.segmentation.onnx_model import export_model_to_onnx
2 | import argparse
3 |
4 |
5 | def main():
6 |
7 | parser = argparse.ArgumentParser()
8 | parser.add_argument(
9 | "--checkpoint",
10 | type=str,
11 | help="path to openfibsem model checkpoint",
12 | required=True,
13 | )
14 | parser.add_argument(
15 | "--output",
16 | type=str,
17 | help="path to save onnx model",
18 | required=True,
19 | )
20 | args = parser.parse_args()
21 | export_model_to_onnx(args.checkpoint, args.output)
22 |
23 |
24 |
25 |
26 | if __name__ == "__main__":
27 | main()
--------------------------------------------------------------------------------
/scripts/export_nnunet_checkpoint.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import json
3 | import os
4 | import torch
5 |
6 | import argparse
7 |
8 |
9 | def export_model_checkpoint(
10 | path: str,
11 | checkpoint_path: str = None,
12 | checkpoint_name: str = "model_checkpoint.pth",
13 | ) -> None:
14 | """Save NNUNet model checkpoint as a single .pth file
15 | args:
16 | path: path to the nnunet model directory
17 |
18 | """
19 | # nnunet model directory structure for ensemble:
20 | # model
21 | # dataset.json
22 | # plans.json
23 | # fold_n:
24 | # checkpoint_best.pth
25 | # checkpoint_final.pth
26 |
27 | # we want to convert it to a single .pth file with the following structure:
28 | # model_checkpoint.pth
29 | # dataset: dataset.json
30 | # plans: plans.json
31 | # fold_n:
32 | # best: checkpoint_best.pth
33 | # final: checkpoint_final.pth
34 |
35 | # this makes it more portable and easier to load
36 |
37 | def load_json(path: str):
38 | with open(path, "r") as f:
39 | return json.load(f)
40 |
41 | # confirm that the path is a nnunet model directory
42 | if not os.path.isdir(path):
43 | raise ValueError(f"{path} is not a directory")
44 | if not os.path.exists(os.path.join(path, "dataset.json")):
45 | raise ValueError(f"{path} does not contain a dataset.json file")
46 | if not os.path.exists(os.path.join(path, "plans.json")):
47 | raise ValueError(f"{path} does not contain a plans.json file")
48 |
49 | print(f"Exporting model checkpoint from {path}...")
50 |
51 | MODEL_CHECKPOINT = {}
52 |
53 | # paths
54 | DATASET_JSON_PATH = os.path.join(path, "dataset.json")
55 | PLAN_JSON_PATH = os.path.join(path, "plans.json")
56 |
57 | # load the dataset and plans
58 | print("Loading dataset and plans configurations...")
59 | MODEL_CHECKPOINT["dataset"] = load_json(DATASET_JSON_PATH)
60 | MODEL_CHECKPOINT["plans"] = load_json(PLAN_JSON_PATH)
61 |
62 | # load the folds
63 | MODEL_CHECKPOINT["folds"] = {}
64 |
65 | # get all the fold directories,
66 | FOLD_DIRS = sorted(glob.glob(os.path.join(path, "fold_*")))
67 | print(f"Found {len(FOLD_DIRS)} folds...")
68 | for fold_dir in FOLD_DIRS:
69 | fold_name = os.path.basename(fold_dir)
70 | print(f"Processing fold {fold_name}...")
71 |
72 | # load the best/ final checkpoint
73 | BEST_CHECKPOINT_PATH = os.path.join(fold_dir, "checkpoint_best.pth")
74 | FINAL_CHECKPOINT_PATH = os.path.join(fold_dir, "checkpoint_final.pth")
75 |
76 | MODEL_CHECKPOINT["folds"][fold_name] = {
77 | "best": torch.load(BEST_CHECKPOINT_PATH, map_location=torch.device("cpu")),
78 | "final": torch.load(
79 | FINAL_CHECKPOINT_PATH, map_location=torch.device("cpu")
80 | ),
81 | }
82 |
83 | # save as single torch checkpoint
84 | if checkpoint_path is None:
85 | checkpoint_path = os.path.join(path, checkpoint_name)
86 | torch.save(MODEL_CHECKPOINT, checkpoint_path)
87 | print(f"Saved model checkpoint to {checkpoint_path}")
88 |
89 |
90 | def main():
91 | parser = argparse.ArgumentParser(
92 | description="Export nnunet model checkpoint as a single .pth file"
93 | )
94 | parser.add_argument("--path", type=str, help="path to nnunet model directory")
95 | parser.add_argument(
96 | "--checkpoint_path",
97 | type=str,
98 | help="path to save the checkpoint",
99 | required=False,
100 | default=None,
101 | )
102 | parser.add_argument(
103 | "--checkpoint_name",
104 | type=str,
105 | help="name of the checkpoint",
106 | required=False,
107 | default="model_checkpoint.pth",
108 | )
109 |
110 | args = parser.parse_args()
111 |
112 | export_model_checkpoint(
113 | path=args.path,
114 | checkpoint_path=args.checkpoint_path,
115 | checkpoint_name=args.checkpoint_name,
116 | )
117 |
118 |
119 | if __name__ == "__main__":
120 | main()
121 |
--------------------------------------------------------------------------------
/scripts/generate_segmentation_objects.py:
--------------------------------------------------------------------------------
1 | #
2 | import argparse
3 | import os
4 |
5 | from fibsem.detection.detection import generate_segmentation_objects
6 |
7 | def main():
8 |
9 | parser = argparse.ArgumentParser("Generate segmentation objects from segmentation labels")
10 | parser.add_argument("--data_path", type=str, help="Path to data directory")
11 | parser.add_argument("--labels_path", type=str, help="Path to labels directory")
12 | parser.add_argument("--dataset_json_path", type=str, default=None, help="Path to save dataset json")
13 | parser.add_argument("--min_pixels", type=int, default=100, help="Minimum number of pixels for an object to be considered")
14 |
15 | args = parser.parse_args()
16 |
17 | if args.dataset_json_path is None:
18 | args.dataset_json_path = os.path.join(args.data_path, "data.json")
19 |
20 | generate_segmentation_objects(
21 | data_path=args.data_path,
22 | labels_path=args.labels_path,
23 | dataset_json_path=args.dataset_json_path,
24 | min_pixels=args.min_pixels,
25 | save=True
26 | )
27 |
28 |
29 | if __name__ == "__main__":
30 | main()
--------------------------------------------------------------------------------
/scripts/install.bat:
--------------------------------------------------------------------------------
1 | CALL conda.bat create -n fibsem python=3.9 pip
2 | CALL activate fibsem
3 | pip install -e .
4 | python shortcut.py
--------------------------------------------------------------------------------
/scripts/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # install fibsem using pip
4 | pip install -e .
5 |
6 |
7 |
--------------------------------------------------------------------------------
/scripts/onnx_notebook.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### ONNX Windowed Model Integration"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": null,
13 | "metadata": {},
14 | "outputs": [],
15 | "source": [
16 | "%load_ext autoreload\n",
17 | "%autoreload 2\n",
18 | "import glob\n",
19 | "import os\n",
20 | "import numpy as np\n",
21 | "import matplotlib.pyplot as plt\n",
22 | "import PIL.Image\n",
23 | "\n",
24 | "from fibsem.segmentation.model import load_model\n",
25 | "from fibsem.structures import FibsemImage\n",
26 | "\n",
27 | "# image filenames\n",
28 | "PATH = \"example_imgs/input\"\n",
29 | "filenames = glob.glob(PATH + \"/*.jpeg\")\n",
30 | "\n",
31 | "# PATH = \"/home/patrick/github/data/autolamella-paper/model-development/train/waffle/test\"\n",
32 | "# filenames = glob.glob(PATH + \"/*.tif\")\n",
33 | "\n",
34 | "# load model\n",
35 | "MODEL_PATH = \"ppliteseg_fibsem_07022024_512x512_128k.onnx\"\n",
36 | "model = load_model(checkpoint=MODEL_PATH)\n",
37 | "\n",
38 | "os.makedirs(\"example_imgs/output/test\", exist_ok=True)\n",
39 | "\n",
40 | "for i, filename in enumerate(filenames):\n",
41 | " print(f\"Processing {i+1}/{len(filenames)}: {filename}\")\n",
42 | "\n",
43 | " # load image\n",
44 | " if \"tif\" in filename:\n",
45 | " image = FibsemImage.load(filename)\n",
46 | " else:\n",
47 | " image = FibsemImage(data=np.asarray(PIL.Image.open(filename)))\n",
48 | " \n",
49 | " # inference\n",
50 | " rgb = model.inference(image.data)\n",
51 | "\n",
52 | " fig = plt.figure(figsize=(10, 10))\n",
53 | " plt.title(f\"Predicted: {os.path.basename(filename)}\", fontsize=10)\n",
54 | " plt.imshow(image.data, cmap=\"gray\")\n",
55 | " plt.imshow(rgb, alpha=0.5)\n",
56 | " plt.axis(\"off\")\n",
57 | " plt.show()\n",
58 | "\n",
59 | " # save figure\n",
60 | " fig.savefig(f\"example_imgs/output/test/{os.path.basename(filename)}\".replace(\".tif\", \".png\"), bbox_inches=\"tight\")\n",
61 | " plt.close(fig)"
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "metadata": {},
68 | "outputs": [],
69 | "source": []
70 | },
71 | {
72 | "cell_type": "code",
73 | "execution_count": null,
74 | "metadata": {},
75 | "outputs": [],
76 | "source": []
77 | }
78 | ],
79 | "metadata": {
80 | "kernelspec": {
81 | "display_name": "fibsem",
82 | "language": "python",
83 | "name": "python3"
84 | },
85 | "language_info": {
86 | "codemirror_mode": {
87 | "name": "ipython",
88 | "version": 3
89 | },
90 | "file_extension": ".py",
91 | "mimetype": "text/x-python",
92 | "name": "python",
93 | "nbconvert_exporter": "python",
94 | "pygments_lexer": "ipython3",
95 | "version": "3.9.18"
96 | }
97 | },
98 | "nbformat": 4,
99 | "nbformat_minor": 2
100 | }
101 |
--------------------------------------------------------------------------------
/scripts/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | fibsem_ui
--------------------------------------------------------------------------------
/scripts/run_ui.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | CALL conda.bat activate fibsem
3 | fibsem_ui
4 | pause
--------------------------------------------------------------------------------
/scripts/shortcut.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import win32com.client
4 | from fibsem.config import BASE_PATH
5 | # Get the user's desktop folder
6 | desktop = os.path.join(os.path.expanduser('~'), 'Desktop')
7 |
8 | # Specify the target file (e.g., a script or program you want to create a shortcut for)
9 | target_file = os.path.join(BASE_PATH, "run_ui.bat") # Replace with your program's path
10 |
11 | # Create a shortcut name
12 | shortcut_name = 'FibsemUI.lnk'
13 |
14 | # Create a shortcut on the desktop
15 | shell = win32com.client.Dispatch("WScript.Shell")
16 | shortcut = shell.CreateShortCut(os.path.join(desktop, shortcut_name))
17 | shortcut.TargetPath = target_file
18 | shortcut.save()
19 |
20 | print(f"Shortcut to '{target_file}' created on the desktop as '{shortcut_name}'")
--------------------------------------------------------------------------------
/scripts/test-alignment-script.py:
--------------------------------------------------------------------------------
1 |
2 | from fibsem import utils, acquire, alignment
3 | from fibsem.structures import BeamType, FibsemRectangle
4 | import numpy as np
5 | import matplotlib.pyplot as plt
6 |
7 |
8 | def main():
9 | microscope, settings = utils.setup_session()
10 |
11 | # parameters
12 | beam_type = BeamType.ION
13 | scan_rotation = 180 # deg
14 |
15 | # set scan rotation
16 | microscope.set("scan_rotation", np.radians(scan_rotation), beam_type=beam_type)
17 |
18 | # reset beam shifts
19 | microscope.reset_beam_shifts()
20 |
21 | # acquire FIB Image
22 | settings.image.hfw = 150e-6
23 | settings.image.beam_type = beam_type
24 | settings.image.reduced_area = FibsemRectangle(0.25, 0.25, 0.5, 0.5)
25 | image1 = acquire.acquire_image(microscope=microscope, settings=settings.image)
26 |
27 | # shift beam shift
28 | microscope.beam_shift(dx=10e-6, dy=5e-6, beam_type=beam_type)
29 |
30 | # acquire FIB image
31 | image2 = acquire.acquire_image(microscope=microscope, settings=settings.image)
32 |
33 | # align beam shift
34 | alignment.multi_step_alignment_v2(microscope, ref_image=image1, beam_type=beam_type, use_autocontrast=True)
35 |
36 | # acquire FIB Image
37 | image3 = acquire.acquire_image(microscope=microscope, settings=settings.image)
38 |
39 | # plot
40 | fig, ax = plt.subplots(1, 3, figsize=(15, 7))
41 | fig.suptitle(f"Test Alignment: {beam_type.name} - Scan Rotation: {scan_rotation}deg")
42 | ax[0].imshow(image1.data, cmap='gray')
43 | ax[0].set_title('Previous Image')
44 | ax[0].axis('off')
45 | ax[1].imshow(image2.data, cmap='gray')
46 | ax[1].set_title('Shifted Image')
47 | ax[1].axis('off')
48 | ax[2].imshow(image3.data, cmap='gray')
49 | ax[2].set_title('Aligned Image')
50 | ax[2].axis('off')
51 |
52 | # plot center crosshair
53 | ax[0].plot([image1.data.shape[1] // 2, image1.data.shape[1] // 2], [0, image1.data.shape[0]], color='yellow', lw=1)
54 | ax[0].plot([0, image1.data.shape[1]], [image1.data.shape[0] // 2, image1.data.shape[0] // 2], color='yellow', lw=1)
55 | ax[1].plot([image2.data.shape[1] // 2, image2.data.shape[1] // 2], [0, image2.data.shape[0]], color='yellow', lw=1)
56 | ax[1].plot([0, image2.data.shape[1]], [image2.data.shape[0] // 2, image2.data.shape[0] // 2], color='yellow', lw=1)
57 | ax[2].plot([image3.data.shape[1] // 2, image3.data.shape[1] // 2], [0, image3.data.shape[0]], color='yellow', lw=1)
58 | ax[2].plot([0, image3.data.shape[1]], [image3.data.shape[0] // 2, image3.data.shape[0] // 2], color='yellow', lw=1)
59 | plt.show()
60 |
61 |
62 | if __name__ == "__main__":
63 | main()
--------------------------------------------------------------------------------
/scripts/test_installation.py:
--------------------------------------------------------------------------------
1 | from fibsem.microscope import THERMO_API_AVAILABLE
2 | from fibsem.microscopes.tescan import TESCAN_API_AVAILABLE
3 |
4 | def main():
5 |
6 | # OpenFIBSEM API
7 | print(f"\n\nOpenFIBSEM API:\n")
8 | try:
9 | import fibsem
10 | FIBSEM_AVAILABLE = True
11 | except ImportError:
12 | FIBSEM_AVAILABLE = False
13 | if FIBSEM_AVAILABLE:
14 | print(f"OpenFIBSEM v{fibsem.__version__}")
15 | print(f"Installed at: {fibsem.__path__}")
16 |
17 | print(f"-" * 80)
18 |
19 | print(f"Applications:\n")
20 | try:
21 | import autolamella
22 | AUTOLAMELLA_AVAILABLE = True
23 | except ImportError:
24 | AUTOLAMELLA_AVAILABLE = False
25 | if AUTOLAMELLA_AVAILABLE:
26 | print(f"AutoLamella v{autolamella.__version__}")
27 | print(f"Installed at: {autolamella.__path__}")
28 |
29 | try:
30 | import salami
31 | SALAMI_AVAILABLE = True
32 | except ImportError:
33 | SALAMI_AVAILABLE = False
34 | if SALAMI_AVAILABLE:
35 | print(f"SALAMI v{salami.__version__}")
36 | print(f"Installed at: {salami.__path__}")
37 | print(f"-" * 80)
38 |
39 | # Hardware APIs
40 | print(f"Hardware APIs:\n")
41 |
42 | # Thermo Fisher API
43 | print(f"ThermoFisher API {'Available' if THERMO_API_AVAILABLE else 'Not Available'}")
44 | if THERMO_API_AVAILABLE:
45 | from fibsem.microscope import version as autoscript_version
46 | print(f"AutoScript v{autoscript_version}")
47 | print(f"-" * 80)
48 |
49 | # Tescan API
50 | print(f"Tescan API {'Available' if TESCAN_API_AVAILABLE else 'Not Available'}")
51 | if TESCAN_API_AVAILABLE:
52 | from fibsem.microscopes.tescan import tescanautomation
53 | print(f"TescanAutomation v{tescanautomation.__version__}")
54 |
55 | if __name__ == "__main__":
56 | main()
--------------------------------------------------------------------------------
/tests/milling/test_base.py:
--------------------------------------------------------------------------------
1 | from fibsem.milling.base import (
2 | FibsemMillingStage,
3 | MillingStrategy,
4 | get_strategy,
5 | )
6 | from fibsem.milling.patterning.patterns2 import RectanglePattern
7 | from fibsem.milling.strategy import DEFAULT_STRATEGY, get_strategies
8 | from fibsem.structures import FibsemMillingSettings, MillingAlignment, Point
9 |
10 | def test_milling_stage():
11 |
12 | milling_settings = FibsemMillingSettings()
13 | pattern = RectanglePattern(width=10, height=5, depth=1)
14 | strategy = get_strategy("Standard")
15 | alignment = MillingAlignment(enabled=True)
16 |
17 | # Create a FibsemMillingStage instance
18 | milling_stage = FibsemMillingStage(
19 | name="Test Stage",
20 | num=1,
21 | milling=milling_settings,
22 | pattern=pattern,
23 | strategy=strategy,
24 | alignment=alignment,
25 | )
26 |
27 | # Check the attributes
28 | assert milling_stage.name == "Test Stage"
29 | assert milling_stage.num == 1
30 | assert isinstance(milling_stage.milling, FibsemMillingSettings)
31 | assert isinstance(milling_stage.pattern, RectanglePattern)
32 | assert isinstance(milling_stage.strategy, MillingStrategy)
33 | assert isinstance(milling_stage.alignment, MillingAlignment)
34 | assert milling_stage.pattern.width == 10
35 | assert milling_stage.pattern.height == 5
36 | assert milling_stage.pattern.depth == 1
37 | assert milling_stage.strategy.name == DEFAULT_STRATEGY.name
38 | assert milling_stage.alignment.enabled is True
39 |
40 | # test to_dict method
41 | dict_repr = milling_stage.to_dict()
42 | assert dict_repr["name"] == "Test Stage"
43 | assert dict_repr["num"] == 1
44 | assert isinstance(dict_repr["milling"], dict)
45 | assert isinstance(dict_repr["pattern"], dict)
46 | assert isinstance(dict_repr["strategy"], dict)
47 | assert isinstance(dict_repr["alignment"], dict)
48 |
49 | # test from_dict method
50 | dict_repr = {
51 | "name": "Test Stage",
52 | "num": 1,
53 | "milling": milling_settings.to_dict(),
54 | "pattern": pattern.to_dict(),
55 | "strategy": strategy.to_dict(),
56 | "alignment": alignment.to_dict(),
57 | }
58 | new_milling_stage = FibsemMillingStage.from_dict(dict_repr)
59 |
60 |
61 | assert new_milling_stage.name == "Test Stage"
62 | assert new_milling_stage.num == 1
63 | assert isinstance(new_milling_stage.milling, FibsemMillingSettings)
64 | assert isinstance(new_milling_stage.pattern, RectanglePattern)
65 | assert isinstance(new_milling_stage.strategy, MillingStrategy)
66 | assert isinstance(new_milling_stage.alignment, MillingAlignment)
67 | assert new_milling_stage.imaging.path is None
68 |
69 | def test_get_strategy():
70 | # Test with default strategy
71 | strategy = get_strategy()
72 | assert isinstance(strategy, MillingStrategy)
73 | assert strategy.name == DEFAULT_STRATEGY.name
74 |
75 | # Test with a specific strategy name
76 | strategy_name = "Standard"
77 | strategy = get_strategy(name=strategy_name)
78 | assert isinstance(strategy, MillingStrategy)
79 | assert strategy.name == strategy_name
80 |
81 | # Test with a non-existent strategy name
82 | strategy = get_strategy(name="NonExistentStrategy")
83 | assert isinstance(strategy, MillingStrategy)
84 | assert strategy.name == DEFAULT_STRATEGY.name
85 |
--------------------------------------------------------------------------------
/tests/test_acquire.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pytest
3 |
4 | from fibsem import acquire, utils
5 | from fibsem.structures import (
6 | FibsemImage,
7 | FibsemRectangle,
8 | )
9 |
10 | def test_reduced_area_acquisition():
11 | """Test the reduced area acquisition functionality of the acquire module."""
12 | # setup a demo microscope session
13 | microscope, settings = utils.setup_session(manufacturer="Demo")
14 |
15 | resolution = settings.image.resolution
16 |
17 | # acquire a full frame image
18 | image = acquire.acquire_image(microscope, settings.image)
19 | assert isinstance(image, FibsemImage)
20 | assert image.data.shape == (resolution[1], resolution[0])
21 |
22 | # acquire a reduced area image
23 | settings.image.reduced_area = FibsemRectangle(0.25, 0.25, 0.5, 0.5)
24 | image = acquire.acquire_image(microscope, settings.image)
25 | assert isinstance(image, FibsemImage)
26 | assert image.data.shape == (resolution[1] // 2, resolution[0] // 2)
--------------------------------------------------------------------------------
/tests/test_alignment.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from fibsem import utils, acquire, alignment
3 | import random
4 | import numpy as np
5 |
6 | def test_align_from_crosscorrelation():
7 |
8 | microscope, settings = utils.setup_session(debug=False)
9 |
10 | # create random images
11 | ref_image = acquire.acquire_image(microscope, settings.image)
12 | new_image = acquire.acquire_image(microscope, settings.image)
13 |
14 | ref_image.data[:] = 0
15 | new_image.data[:] = 0
16 |
17 | # crop a random square out
18 | w = h = 150
19 | offset_limits = (-50, 50)
20 | x = random.randint(
21 | 0 - offset_limits[0], ref_image.data.shape[1] - offset_limits[1] - 2 * w
22 | )
23 | y = random.randint(
24 | 0 - offset_limits[0], ref_image.data.shape[0] - offset_limits[1] - 2 * h
25 | )
26 | ref_image.data[y:y+h, x:x+w] = 255
27 |
28 | # new image should be offset by 250 pixels in x and y
29 | offset = random.randint(offset_limits[0], offset_limits[1])
30 | new_image.data[y+offset:y+h+offset, x+offset:x+w+offset] = 255
31 |
32 | dx, dy, xcorr = alignment.shift_from_crosscorrelation(
33 | ref_image, new_image, lowpass=50, highpass=4, sigma=5, use_rect_mask=True
34 | )
35 |
36 | # write a test case with pytest for this case
37 | # test that the shift is within 1 pixel of the offset
38 |
39 | pixel_size = ref_image.metadata.pixel_size.x
40 | assert np.isclose(dx, offset*pixel_size, atol=pixel_size), f"dx: {dx}, offset: {offset*pixel_size}"
41 | assert np.isclose(dy, offset*pixel_size, atol=pixel_size), f"dy: {dy}, offset: {offset*pixel_size}"
--------------------------------------------------------------------------------
/tests/test_example.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def test_example():
5 |
6 | assert 1 + 1 == 2
--------------------------------------------------------------------------------
/tests/test_microscope.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from fibsem import utils
4 | from fibsem.structures import BeamType
5 |
6 |
7 | def test_microscope():
8 | """Test get/set microscope functions."""
9 |
10 | microscope, settings = utils.setup_session(manufacturer="Demo")
11 |
12 | hfw = 150e-6
13 | microscope.set_field_of_view(hfw, BeamType.ELECTRON)
14 | assert microscope.get_field_of_view(BeamType.ELECTRON) == hfw
15 |
16 | beam_current = 1e-9
17 | microscope.set_beam_current(beam_current, BeamType.ION)
18 | assert microscope.get_beam_current(BeamType.ION) == beam_current
--------------------------------------------------------------------------------
/tests/test_movement.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import numpy as np
3 |
4 | from fibsem import movement
5 |
6 | def test_angle_difference():
7 | assert np.isclose(movement.angle_difference(np.deg2rad(0), np.deg2rad(0)), 0)
8 | assert np.isclose(movement.angle_difference(np.deg2rad(0), np.deg2rad(360)), 0)
9 | assert np.isclose(movement.angle_difference(np.deg2rad(0), np.deg2rad(180)), np.pi)
10 | assert np.isclose(movement.angle_difference(np.deg2rad(2), np.deg2rad(358)), np.deg2rad(4))
11 | assert np.isclose(movement.angle_difference(np.deg2rad(-45), np.deg2rad(45)), np.pi / 2)
12 | assert np.isclose(movement.angle_difference(np.deg2rad(-360), np.deg2rad(360)), 0)
13 | assert np.isclose(movement.angle_difference(-4*np.pi, 4*np.pi), 0)
14 |
15 | def test_rotation_angle_is_larger():
16 | assert movement.rotation_angle_is_larger(np.deg2rad(0), np.deg2rad(0)) == False
17 | assert movement.rotation_angle_is_larger(np.deg2rad(0), np.deg2rad(360)) == False
18 | assert movement.rotation_angle_is_larger(np.deg2rad(-45), np.deg2rad(45)) == False
19 | assert movement.rotation_angle_is_larger(np.deg2rad(0), np.deg2rad(180)) == True
20 | assert movement.rotation_angle_is_larger(np.deg2rad(-90), np.deg2rad(90)) == True
21 | assert movement.rotation_angle_is_larger(np.deg2rad(0), np.deg2rad(720)) == False
22 |
23 |
24 | def test_rotation_angle_is_smaller():
25 | assert movement.rotation_angle_is_smaller(np.deg2rad(0), np.deg2rad(0), 5) == True
26 | assert movement.rotation_angle_is_smaller(np.deg2rad(0), np.deg2rad(360), 5) == True
27 | assert movement.rotation_angle_is_smaller(np.deg2rad(0), np.deg2rad(180)) == False
28 | assert movement.rotation_angle_is_smaller(np.deg2rad(-90), np.deg2rad(90), 180) == False
29 | assert movement.rotation_angle_is_smaller(np.deg2rad(0), np.deg2rad(-360), 1) == True
30 |
--------------------------------------------------------------------------------
/tests/test_tescan.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def test_tescan():
5 |
6 | assert True
--------------------------------------------------------------------------------