Skip to content

Commit

Permalink
Merge pull request #240 from zhujun98/peak_finding_for_azimuthal_inte…
Browse files Browse the repository at this point in the history
…gration

Implement peak finding in Azimuthal Integration View.
  • Loading branch information
zhujun98 committed Jun 26, 2020
2 parents f453822 + 265b1bb commit 247f35f
Show file tree
Hide file tree
Showing 21 changed files with 430 additions and 90 deletions.
6 changes: 5 additions & 1 deletion extra_foam/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@

from .computer_vision import (
edge_detect, fourier_transform_2d
)
)

from .peak_finding import (
find_peaks_1d
)
14 changes: 14 additions & 0 deletions extra_foam/algorithms/peak_finding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Distributed under the terms of the BSD 3-Clause License.
The full license is in the file LICENSE, distributed with this software.
Author: Jun Zhu <jun.zhu@xfel.eu>
Copyright (C) European X-Ray Free-Electron Laser Facility GmbH.
All rights reserved.
"""
from scipy.signal import find_peaks


def find_peaks_1d(a, *args, **kwargs):
return find_peaks(a, *args, **kwargs)
120 changes: 86 additions & 34 deletions extra_foam/gui/ctrl_widgets/azimuthal_integ_ctrl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QDoubleValidator, QIntValidator
from PyQt5.QtWidgets import QComboBox, QGridLayout, QLabel
from PyQt5.QtWidgets import (
QCheckBox, QComboBox, QFrame, QGridLayout, QHBoxLayout, QLabel
)

from .base_ctrl_widgets import _AbstractCtrlWidget
from .smart_widgets import SmartBoundaryLineEdit, SmartLineEdit
from .smart_widgets import (
SmartBoundaryLineEdit, SmartSliceLineEdit, SmartLineEdit
)
from ..gui_helpers import invert_dict
from ...algorithms import compute_q
from ...config import config, list_azimuthal_integ_methods, Normalizer
from ...database import Metadata as mt

_DEFAULT_AZIMUTHAL_INTEG_POINTS = 512
_DEFAULT_PEAK_PROMINENCE = 100


def _estimate_q_range():
Expand Down Expand Up @@ -90,56 +95,85 @@ def __init__(self, *args, **kwargs):
self._auc_range_le = SmartBoundaryLineEdit("0, Inf")
self._fom_integ_range_le = SmartBoundaryLineEdit("0, Inf")

self._peak_finding_cb = QCheckBox("Peak finding")
self._peak_finding_cb.setChecked(True)
self._peak_prominence_le = SmartLineEdit(str(_DEFAULT_PEAK_PROMINENCE))
self._peak_prominence_le.setValidator(QIntValidator())
self._peak_slicer_le = SmartSliceLineEdit(":")

self._non_reconfigurable_widgets = [
]

self.initUI()
self.initConnections()

self.setFixedHeight(self.minimumSizeHint().height())

def initUI(self):
"""Override."""
layout = QGridLayout()
layout = QHBoxLayout()
AR = Qt.AlignRight

param_widget = QFrame()
param_layout = QGridLayout()
row = 0
layout.addWidget(QLabel("Cx (pixel): "), row, 0, AR)
layout.addWidget(self._cx_le, row, 1)
layout.addWidget(QLabel("Cy (pixel): "), row, 2, AR)
layout.addWidget(self._cy_le, row, 3)
layout.addWidget(QLabel("Pixel x (m): "), row, 4, AR)
layout.addWidget(self._px_le, row, 5)
layout.addWidget(QLabel("Pixel y (m): "), row, 6, AR)
layout.addWidget(self._py_le, row, 7)
param_layout.addWidget(QLabel("Cx (pixel): "), row, 0, AR)
param_layout.addWidget(self._cx_le, row, 1)
param_layout.addWidget(QLabel("Cy (pixel): "), row, 2, AR)
param_layout.addWidget(self._cy_le, row, 3)
param_layout.addWidget(QLabel("Pixel x (m): "), row, 4, AR)
param_layout.addWidget(self._px_le, row, 5)
param_layout.addWidget(QLabel("Pixel y (m): "), row, 6, AR)
param_layout.addWidget(self._py_le, row, 7)

row += 1
layout.addWidget(QLabel("Sample distance (m): "), row, 0, AR)
layout.addWidget(self._sample_dist_le, row, 1)
layout.addWidget(QLabel("Rotation x (rad): "), row, 2, AR)
layout.addWidget(self._rx_le, row, 3)
layout.addWidget(QLabel("Rotation y (rad): "), row, 4, AR)
layout.addWidget(self._ry_le, row, 5)
layout.addWidget(QLabel("Rotation z (rad): "), row, 6, AR)
layout.addWidget(self._rz_le, row, 7)
param_layout.addWidget(QLabel("Sample distance (m): "), row, 0, AR)
param_layout.addWidget(self._sample_dist_le, row, 1)
param_layout.addWidget(QLabel("Rotation x (rad): "), row, 2, AR)
param_layout.addWidget(self._rx_le, row, 3)
param_layout.addWidget(QLabel("Rotation y (rad): "), row, 4, AR)
param_layout.addWidget(self._ry_le, row, 5)
param_layout.addWidget(QLabel("Rotation z (rad): "), row, 6, AR)
param_layout.addWidget(self._rz_le, row, 7)

row += 1
layout.addWidget(QLabel("Photon energy (keV): "), row, 0, AR)
layout.addWidget(self._photon_energy_le, row, 1)
layout.addWidget(QLabel("Integ method: "), row, 2, AR)
layout.addWidget(self._integ_method_cb, row, 3)
layout.addWidget(QLabel("Integ points: "), row, 4, AR)
layout.addWidget(self._integ_pts_le, row, 5)
layout.addWidget(QLabel("Integ range (1/A): "), row, 6, AR)
layout.addWidget(self._integ_range_le, row, 7)
param_layout.addWidget(QLabel("Photon energy (keV): "), row, 0, AR)
param_layout.addWidget(self._photon_energy_le, row, 1)
param_layout.addWidget(QLabel("Integ method: "), row, 2, AR)
param_layout.addWidget(self._integ_method_cb, row, 3)
param_layout.addWidget(QLabel("Integ points: "), row, 4, AR)
param_layout.addWidget(self._integ_pts_le, row, 5)
param_layout.addWidget(QLabel("Integ range (1/A): "), row, 6, AR)
param_layout.addWidget(self._integ_range_le, row, 7)

row += 1
layout.addWidget(QLabel("Norm: "), row, 0, AR)
layout.addWidget(self._norm_cb, row, 1)
layout.addWidget(QLabel("AUC range (1/A): "), row, 2, AR)
layout.addWidget(self._auc_range_le, row, 3)
layout.addWidget(QLabel("FOM range (1/A): "), row, 4, AR)
layout.addWidget(self._fom_integ_range_le, row, 5)

param_layout.addWidget(QLabel("Norm: "), row, 0, AR)
param_layout.addWidget(self._norm_cb, row, 1)
param_layout.addWidget(QLabel("AUC range (1/A): "), row, 2, AR)
param_layout.addWidget(self._auc_range_le, row, 3)
param_layout.addWidget(QLabel("FOM range (1/A): "), row, 4, AR)
param_layout.addWidget(self._fom_integ_range_le, row, 5)

param_widget.setLayout(param_layout)

algo_widget = QFrame()
algo_layout = QGridLayout()
algo_layout.addWidget(self._peak_finding_cb, 0, 0, 1, 2)
algo_layout.addWidget(QLabel("Peak prominence: "), 1, 0, AR)
algo_layout.addWidget(self._peak_prominence_le, 1, 1)
algo_layout.addWidget(QLabel("Peak slicer: "), 2, 0, AR)
algo_layout.addWidget(self._peak_slicer_le, 2, 1)
algo_widget.setLayout(algo_layout)

layout.addWidget(param_widget)
layout.addWidget(algo_widget)
layout.setContentsMargins(1, 1, 1, 1)
self.setLayout(layout)

self.setFrameStyle(QFrame.NoFrame)
param_widget.setFrameStyle(QFrame.StyledPanel)
algo_widget.setFrameStyle(QFrame.StyledPanel)

def initConnections(self):
"""Override."""
mediator = self._mediator
Expand Down Expand Up @@ -178,6 +212,15 @@ def initConnections(self):
self._fom_integ_range_le.value_changed_sgn.connect(
mediator.onAiFomIntegRangeChange)

self._peak_finding_cb.toggled.connect(
mediator.onAiPeakFindingChange)

self._peak_prominence_le.value_changed_sgn.connect(
mediator.onAiPeakProminenceChange)

self._peak_slicer_le.value_changed_sgn.connect(
mediator.onAiPeakSlicerChange)

def updateMetaData(self):
"""Override."""
self._photon_energy_le.returnPressed.emit()
Expand All @@ -203,6 +246,10 @@ def updateMetaData(self):

self._fom_integ_range_le.returnPressed.emit()

self._peak_finding_cb.toggled.emit(self._peak_finding_cb.isChecked())
self._peak_prominence_le.returnPressed.emit()
self._peak_slicer_le.returnPressed.emit()

return True

def loadMetaData(self):
Expand All @@ -223,3 +270,8 @@ def loadMetaData(self):
self._available_norms_inv[int(cfg['normalizer'])])
self._auc_range_le.setText(cfg['auc_range'][1:-1])
self._fom_integ_range_le.setText(cfg['fom_integ_range'][1:-1])

self._updateWidgetValue(self._peak_finding_cb, cfg, "peak_finding")
self._updateWidgetValue(
self._peak_prominence_le, cfg, "peak_prominence")
self._updateWidgetValue(self._peak_slicer_le, cfg, "peak_slicer")
43 changes: 42 additions & 1 deletion extra_foam/gui/ctrl_widgets/base_ctrl_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
import abc

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QFrame, QGroupBox
from PyQt5.QtWidgets import (
QCheckBox, QComboBox, QFrame, QGroupBox, QLineEdit, QAbstractSpinBox
)

from .smart_widgets import SmartBoundaryLineEdit, SmartSliceLineEdit
from ..gui_helpers import parse_slice_inv
from ..mediator import Mediator
from ...database import MetaProxy
from ...logger import logger


class _AbstractCtrlWidgetMixin:
Expand Down Expand Up @@ -50,6 +55,42 @@ def onStart(self):
def onStop(self):
raise NotImplementedError

def _updateWidgetValue(self, widget, config, key, *, cast=None):
"""Update widget value from meta data."""
value = self._getMetaData(config, key)
if value is None:
return

if cast is not None:
value = cast(value)

if isinstance(widget, QCheckBox):
widget.setChecked(value == 'True')
elif isinstance(widget, SmartBoundaryLineEdit):
widget.setText(value[1:-1])
elif isinstance(widget, SmartSliceLineEdit):
widget.setText(parse_slice_inv(value))
elif isinstance(widget, QLineEdit):
widget.setText(value)
elif isinstance(widget, QAbstractSpinBox):
widget.setValue(value)
else:
logger.error(f"Unknown widget type: {type(widget)}")

@staticmethod
def _getMetaData(config, key):
"""Convienient function to get metadata and capture key error.
:param dict config: config dictionary.
:param str key: meta data key.
"""
try:
return config[key]
except KeyError:
# This happens when loading metadata in a new version with
# a config file in the old version.
logger.warning(f"Meta data key not found: {key}")


class _AbstractCtrlWidget(QFrame, _AbstractCtrlWidgetMixin):

Expand Down
8 changes: 3 additions & 5 deletions extra_foam/gui/ctrl_widgets/calibration_ctrl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from .base_ctrl_widgets import _AbstractCtrlWidget
from .smart_widgets import SmartSliceLineEdit
from ..gui_helpers import create_icon_button, parse_slice_inv
from ..gui_helpers import create_icon_button
from ...database import Metadata as mt


Expand Down Expand Up @@ -134,7 +134,5 @@ def loadMetaData(self):
self._dark_as_offset_cb.setChecked(cfg["dark_as_offset"] == 'True')

if self._pulse_resolved:
self._gain_cells_le.setText(
parse_slice_inv(cfg["gain_cells"]))
self._offset_cells_le.setText(
parse_slice_inv(cfg["offset_cells"]))
self._updateWidgetValue(self._gain_cells_le, cfg, "gain_cells")
self._updateWidgetValue(self._offset_cells_le, cfg, "offset_cells")
32 changes: 19 additions & 13 deletions extra_foam/gui/ctrl_widgets/geometry_ctrl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
)

from .base_ctrl_widgets import _AbstractCtrlWidget
from .smart_widgets import SmartLineEdit, SmartStringLineEdit
from .smart_widgets import SmartLineEdit
from ..gui_helpers import invert_dict
from ..items import GeometryItem
from ...config import config, GeomAssembler
from ...database import Metadata as mt
from ...geometries import module_indices
from ..items import GeometryItem
from ...logger import logger


def _parse_table_widget(widget):
Expand Down Expand Up @@ -213,16 +214,21 @@ def loadMetaData(self):

cfg = self._meta.hget_all(mt.GEOMETRY_PROC)

self._assembler_cb.setCurrentText(
self._assemblers_inv[int(cfg["assembler"])])
self._stack_only_cb.setChecked(cfg["stack_only"] == 'True')
self._geom_file_le.setText(cfg["geometry_file"])
assembler = self._getMetaData(cfg, "assembler")
if assembler is not None:
self._assembler_cb.setCurrentText(
self._assemblers_inv[int(cfg["assembler"])])

self._updateWidgetValue(self._stack_only_cb, cfg, "stack_only")
self._updateWidgetValue(self._geom_file_le, cfg, "geometry_file")

# TODO: check number of modules for JungFrau
coordinates = json.loads(cfg["coordinates"], encoding='utf8')
table = self._coordinates_tb
n_rows = table.rowCount()
n_cols = table.columnCount()
for j in range(n_cols):
for i in range(n_rows):
table.cellWidget(i, j).setText(str(coordinates[j][i]))
coordinates = self._getMetaData(cfg, "coordinates")
if coordinates is not None:
coordinates = json.loads(coordinates, encoding='utf8')
table = self._coordinates_tb
n_rows = table.rowCount()
n_cols = table.columnCount()
for j in range(n_cols):
for i in range(n_rows):
table.cellWidget(i, j).setText(str(coordinates[j][i]))
11 changes: 6 additions & 5 deletions extra_foam/gui/ctrl_widgets/image_transform_ctrl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,18 @@ def loadMetaData(self):
"""Override."""
cfg = self._meta.hget_all(mt.IMAGE_TRANSFORM_PROC)

self._ma_window_le.setText(str(cfg["ma_window"]))
self._updateWidgetValue(self._ma_window_le, cfg, "ma_window")

# do not load transform type since it is not an "input"

fft = self._fourier_transform
fft.logrithmic_cb.setChecked(cfg["fft:logrithmic"] == 'True')
self._updateWidgetValue(fft.logrithmic_cb, cfg, "fft:logrithmic")

ed = self._edge_detection
ed.kernel_size_sp.setValue(int(cfg["ed:kernel_size"]))
ed.sigma_sp.setValue(float(cfg["ed:sigma"]))
ed.threshold_le.setText(cfg['ed:threshold'][1:-1])
self._updateWidgetValue(
ed.kernel_size_sp, cfg, "ed:kernel_size", cast=int)
self._updateWidgetValue(ed.sigma_sp, cfg, "ed:sigma", cast=float)
self._updateWidgetValue(ed.threshold_le, cfg, "ed:threshold")

def registerTransformType(self):
self._mediator.onItTransformTypeChange(
Expand Down
10 changes: 5 additions & 5 deletions extra_foam/gui/ctrl_widgets/pump_probe_ctrl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from .base_ctrl_widgets import _AbstractGroupBoxCtrlWidget
from .smart_widgets import SmartSliceLineEdit
from ..gui_helpers import invert_dict, parse_slice_inv
from ..gui_helpers import invert_dict
from ...config import PumpProbeMode, AnalysisType
from ...database import Metadata as mt

Expand Down Expand Up @@ -140,10 +140,10 @@ def loadMetaData(self):
self._abs_difference_cb.setChecked(cfg["abs_difference"] == 'True')

if self._pulse_resolved:
self._on_pulse_le.setText(
parse_slice_inv(cfg["on_pulse_slicer"]))
self._off_pulse_le.setText(
parse_slice_inv(cfg["off_pulse_slicer"]))
self._updateWidgetValue(
self._on_pulse_le, cfg, "on_pulse_slicer")
self._updateWidgetValue(
self._off_pulse_le, cfg, "off_pulse_slicer")

def onPpModeChange(self, pp_mode):
if not self._pulse_resolved:
Expand Down

0 comments on commit 247f35f

Please sign in to comment.