Skip to content

Commit

Permalink
v0.2.2 Release (#156)
Browse files Browse the repository at this point in the history
### Highlights

- OpenFIBSEM is now available on PyPI. Use pip to install: `pip install fibsem`. On ThermoFisher systems, OpenFIBSEM will automatically find your Autoscript installation if it installed. On Tescan, please install into the same environment as the Automation API.
- Minimap: Added a minimap widget for collecting tiled images, selecting positions and correlation. Provides an overview of the current stage position and the positions of the selected locations. Also provides an integrated correlation user interface. You can use the minimap to select locations for other applications, such as AutoLamella.  

### Features

- Added a _safe_absolute_stage_movement. This function will tilt flat before performing large movements to prevent collions.
- Added cleaning_cross_section and scan_direction to the milling widget user interface.
- Rectangle Patterns now sputter a 'passes' parameter. This allows you to explicitly set the number of passes the beam will scan.
- Adjusted the milling widget to allow for the selection of multiple milling stages. This allows you to move multiple stages together.
- Added automatic logging for alignment data. All alignment data is now logged to a file in the log/crosscorrelation directory. You can change this log directory in the config.
- Added a cryo sputter widget for automated sputtering in cryo conditions.
- Added two way projection between image and stage coordinates. This allows you to click on an image and move the stage to that location, as well as project a stage coordinate to an image coordinate (currently located in fibsem.imaging._tile).
- To move milling stages in the UI, you now need to 'Shift' + 'Left Click' (Was 'Right Click')
- To move the stage vertically (eucentric_move), you now need to 'Alt' + 'Left Click' (Was previously an option in the UI).

### Fixes / Updates

- Fixed an issue where masks were not calculated for alignment.correct_stage_drift.
- Changed the model checkpoint lookup to search the fibsem/segmentation/models directory instead of expecting an absolute path.
- Fixed an issue where coordinate system was flipped when moving using a detection.
- Fixed an issue where milling protocols were being overwritten when setting the milling stages directly. [USER-INTERFACE]
- The milling widget hfw should now update automatically when changing the imaging settings. [USER-INTERFACE]
- The user interface won't try to draw the cross hair if no image is available. [USER-INTERFACE]
- Explicitly converting the last_image to np.uint8 (was np.uint16) [THERMO]
- Explictly settings the manipulator coordinate system when performing movements [THERMO]
- Post milling current now set to 30keV: 150 pA instead of 30keV: UHR Imaging [TESCAN]
- Fixed milling rate conversions, where the milling rate units were not converted correctly [TESCAN]
  • Loading branch information
patrickcleeve2 authored Aug 2, 2023
1 parent f832c65 commit ba7ea68
Show file tree
Hide file tree
Showing 63 changed files with 6,264 additions and 2,818 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- v0.2-stable
pull_request:
branches:
- v0.2-stable
- v0.2-release
jobs:
deploy:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Python package

on:
pull_request:
branches: [ "v0.2-stable" ]
branches: [ "v0.2-release" ]

jobs:
build:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ fibsem/segmentation/models/*
example/**/*.tif
example/demo/*
fibsem/chat/secret.txt

dist/*
scratch/tile-images/*
fibsem/log/*
57 changes: 43 additions & 14 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
## Changes
# Changes

### 12/07/2023
## v0.2.2 - 31/07/2023

### Highlights

- OpenFIBSEM is now available on PyPI. Use pip to install: `pip install fibsem`. On ThermoFisher systems, OpenFIBSEM will automatically find your Autoscript installation if it installed. On Tescan, please install into the same environment as the Automation API.
- Minimap: Added a minimap widget for collecting tiled images, selecting positions and correlation. Provides an overview of the current stage position and the positions of the selected locations. Also provides an integrated correlation user interface. You can use the minimap to select locations for other applications, such as AutoLamella.

### Features

- Added a _safe_absolute_stage_movement. This function will tilt flat before performing large movements to prevent collions.
- Added cleaning_cross_section and scan_direction to the milling widget user interface.
- Rectangle Patterns now sputter a 'passes' parameter. This allows you to explicitly set the number of passes the beam will scan.
- Adjusted the milling widget to allow for the selection of multiple milling stages. This allows you to move multiple stages together.
- Added automatic logging for alignment data. All alignment data is now logged to a file in the log/crosscorrelation directory. You can change this log directory in the config.
- Added a cryo sputter widget for automated sputtering in cryo conditions.
- Added two way projection between image and stage coordinates. This allows you to click on an image and move the stage to that location, as well as project a stage coordinate to an image coordinate (currently located in fibsem.imaging._tile).
- To move milling stages in the UI, you now need to 'Shift' + 'Left Click' (Was 'Right Click')
- To move the stage vertically (eucentric_move), you now need to 'Alt' + 'Left Click' (Was previously an option in the UI).

### Fixes / Updates

- Fixed an issue where masks were not calculated for alignment.correct_stage_drift.
- Changed the model checkpoint lookup to search the fibsem/segmentation/models directory instead of expecting an absolute path.
- Fixed an issue where coordinate system was flipped when moving using a detection.
- Fixed an issue where milling protocols were being overwritten when setting the milling stages directly. [USER-INTERFACE]
- The milling widget hfw should now update automatically when changing the imaging settings. [USER-INTERFACE]
- The user interface won't try to draw the cross hair if no image is available. [USER-INTERFACE]
- Explicitly converting the last_image to np.uint8 (was np.uint16) [THERMO]
- Explictly settings the manipulator coordinate system when performing movements [THERMO]
- Post milling current now set to 30keV: 150 pA instead of 30keV: UHR Imaging [TESCAN]
- Fixed milling rate conversions, where the milling rate units were not converted correctly [TESCAN]

## 12/07/2023

- Added Documentation
- Added documentation for the detection and labelling widget
Expand All @@ -14,12 +46,12 @@
- Added a fibsem version number for development tracking
- Live chat (experimental)
- Autoliftout utils
- GIS Widget for cryo-control of gas injection
- GIS Widget for cryo-control of gas injection
- Embedded detection widget

- Fixed bugs
- fixed issue where parameters were passed incorrectly for milling
- fixed Eucentric movement where z-direction was flipped
- fixed Eucentric movement where z-direction was flipped

- Updated Functionality / Improved Processes
- system/model yaml files can now be modified from the system widget
Expand All @@ -28,26 +60,23 @@
- An option to click to move multiple milling stages together is now available
- Added a crosshair to the images
- movement of milling pattern now emits a pyqt signal (backend)
- Manufacturer / model /serial no info can now be accessed/saved
- Manipulator UI adaptive based on if manipulator is retracted or inserted
- Enabled granular hardware control for stage and manipulator (backend), eg: disable rotation only

- Manufacturer / model /serial no info can now be accessed/saved
- Manipulator UI adaptive based on if manipulator is retracted or inserted
- Enabled granular hardware control for stage and manipulator (backend), eg: disable rotation only

### 24/05/2023
## 24/05/2023

- Added new features
- FIB current alignment
- Manipulator Controls
- Measurement tools
- Segment Anything Labelling
- Added new milling patterns (Bitmap, Annulus)
- Separated stage pretilt
- Separated stage pretilt
- Fixed bugs
- Autolamella example
- Set microscope stage
- HFW
- HFW
- Milling widget
- Application file/Presets set on startup
- Import TESCAN image files


- Import TESCAN image files
13 changes: 13 additions & 0 deletions fibsem/acquire.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ def take_reference_images(
- The `FibsemImage` objects in the returned tuple contain the image data as numpy arrays,
as well as other image metadata.
"""
import time
from fibsem.microscope import TescanMicroscope
tmp_beam_type = image_settings.beam_type
image_settings.beam_type = BeamType.ELECTRON
eb_image = new_image(microscope, image_settings)
image_settings.beam_type = BeamType.ION
if isinstance(microscope, TescanMicroscope):
time.sleep(3)
ib_image = new_image(microscope, image_settings)
image_settings.beam_type = tmp_beam_type # reset to original beam type

Expand Down Expand Up @@ -88,6 +92,15 @@ def take_set_of_reference_images(

reference_images = ReferenceImages(low_eb, high_eb, low_ib, high_ib)


# more flexible version
# reference_images = []
# for i, hfw in enumerate(hfws):
# image_settings.hfw = hfw
# image_settings.label = f"{label}_res_{i:02d}"
# eb_image, ib_image = take_reference_images(microscope, image_settings)
# reference_images.append([eb_image, ib_image])

return reference_images


Expand Down
130 changes: 99 additions & 31 deletions fibsem/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
FibsemRectangle,
)
from fibsem.microscope import FibsemMicroscope
from typing import Union
from typing import Union, Optional


def auto_eucentric_correction(
Expand Down Expand Up @@ -51,7 +51,7 @@ def beam_shift_alignment(
microscope: FibsemMicroscope,
image_settings: ImageSettings,
ref_image: FibsemImage,
reduced_area: FibsemRectangle = None,
reduced_area: Optional[FibsemRectangle] = None,
):
"""Aligns the images by adjusting the beam shift instead of moving the stage.
Expand All @@ -71,6 +71,8 @@ def beam_shift_alignment(
ValueError: If `image_settings.beam_type` is not set to `BeamType.ION`.
"""
image_settings = ImageSettings.fromFibsemImage(ref_image)
image_settings.beam_type = BeamType.ION
image_settings.reduced_area = reduced_area
new_image = acquire.new_image(
microscope, settings=image_settings
Expand All @@ -89,10 +91,10 @@ def correct_stage_drift(
reference_images: ReferenceImages,
alignment: tuple[BeamType, BeamType] = (BeamType.ELECTRON, BeamType.ELECTRON),
rotate: bool = False,
use_ref_mask: bool = False,
mask_scale: int = 4,
ref_mask_rad: int = 512,
xcorr_limit: Union[tuple[int, int], None] = None,
constrain_vertical: bool = False,
use_beam_shift: bool = False,
) -> bool:
"""Corrects the stage drift by aligning low- and high-resolution reference images
using cross-correlation.
Expand All @@ -108,10 +110,7 @@ def correct_stage_drift(
BeamType.ELECTRON).
rotate (bool, optional): Whether to rotate the reference images before
alignment. Defaults to False.
use_ref_mask (bool, optional): Whether to apply a mask to the reference images
before alignment. Defaults to False.
mask_scale (int, optional): The scale factor used for creating the mask. Defaults
to 4.
ref_mask_rad (int, optional): The radius of the circular mask used for reference
xcorr_limit (tuple[int, int] | None, optional): A tuple of two integers that
represent the minimum and maximum cross-correlation values allowed for the
alignment. If not specified, the values are set to (None, None), which means
Expand Down Expand Up @@ -146,21 +145,10 @@ def correct_stage_drift(
# align lowres, then highres
for i, ref_image in enumerate([ref_lowres, ref_highres]):

if use_ref_mask:
ref_mask = masks.create_lamella_mask(
ref_image,
settings.protocol["lamella"],
scale=mask_scale,
use_trench_height=True,
) # TODO: refactor, liftout specific
else:
ref_mask = None
ref_mask = masks.create_circle_mask(ref_image.data.shape, ref_mask_rad)

# take new images
# set new image settings (same as reference)
# settings.image = utils.match_image_settings(
# ref_image, settings.image, beam_type=alignment[1]
# )
settings.image = ImageSettings.fromFibsemImage(ref_image)
settings.image.beam_type = alignment[1]
new_image = acquire.new_image(microscope, settings.image)
Expand All @@ -174,6 +162,7 @@ def correct_stage_drift(
ref_mask=ref_mask,
xcorr_limit=xcorr_limit[i],
constrain_vertical=constrain_vertical,
use_beam_shift=use_beam_shift,
)

if ret is False:
Expand All @@ -190,6 +179,7 @@ def align_using_reference_images(
ref_mask: np.ndarray = None,
xcorr_limit: int = None,
constrain_vertical: bool = False,
use_beam_shift: bool = False,
) -> bool:
"""
Uses cross-correlation to align a new image to a reference image.
Expand Down Expand Up @@ -245,13 +235,17 @@ def align_using_reference_images(
settings=settings, dy=-dy
) # FLAG_TEST
else:
# move the stage
microscope.stable_move(
settings=settings,
dx=dx,
dy=-dy,
beam_type=new_beam_type,
)
if use_beam_shift:
# move the beam shift
microscope.beam_shift(dx=-dx, dy=dy, beam_type=new_beam_type)
else:
# move the stage
microscope.stable_move(
settings=settings,
dx=dx,
dy=-dy,
beam_type=new_beam_type,
)

return shift_within_tolerance

Expand Down Expand Up @@ -326,17 +320,35 @@ def shift_from_crosscorrelation(
err = np.array(cen - [maxX, maxY], int)

# calculate shift in metres
x_shift = err[1] * pixelsize_x
y_shift = err[0] * pixelsize_y # this could be the issue?
dx = err[1] * pixelsize_x
dy = err[0] * pixelsize_y # this could be the issue?

logging.debug(f"cross-correlation:")
logging.debug(f"pixelsize: x: {pixelsize_x:.2e}, y: {pixelsize_y:.2e}")
logging.debug(f"maxX: {maxX}, {maxY}, centre: {cen}")
logging.debug(f"x: {err[1]}px, y: {err[0]}px")
logging.debug(f"x: {x_shift:.2e}m, y: {y_shift:.2e} meters")
logging.debug(f"x: {dx:.2e}m, y: {dy:.2e} meters")

# save data
_save_alignment_data(
ref_image=ref_image,
new_image=new_image,
bandpass=bandpass,
xcorr=xcorr,
use_rect_mask=use_rect_mask,
ref_mask=ref_mask,
xcorr_limit=xcorr_limit,
lowpass=lowpass,
highpass=highpass,
sigma=sigma,
dx=dx,
dy=dy,
pixelsize_x=pixelsize_x,
pixelsize_y=pixelsize_y,
)

# metres
return x_shift, y_shift, xcorr
return dx, dy, xcorr


def crosscorrelation_v2(
Expand Down Expand Up @@ -388,3 +400,59 @@ def crosscorrelation_v2(
xcorr = np.real(np.fft.fftshift(np.fft.ifft2(img1ft * np.conj(img2ft))))

return xcorr

def _save_alignment_data(
ref_image: FibsemImage,
new_image: FibsemImage,
bandpass: np.ndarray,
xcorr: np.ndarray,
ref_mask: np.ndarray = None,
lowpass: float = None,
highpass: float = None,
sigma: float = None,
xcorr_limit: float = None,
use_rect_mask: bool = False,
dx: float = None,
dy: float = None,
pixelsize_x: float = None,
pixelsize_y: float = None,


):
"""Save alignment data to disk."""

import pandas as pd
import os
from fibsem import config as cfg
import tifffile as tff

ts = utils.current_timestamp_v2()
fname = os.path.join(cfg.DATA_CC_PATH, str(ts))

# save fibsem images
ref_image.save(fname + "_ref.tif")
new_image.save(fname + "_new.tif")

# convert to tiff , save
tff.imwrite(fname + "_xcorr.tif", xcorr)
tff.imwrite(fname + "_bandpass.tif", bandpass)
if ref_mask is not None:
tff.imwrite(fname + "_ref_mask.tif", ref_mask)

info = {
# "ref_image": ref_image, "new_image": new_image, "bandpass": bandpass, "xcorr": xcorr, "ref_mask": ref_mask,
"lowpass": lowpass, "highpass": highpass, "sigma": sigma,
"pixelsize_x": pixelsize_x, "pixelsize_y": pixelsize_y,
"use_rect_mask": use_rect_mask, "xcorr_limit": xcorr_limit, "ref_mask": ref_mask is not None,
"dx": dx, "dy": dy, "fname": fname, "timestamp": ts }


df = pd.DataFrame.from_dict(info, orient='index').T

# save the dataframe to a csv file, append if the file already exists
DATAFRAME_PATH = os.path.join(cfg.DATA_CC_PATH, "data.csv")
if os.path.exists(DATAFRAME_PATH):
df_tmp = pd.read_csv(DATAFRAME_PATH)
df = pd.concat([df_tmp, df], axis=0, ignore_index=True)

df.to_csv(DATAFRAME_PATH, index=False)
Loading

0 comments on commit ba7ea68

Please sign in to comment.