Skip to content

Commit

Permalink
Implement peak finding in Azimuthal Integration View.
Browse files Browse the repository at this point in the history
- Catch exception in uploadMetadata.
  • Loading branch information
zhujun98 committed Jun 25, 2020
1 parent f453822 commit 3ea83da
Show file tree
Hide file tree
Showing 13 changed files with 202 additions and 56 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)
97 changes: 64 additions & 33 deletions extra_foam/gui/ctrl_widgets/azimuthal_integ_ctrl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

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
Expand Down Expand Up @@ -90,56 +92,78 @@ 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._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)
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 +202,9 @@ def initConnections(self):
self._fom_integ_range_le.value_changed_sgn.connect(
mediator.onAiFomIntegRangeChange)

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

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

self._fom_integ_range_le.returnPressed.emit()

self._peak_finding_cb.toggled.emit(self._peak_finding_cb.isChecked())

return True

def loadMetaData(self):
Expand All @@ -223,3 +252,5 @@ 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")
39 changes: 38 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,14 @@
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
from ..mediator import Mediator
from ...database import MetaProxy
from ...logger import logger


class _AbstractCtrlWidgetMixin:
Expand Down Expand Up @@ -87,6 +91,39 @@ def onStop(self):
for widget in self._non_reconfigurable_widgets:
widget.setEnabled(True)

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, QLineEdit):
widget.setText(value)
elif isinstance(widget, QAbstractSpinBox):
widget.setValue(value)
else:
logger.error(f"Unknown widget type: {type(widget)}")

def _getMetaData(self, 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 _AbstractGroupBoxCtrlWidget(QGroupBox, _AbstractCtrlWidgetMixin):
GROUP_BOX_STYLE_SHEET = 'QGroupBox:title {'\
Expand Down
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
13 changes: 12 additions & 1 deletion extra_foam/gui/image_tool/azimuthal_integ_1d_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ..ctrl_widgets import AzimuthalIntegCtrlWidget
from ..misc_widgets import FColor
from ..plot_widgets import ImageViewF, PlotWidgetF
from ...algorithms import find_peaks_1d
from ...config import AnalysisType, plot_labels


Expand All @@ -31,16 +32,25 @@ def __init__(self, *, parent=None):
self.setTitle('Azimuthal integration')

self._plot = self.plotCurve(pen=FColor.mkPen("p"))
self._peaks = self.plotScatter(
pen=FColor.mkPen("r"), symbol="+", size=12)

def updateF(self, data):
"""Override."""
momentum, intensity = data.ai.x, data.ai.y
ai = data.ai
momentum, intensity = ai.x, ai.y

if intensity is None:
return

self._plot.setData(momentum, intensity)

peaks = ai.peaks
if peaks is None:
self._peaks.setData([], [])
else:
self._peaks.setData(momentum[peaks], intensity[peaks])


@create_imagetool_view(AzimuthalIntegCtrlWidget)
class AzimuthalInteg1dView(_AbstractImageToolView):
Expand All @@ -63,6 +73,7 @@ def __init__(self, *args, **kwargs):
self._azimuthal_integ_1d_curve = AzimuthalInteg1dPlot()

self.initUI()
self.initConnections()

def initUI(self):
"""Override."""
Expand Down
5 changes: 5 additions & 0 deletions extra_foam/gui/image_tool/tests/test_image_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ def testAzimuthalInteg1dCtrlWidget(self):
self.assertEqual(default_pixel_size, proc._pixel2)
self.assertEqual(0, proc._poni1)
self.assertEqual(0, proc._poni2)
self.assertTrue(proc._find_peaks)

# test setting new values
widget._photon_energy_le.setText("12.4")
Expand All @@ -600,6 +601,7 @@ def testAzimuthalInteg1dCtrlWidget(self):
widget._py_le.setText("0.000002")
widget._cx_le.setText("-1000")
widget._cy_le.setText("1000")
widget._peak_finding_cb.setChecked(False)
proc.update()
self.assertAlmostEqual(1e-10, proc._wavelength)
self.assertAlmostEqual(0.3, proc._sample_dist)
Expand All @@ -613,6 +615,7 @@ def testAzimuthalInteg1dCtrlWidget(self):
self.assertEqual(0.000002, proc._pixel1)
self.assertEqual(-1000 * 0.000001, proc._poni2)
self.assertEqual(1000 * 0.000002, proc._poni1)
self.assertFalse(proc._find_peaks)

# test loading meta data
mediator = widget._mediator
Expand All @@ -628,6 +631,7 @@ def testAzimuthalInteg1dCtrlWidget(self):
mediator.onAiPixelSizeYChange(0.002)
mediator.onAiIntegCenterXChange(1)
mediator.onAiIntegCenterYChange(2)
mediator.onAiPeakFindingChange(True)
widget.loadMetaData()
self.assertEqual("2.0", widget._photon_energy_le.text())
self.assertEqual("0.2", widget._sample_dist_le.text())
Expand All @@ -641,6 +645,7 @@ def testAzimuthalInteg1dCtrlWidget(self):
self.assertEqual("0.002", widget._py_le.text())
self.assertEqual("1", widget._cx_le.text())
self.assertEqual("2", widget._cy_le.text())
self.assertTrue(widget._peak_finding_cb.isChecked())

def testRoiFomCtrlWidget(self):
widget = self.image_tool._corrected_view._roi_fom_ctrl_widget
Expand Down
3 changes: 3 additions & 0 deletions extra_foam/gui/mediator.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ def onAiAucRangeChange(self, value: tuple):
def onAiFomIntegRangeChange(self, value: tuple):
self._meta.hset(mt.AZIMUTHAL_INTEG_PROC, 'fom_integ_range', str(value))

def onAiPeakFindingChange(self, value: bool):
self._meta.hset(mt.AZIMUTHAL_INTEG_PROC, "peak_finding", str(value))

def onPpModeChange(self, value: IntEnum):
self._meta.hset(mt.PUMP_PROBE_PROC, 'mode', int(value))

Expand Down

0 comments on commit 3ea83da

Please sign in to comment.