Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ptycho+XRF Demo #101

Merged
merged 11 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ptychodus/api/fluorescence.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ class FluorescenceDataset:
# scan_indexes: IntegerArray


class FluorescenceEnhancingAlgorithm(ABC):
@abstractmethod
def enhance(self, dataset: FluorescenceDataset, product: Product) -> FluorescenceDataset:
pass


class FluorescenceFileReader(ABC):
@abstractmethod
def read(self, filePath: Path) -> FluorescenceDataset:
Expand Down
5 changes: 4 additions & 1 deletion ptychodus/controller/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,12 @@ def __init__(self, model: ModelCore, view: ViewCore) -> None:
self._refreshDataTimer.timeout.connect(model.refreshActiveDataset)
self._refreshDataTimer.start(1000) # TODO make configurable

view.navigationActionGroup.triggered.connect(lambda action: self.swapCentralWidgets(action))
view.workflowAction.setVisible(model.areWorkflowsSupported)

self.swapCentralWidgets(view.patternsAction)
view.patternsAction.setChecked(True)
view.navigationActionGroup.triggered.connect(lambda action: self.swapCentralWidgets(action))

def showMainWindow(self, windowTitle: str) -> None:
self.view.setWindowTitle(windowTitle)
self.view.show()
Expand Down
2 changes: 1 addition & 1 deletion ptychodus/controller/probe/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

from ...model.analysis import (
ExposureAnalyzer,
FluorescenceEnhancer,
ProbePropagator,
STXMSimulator,
)
from ...model.fluorescence import FluorescenceEnhancer
from ...model.product import ProbeAPI, ProbeRepository
from ...model.product.probe import ProbeRepositoryItem
from ...model.visualization import VisualizationEngine
Expand Down
140 changes: 107 additions & 33 deletions ptychodus/controller/probe/fluorescence.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
from typing import Any
from decimal import Decimal
from typing import Any, Final
import logging

from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex, QObject, QStringListModel
from PyQt5.QtWidgets import QWidget

from ptychodus.api.observer import Observable, Observer

from ...model.analysis import FluorescenceEnhancer
from ...model.fluorescence import (
FluorescenceEnhancer,
TwoStepFluorescenceEnhancingAlgorithm,
VSPIFluorescenceEnhancingAlgorithm,
)
from ...model.visualization import VisualizationEngine
from ...view.probe import FluorescenceDialog
from ...view.probe import (
FluorescenceDialog,
FluorescenceTwoStepParametersView,
FluorescenceVSPIParametersView,
)
from ...view.widgets import ExceptionDialog
from ..data import FileDialogFactory
from ..visualization import (
Expand All @@ -33,6 +43,71 @@ def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
return self._enhancer.getNumberOfChannels()


class FluorescenceTwoStepViewController(Observer):
def __init__(self, algorithm: TwoStepFluorescenceEnhancingAlgorithm) -> None:
super().__init__()
self._algorithm = algorithm
self._view = FluorescenceTwoStepParametersView()

self._upscalingModel = QStringListModel()
self._upscalingModel.setStringList(self._algorithm.getUpscalingStrategyList())
self._view.upscalingStrategyComboBox.setModel(self._upscalingModel)
self._view.upscalingStrategyComboBox.textActivated.connect(algorithm.setUpscalingStrategy)

self._deconvolutionModel = QStringListModel()
self._deconvolutionModel.setStringList(self._algorithm.getDeconvolutionStrategyList())
self._view.deconvolutionStrategyComboBox.setModel(self._deconvolutionModel)
self._view.deconvolutionStrategyComboBox.textActivated.connect(
algorithm.setDeconvolutionStrategy
)

self._syncModelToView()
algorithm.addObserver(self)

def getWidget(self) -> QWidget:
return self._view

def _syncModelToView(self) -> None:
self._view.upscalingStrategyComboBox.setCurrentText(self._algorithm.getUpscalingStrategy())
self._view.deconvolutionStrategyComboBox.setCurrentText(
self._algorithm.getDeconvolutionStrategy()
)

def update(self, observable: Observable) -> None:
if observable is self._algorithm:
self._syncModelToView()


class FluorescenceVSPIViewController(Observer):
MAX_INT: Final[int] = 0x7FFFFFFF

def __init__(self, algorithm: VSPIFluorescenceEnhancingAlgorithm) -> None:
super().__init__()
self._algorithm = algorithm
self._view = FluorescenceVSPIParametersView()

self._view.dampingFactorLineEdit.valueChanged.connect(self._syncDampingFactorToModel)
self._view.maxIterationsSpinBox.setRange(1, self.MAX_INT)
self._view.maxIterationsSpinBox.valueChanged.connect(algorithm.setMaxIterations)

algorithm.addObserver(self)
self._syncModelToView()

def getWidget(self) -> QWidget:
return self._view

def _syncDampingFactorToModel(self, value: Decimal) -> None:
self._algorithm.setDampingFactor(float(value))

def _syncModelToView(self) -> None:
self._view.dampingFactorLineEdit.setValue(Decimal(repr(self._algorithm.getDampingFactor())))
self._view.maxIterationsSpinBox.setValue(self._algorithm.getMaxIterations())

def update(self, observable: Observable) -> None:
if observable is self._algorithm:
self._syncModelToView()


class FluorescenceViewController(Observer):
def __init__(
self,
Expand All @@ -46,41 +121,42 @@ def __init__(
self._fileDialogFactory = fileDialogFactory
self._dialog = FluorescenceDialog()
self._enhancementModel = QStringListModel()
self._enhancementModel.setStringList(self._enhancer.getEnhancementStrategyList())
self._upscalingModel = QStringListModel()
self._upscalingModel.setStringList(self._enhancer.getUpscalingStrategyList())
self._deconvolutionModel = QStringListModel()
self._deconvolutionModel.setStringList(self._enhancer.getDeconvolutionStrategyList())
self._enhancementModel.setStringList(self._enhancer.getAlgorithmList())
self._channelListModel = FluorescenceChannelListModel(enhancer)

self._dialog.fluorescenceParametersView.openButton.clicked.connect(
self._openMeasuredDataset
)

self._dialog.fluorescenceParametersView.enhancementStrategyComboBox.setModel(
self._enhancementModel
twoStepViewController = FluorescenceTwoStepViewController(
enhancer.twoStepEnhancingAlgorithm
)
self._dialog.fluorescenceParametersView.algorithmComboBox.addItem(
TwoStepFluorescenceEnhancingAlgorithm.DISPLAY_NAME,
self._dialog.fluorescenceParametersView.algorithmComboBox.count(),
)
self._dialog.fluorescenceParametersView.enhancementStrategyComboBox.textActivated.connect(
enhancer.setEnhancementStrategy
self._dialog.fluorescenceParametersView.stackedWidget.addWidget(
twoStepViewController.getWidget()
)

self._dialog.fluorescenceParametersView.upscalingStrategyComboBox.setModel(
self._upscalingModel
vspiViewController = FluorescenceVSPIViewController(enhancer.vspiEnhancingAlgorithm)
self._dialog.fluorescenceParametersView.algorithmComboBox.addItem(
VSPIFluorescenceEnhancingAlgorithm.DISPLAY_NAME,
self._dialog.fluorescenceParametersView.algorithmComboBox.count(),
)
self._dialog.fluorescenceParametersView.upscalingStrategyComboBox.textActivated.connect(
enhancer.setUpscalingStrategy
self._dialog.fluorescenceParametersView.stackedWidget.addWidget(
vspiViewController.getWidget()
)

self._dialog.fluorescenceParametersView.deconvolutionStrategyComboBox.setModel(
self._deconvolutionModel
self._dialog.fluorescenceParametersView.algorithmComboBox.textActivated.connect(
enhancer.setAlgorithm
)
self._dialog.fluorescenceParametersView.deconvolutionStrategyComboBox.textActivated.connect(
enhancer.setDeconvolutionStrategy
self._dialog.fluorescenceParametersView.algorithmComboBox.currentIndexChanged.connect(
self._dialog.fluorescenceParametersView.stackedWidget.setCurrentIndex
)

self._dialog.fluorescenceChannelListView.setModel(self._channelListModel)
self._dialog.fluorescenceChannelListView.selectionModel().currentChanged.connect(
self._updateView
self._dialog.fluorescenceParametersView.algorithmComboBox.setModel(self._enhancementModel)
self._dialog.fluorescenceParametersView.algorithmComboBox.textActivated.connect(
enhancer.setAlgorithm
)

self._dialog.fluorescenceParametersView.enhanceButton.clicked.connect(
Expand All @@ -90,6 +166,11 @@ def __init__(
self._saveEnhancedDataset
)

self._dialog.fluorescenceChannelListView.setModel(self._channelListModel)
self._dialog.fluorescenceChannelListView.selectionModel().currentChanged.connect(
self._updateView
)

self._measuredWidgetController = VisualizationWidgetController(
engine,
self._dialog.measuredWidget,
Expand Down Expand Up @@ -160,16 +241,9 @@ def _saveEnhancedDataset(self) -> None:
ExceptionDialog.showException(title, err)

def _syncModelToView(self) -> None:
self._dialog.fluorescenceParametersView.enhancementStrategyComboBox.setCurrentText(
self._enhancer.getEnhancementStrategy()
)
self._dialog.fluorescenceParametersView.upscalingStrategyComboBox.setCurrentText(
self._enhancer.getUpscalingStrategy()
self._dialog.fluorescenceParametersView.algorithmComboBox.setCurrentText(
self._enhancer.getAlgorithm()
)
self._dialog.fluorescenceParametersView.deconvolutionStrategyComboBox.setCurrentText(
self._enhancer.getDeconvolutionStrategy()
)

self._channelListModel.beginResetModel()
self._channelListModel.endResetModel()

Expand Down
2 changes: 0 additions & 2 deletions ptychodus/model/analysis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from .core import AnalysisCore
from .exposure import ExposureAnalyzer, ExposureMap
from .fluorescence import FluorescenceEnhancer
from .frc import FourierRingCorrelator
from .objectInterpolator import ObjectLinearInterpolator
from .objectStitcher import ObjectStitcher
Expand All @@ -12,7 +11,6 @@
'AnalysisCore',
'ExposureAnalyzer',
'ExposureMap',
'FluorescenceEnhancer',
'FourierRingCorrelator',
'ObjectLinearInterpolator',
'ObjectStitcher',
Expand Down
42 changes: 1 addition & 41 deletions ptychodus/model/analysis/core.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
from pathlib import Path
import logging

from ptychodus.api.fluorescence import (
DeconvolutionStrategy,
FluorescenceFileReader,
FluorescenceFileWriter,
UpscalingStrategy,
)
from ptychodus.api.plugins import PluginChooser
from ptychodus.api.settings import SettingsRegistry

from ..product import ObjectRepository, ProductRepository
from ..reconstructor import DiffractionPatternPositionMatcher
from ..visualization import VisualizationEngine
from .exposure import ExposureAnalyzer
from .fluorescence import FluorescenceEnhancer
from .frc import FourierRingCorrelator
from .propagator import ProbePropagator
from .settings import FluorescenceSettings, ProbePropagationSettings
from .settings import ProbePropagationSettings
from .stxm import STXMSimulator
from .xmcd import XMCDAnalyzer

Expand All @@ -31,10 +22,6 @@ def __init__(
dataMatcher: DiffractionPatternPositionMatcher,
productRepository: ProductRepository,
objectRepository: ObjectRepository,
upscalingStrategyChooser: PluginChooser[UpscalingStrategy],
deconvolutionStrategyChooser: PluginChooser[DeconvolutionStrategy],
fluorescenceFileReaderChooser: PluginChooser[FluorescenceFileReader],
fluorescenceFileWriterChooser: PluginChooser[FluorescenceFileWriter],
) -> None:
self.stxmSimulator = STXMSimulator(dataMatcher)
self.stxmVisualizationEngine = VisualizationEngine(isComplex=False)
Expand All @@ -46,32 +33,5 @@ def __init__(
self.exposureVisualizationEngine = VisualizationEngine(isComplex=False)
self.fourierRingCorrelator = FourierRingCorrelator(objectRepository)

self._fluorescenceSettings = FluorescenceSettings(settingsRegistry)
self.fluorescenceEnhancer = FluorescenceEnhancer(
self._fluorescenceSettings,
productRepository,
upscalingStrategyChooser,
deconvolutionStrategyChooser,
fluorescenceFileReaderChooser,
fluorescenceFileWriterChooser,
settingsRegistry,
)
self.fluorescenceVisualizationEngine = VisualizationEngine(isComplex=False)
self.xmcdAnalyzer = XMCDAnalyzer(objectRepository)
self.xmcdVisualizationEngine = VisualizationEngine(isComplex=False)

def enhanceFluorescence(
self, productIndex: int, inputFilePath: Path, outputFilePath: Path
) -> int:
fileType = 'XRF-Maps'

try:
self.fluorescenceEnhancer.setProduct(productIndex)
self.fluorescenceEnhancer.openMeasuredDataset(inputFilePath, fileType)
self.fluorescenceEnhancer.enhanceFluorescence()
self.fluorescenceEnhancer.saveEnhancedDataset(outputFilePath, fileType)
except Exception as exc:
logger.exception(exc)
return -1

return 0
Loading