Skip to content

Commit

Permalink
Merge pull request #3 from peng-lab/dev
Browse files Browse the repository at this point in the history
Fixed pypi secret
  • Loading branch information
lennartkowitz authored Dec 6, 2024
2 parents dc30003 + 6a03986 commit a702cf2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
git tag
python -m build .
Expand Down
35 changes: 0 additions & 35 deletions src/destripe_lsfm/_tests/test_reader.py
Original file line number Diff line number Diff line change
@@ -1,35 +0,0 @@
import numpy as np
from aicsimageio.writers import OmeTiffWriter

from destripe_lsfm import napari_get_reader


# tmp_path is a pytest fixture
def test_reader(tmp_path):
"""An example of how you might test your plugin."""

# write some fake data using your supported file format
my_test_file = str(tmp_path / "myfile.tiff")
original_data = np.random.randint(0, 2**16, (20, 20), dtype=np.uint16)
data = original_data.astype(np.uint16)
OmeTiffWriter.save(data, my_test_file, dim_order_out="YX")

# try to read it back in
reader = napari_get_reader(my_test_file)
assert callable(reader)

# make sure we're delivering the right format
result = reader(my_test_file)
assert isinstance(result, tuple) and len(result) > 0
image, filename = result
assert isinstance(image, np.ndarray)
assert filename == "myfile.tiff"

# make sure it's the same as it started
assert image.shape == original_data.shape
assert image.dtype == original_data.dtype


def test_get_reader_pass():
reader = napari_get_reader("fake.file")
assert reader is None
174 changes: 141 additions & 33 deletions src/destripe_lsfm/_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import numpy as np
import torch
import warnings

from qtpy.QtWidgets import (
QVBoxLayout,
Expand Down Expand Up @@ -33,18 +34,19 @@
class DestripeWidget(QWidget):
"""Main widget of the plugin"""

print(callable(DeStripe)) # sanity check for vscode

def __init__(self, viewer: "napari.viewer.Viewer"):
super().__init__()
self.viewer = viewer

self.advanced_options_elements = []

# This is how to set position and size of the viewer window:
# self.viewer.window.set_geometry(0, 0, max(1000, width), max(600, height))

_, _, width, height = self.viewer.window.geometry()
width = self.viewer.window.geometry()[2]
height = self.viewer.window.geometry()[3]
# width = self.viewer.window.geometry()[2]
# height = self.viewer.window.geometry()[3]
self.viewer.window.resize(max(1000, width),max(600, height))
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG)
Expand All @@ -61,28 +63,61 @@ def __init__(self, viewer: "napari.viewer.Viewer"):
label_angle = QLabel("Angle offset:")
label_angle.setToolTip("Angle offset in degrees")

label_resample = QLabel("Resample ratio:")
label_kernel_size = QLabel("GF Kernel size inference:")
label_kernel_size.setToolTip("Must be odd")
label_lambda_hessian = QLabel("Lambda Hessian:")
label_hessian_kernel_sigma = QLabel("Hessian Kernel Sigma:")
label_isotropic_hessian = QLabel("Isotropic Hessian:")

self.advanced_options_elements.extend([
label_resample,
label_kernel_size,
label_lambda_hessian,
label_hessian_kernel_sigma,
label_isotropic_hessian,
])

# QPushbutton
btn_load = QPushButton("Load")
btn_process = QPushButton("Process")
btn_save = QPushButton("Save")
self.btn_advanced_options = QPushButton("Hide Advanced Options")

btn_load.clicked.connect(self.load)
btn_process.clicked.connect(self.process)
btn_save.clicked.connect(self.save)
self.btn_advanced_options.clicked.connect(self.toggle_advanced_options)

# QCombobox
self.combobox_mask = QComboBox()
# self.combobox_mask.addItems(
# ["Layernames", "will", "show", "up", "here"]
# )

# QCheckBox
self.checkbox_vertical = QCheckBox()
self.checkbox_vertical.setChecked(True)
self.checkbox_iso_hessian = QCheckBox()
self.checkbox_iso_hessian.setChecked(True)

self.advanced_options_elements.append(self.checkbox_iso_hessian)

# QLineEdit
self.lineedit_angle = QLineEdit()
self.lineedit_angle.setText("0")
self.lineedit_resample = QLineEdit()
self.lineedit_resample.setText("3")
self.lineedit_kernel_size = QLineEdit()
self.lineedit_kernel_size.setText("29")
self.lineedit_lambda_hessian = QLineEdit()
self.lineedit_lambda_hessian.setText("1")
self.lineedit_hessian_kernel_sigma = QLineEdit()
self.lineedit_hessian_kernel_sigma.setText("1")

self.advanced_options_elements.extend([
self.lineedit_resample,
self.lineedit_kernel_size,
self.lineedit_lambda_hessian,
self.lineedit_hessian_kernel_sigma,
])

# QGroupBox
parameters = QGroupBox("Parameters")
Expand All @@ -93,6 +128,17 @@ def __init__(self, viewer: "napari.viewer.Viewer"):
gb_layout.addWidget(self.lineedit_angle, 1, 1)
gb_layout.addWidget(label_mask, 2, 0)
gb_layout.addWidget(self.combobox_mask, 2, 1)
gb_layout.addWidget(self.btn_advanced_options, 3, 0, 1, -1)
gb_layout.addWidget(label_resample, 4, 0)
gb_layout.addWidget(self.lineedit_resample, 4, 1)
gb_layout.addWidget(label_kernel_size, 5, 0)
gb_layout.addWidget(self.lineedit_kernel_size, 5, 1)
gb_layout.addWidget(label_lambda_hessian, 6, 0)
gb_layout.addWidget(self.lineedit_lambda_hessian, 6, 1)
gb_layout.addWidget(label_hessian_kernel_sigma, 7, 0)
gb_layout.addWidget(self.lineedit_hessian_kernel_sigma, 7, 1)
gb_layout.addWidget(label_isotropic_hessian, 8, 0)
gb_layout.addWidget(self.checkbox_iso_hessian, 8, 1)
parameters.setLayout(gb_layout)

layout = QGridLayout()
Expand All @@ -112,7 +158,17 @@ def __init__(self, viewer: "napari.viewer.Viewer"):
self.setLayout(QVBoxLayout())
self.layout().addWidget(scroll_area)
self.setMinimumWidth(300)
QApplication.instance().aboutToQuit.connect(lambda: self.logger.debug("Exiting..."))

self.toggle_advanced_options()

def wrapper(self, func, event):
self.logger.debug("Exiting...")
return func(event)

with warnings.catch_warnings():
warnings.simplefilter("ignore")
func = self.viewer.window._qt_window.closeEvent
self.viewer.window._qt_window.closeEvent = lambda event: wrapper(self, func, event)

self.viewer.layers.events.inserted.connect(self.update_combobox)
self.viewer.layers.events.inserted.connect(self.connect_rename)
Expand Down Expand Up @@ -189,43 +245,95 @@ def save(self):
self.logger.info("Data saved")

def process(self):
params = self.get_parameters()
if params is None:
return
output_image = DeStripe.train_on_full_arr(
X = params["input_image"],
is_vertical = params["is_vertical"],
angle_offset = params["angle_offset"],
mask = params["mask"],
device = params["device"],
)
self.viewer.add_image(output_image, name="Destriped Image")

def get_parameters(self):
mask_layer_name = self.combobox_mask.currentText()
self.logger.debug("Selected mask: %s", mask_layer_name)
if mask_layer_name not in self.viewer.layers:
self.logger.info("Selected mask not found")
return
is_vertical = self.checkbox_vertical.isChecked()
self.logger.debug("Vertical: %s", is_vertical)
try:
angle_offset = list(map(float, self.lineedit_angle.text().split(",")))
except ValueError:
self.logger.error("Invalid angle offset")
return
self.logger.debug("Angle offset: %s", angle_offset)
params = {}
for layer in self.viewer.layers:
if isinstance(layer, napari.layers.Image):
input_image = layer.data
params["input_image"] = layer.data
self.logger.debug(f"Using layer: {layer.name}")
break
if input_image is None:
if params["input_image"] is None:
self.logger.info("No image layer found")
return
input_image = np.expand_dims(input_image, axis=1)
params["input_image"] = np.expand_dims(params["input_image"], axis=1)
self.logger.debug(f"Selected mask: {mask_layer_name}")
params["is_vertical"] = self.checkbox_vertical.isChecked()
self.logger.debug(f"Vertical: {params['is_vertical']}")
try:
params["angle_offset"] = list(map(float, self.lineedit_angle.text().split(",")))
except ValueError:
self.logger.error("Invalid angle offset")
return
self.logger.debug(f"Angle offset: {params['angle_offset']}")
mask_layer_index = self.viewer.layers.index(mask_layer_name)
mask_arr = self.viewer.layers[mask_layer_index].data
if torch.cuda.is_available():
self.logger.debug("CUDA is available")
device = "cuda"
params["mask"] = self.viewer.layers[mask_layer_index].data
params["device"] = "cuda" if torch.cuda.is_available() else "cpu"
self.logger.debug(f"Device: {params['device']}")
try:
params["resample_ratio"] = int(self.lineedit_resample.text())
except ValueError:
self.logger.error("Invalid resample ratio")
return
if not params["resample_ratio"] in range(1, 6):
self.logger.error("Resample ratio must be between 1 and 5")
return
self.logger.debug(f"Resample ratio: {params['resample_ratio']}")
try:
params["gf_kernel_size_inference"] = int(self.lineedit_kernel_size.text())
except ValueError:
self.logger.error("Invalid GF kernel size inference")
return
if params["gf_kernel_size_inference"] not in range(29, 90, 2):
self.logger.error("GF kernel size inference must be odd and between 29 and 89")
return
self.logger.debug(f"GF kernel size inference: {params['gf_kernel_size_inference']}")
try:
params["lambda_hessian"] = float(self.lineedit_lambda_hessian.text())
except ValueError:
self.logger.error("Invalid lambda Hessian")
return
if not (0 <= params["lambda_hessian"] and params["lambda_hessian"] <= 10):
self.logger.error("Lambda Hessian must be between 0 and 10")
return
self.logger.debug(f"Lambda Hessian: {params['lambda_hessian']}")
try:
params["hessian_kernel_sigma"] = float(self.lineedit_hessian_kernel_sigma.text())
except ValueError:
self.logger.error("Invalid Hessian kernel sigma")
return
if not (0.5 <= params["hessian_kernel_sigma"] and params["hessian_kernel_sigma"] <= 1.5):
self.logger.error("Hessian kernel sigma must be between 0.5 and 1.5")
return
self.logger.debug(f"Hessian kernel sigma: {params['hessian_kernel_sigma']}")
params["isotropic_hessian"] = self.checkbox_iso_hessian.isChecked()
self.logger.debug(f"Isotropic Hessian: {params['isotropic_hessian']}")
return params

def toggle_advanced_options(self):
if self.btn_advanced_options.text() == "Show Advanced Options":
self.btn_advanced_options.setText("Hide Advanced Options")
show = True
else:
self.logger.debug("CUDA is not available")
device = "cpu"
output_image = DeStripe.train_on_full_arr(
X = input_image,
is_vertical = is_vertical,
angle_offset = angle_offset,
mask = mask_arr,
device = device,
)
self.viewer.add_image(output_image, name="Destriped Image")
self.btn_advanced_options.setText("Show Advanced Options")
show = False
for element in self.advanced_options_elements:
element.setVisible(show)

class LayerSelection(QDialog):
def __init__(self, layernames: list[str]):
Expand Down

0 comments on commit a702cf2

Please sign in to comment.