Commit 8cfb0dbc authored by Carsten Richter's avatar Carsten Richter

Merge branch 'default-qspace-bin' into 'master'

Default qspace bins number

Closes #44

See merge request !73
parents 75fe19cf cbba53ac
Pipeline #5072 passed with stages
in 5 minutes and 17 seconds
......@@ -40,7 +40,7 @@ from ...io.XsocsH5 import XsocsH5
from ..widgets.AcqParamsWidget import AcqParamsWidget
from ..widgets.Containers import GroupBox, SubGroupBox
from ..widgets.Input import StyledLineEdit
from ...process.qspace.QSpaceConverter import QSpaceConverter
from ...process.qspace import QSpaceConverter, qspace_conversion
_ETA_LOWER = u'\u03B7'
......@@ -158,20 +158,20 @@ class ConversionParamsWidget(Qt.QWidget):
self.__medfiltCBox.toggled.connect(inputBase.setEnabled)
medfiltHEdit = StyledLineEdit(nChar=5)
medfiltHEdit.setValidator(Qt.QIntValidator(medfiltHEdit))
medfiltHEdit.setAlignment(Qt.Qt.AlignRight)
# medfiltHEdit.setText(str(medfiltDims[0]))
self.__medfiltHEdit = StyledLineEdit(nChar=5)
self.__medfiltHEdit.setValidator(Qt.QIntValidator(self.__medfiltHEdit))
self.__medfiltHEdit.setAlignment(Qt.Qt.AlignRight)
# self.__medfiltHEdit.setText(str(medfiltDims[0]))
medfiltVEdit = StyledLineEdit(nChar=5)
medfiltVEdit.setValidator(Qt.QIntValidator(medfiltVEdit))
medfiltVEdit.setAlignment(Qt.Qt.AlignRight)
# medfiltVEdit.setText(str(medfiltDims[1]))
self.__medfiltVEdit = StyledLineEdit(nChar=5)
self.__medfiltVEdit.setValidator(Qt.QIntValidator(self.__medfiltVEdit))
self.__medfiltVEdit.setAlignment(Qt.Qt.AlignRight)
# self.__medfiltVEdit.setText(str(medfiltDims[1]))
medfiltLayout.addWidget(Qt.QLabel('w='))
medfiltLayout.addWidget(medfiltHEdit)
medfiltLayout.addWidget(self.__medfiltHEdit)
medfiltLayout.addWidget(Qt.QLabel('h='))
medfiltLayout.addWidget(medfiltVEdit)
medfiltLayout.addWidget(self.__medfiltVEdit)
imgGboxLayout.addRow(self.__medfiltCBox, inputBase)
......@@ -184,34 +184,28 @@ class ConversionParamsWidget(Qt.QWidget):
qspaceGbox = SubGroupBox('QSpace grid dimensions.')
qspaceLayout = Qt.QHBoxLayout(qspaceGbox)
qDimsXEdit = StyledLineEdit(nChar=5)
qDimsYEdit = StyledLineEdit(nChar=5)
qDimsZEdit = StyledLineEdit(nChar=5)
self.__qDimsXEdit = StyledLineEdit(nChar=5)
self.__qDimsYEdit = StyledLineEdit(nChar=5)
self.__qDimsZEdit = StyledLineEdit(nChar=5)
dimLayout = Qt.QHBoxLayout()
qspaceLayout.addLayout(dimLayout)
dimLayout.addWidget(Qt.QLabel('qx:'))
dimLayout.addWidget(qDimsXEdit)
dimLayout.addWidget(self.__qDimsXEdit)
dimLayout.addStretch(1)
dimLayout = Qt.QHBoxLayout()
qspaceLayout.addLayout(dimLayout)
dimLayout.addWidget(Qt.QLabel('qy:'))
dimLayout.addWidget(qDimsYEdit)
dimLayout.addWidget(self.__qDimsYEdit)
dimLayout.addStretch(1)
dimLayout = Qt.QHBoxLayout()
qspaceLayout.addLayout(dimLayout)
dimLayout.addWidget(Qt.QLabel('qz:'))
dimLayout.addWidget(qDimsZEdit)
dimLayout.addWidget(self.__qDimsZEdit)
dimLayout.addStretch(1)
self.__medfiltHEdit = medfiltHEdit
self.__medfiltVEdit = medfiltVEdit
self.__qDimsXEdit = qDimsXEdit
self.__qDimsYEdit = qDimsYEdit
self.__qDimsZEdit = qDimsZEdit
layout.addWidget(qspaceGbox)
self.setMedfiltDims(medfiltDims)
......@@ -346,8 +340,8 @@ class ConversionParamsWidget(Qt.QWidget):
return [qsize_x, qsize_y, qsize_z]
def setQSpaceDims(self, qspaceDims):
"""
Sets the qspace dimensions.
"""Sets the qspace dimensions.
:param qspaceDims: A three integers array.
:return:
"""
......@@ -604,6 +598,44 @@ class QSpaceWidget(Qt.QDialog):
self.__fillScansInfos()
# Set default qspace bin size
if entries:
# Get angles from all entries
phi = []
eta = []
nu = []
delta = []
for entry in entries:
phi.append(xsocsH5.positioner(entry, 'phi'))
eta.append(xsocsH5.positioner(entry, 'eta'))
nu.append(xsocsH5.positioner(entry, 'nu'))
delta.append(xsocsH5.positioner(entry, 'del'))
entry = entries[0] # Get default config from first entry
# Compute Qspace conversion
q_array = qspace_conversion(
img_size=xsocsH5.image_size(entry),
center_chan=xsocsH5.direct_beam(entry),
chan_per_deg=xsocsH5.chan_per_deg(entry),
beam_energy=xsocsH5.beam_energy(entry),
phi=phi,
eta=eta,
nu=nu,
delta=delta)
# Estimate bin numbers based on smallest distance between q vectors
maxbins = []
for dim in (q_array[:, :, :, 0], q_array[:, :, :, 1], q_array[:, :, :, 2]):
maxbin = np.inf
for j in range(dim.ndim):
step = abs(np.diff(dim, axis=j)).max(j)
bins = np.nanmin(((abs(dim).max() - abs(dim).min()) / step))
maxbin = min(maxbin, bins)
maxbins.append(int(maxbin) + 1)
self.__paramsWid.setQSpaceDims(maxbins)
def __slotConvertBnClicked(self):
"""
Slot called when the convert button is clicked. Does some checks
......
......@@ -54,6 +54,74 @@ logger = logging.getLogger(__name__)
disp_times = False
def qspace_conversion(img_size, center_chan, chan_per_deg,
beam_energy, phi, eta, nu, delta):
"""Returns array of Q vectors corresponding to each pixel
This function is based on xrayutilities
:param List[int] img_size:
Size of the detector images in pixels (dim0, dim1)
:param List[float] center_chan:
Calibration center channel in image coordinates (dim0, dim1)
:param List[float] chan_per_deg:
Channel per degree calibration in image coordinates (dim0, dim1)
:param float beam_energy: Energy in eV
:param numpy.ndarray phi: 1D array of phi angle for each image
:param numpy.ndarray eta: 1D array of eta angle for each image
:param numpy.ndarray nu: 1D array of nu angle for each image
:param numpy.ndarray delta: 1D array of delta angle for each image
:return: Array Q vectors of shape:
(nb images, image height, image width, 3 (qx, qy, qz))
:rtype: numpy.ndarray
"""
phi = np.array(phi, copy=False, dtype=np.float64)
eta = np.array(eta, copy=False, dtype=np.float64)
nu = np.array(nu, copy=False, dtype=np.float64)
delta = np.array(delta, copy=False, dtype=np.float64)
qconv = xu.experiment.QConversion(['y-', 'z-'],
['z-', 'y-'],
[1, 0, 0])
# convention for coordinate system:
# x downstream
# z upwards
# y to the "outside"
# (righthanded)
hxrd = xu.HXRD([1, 0, 0],
[0, 0, 1],
en=beam_energy,
qconv=qconv)
hxrd.Ang2Q.init_area('z-',
'y+',
cch1=center_chan[0],
cch2=center_chan[1],
Nch1=img_size[0],
Nch2=img_size[1],
chpdeg1=chan_per_deg[0],
chpdeg2=chan_per_deg[1])
n_images = len(eta)
# shape of the array that will store the qx/qy/qz for all
# rocking angles
q_shape = n_images, img_size[0], img_size[1], 3
# then the array
q_ar = np.zeros(q_shape, dtype=np.float64)
for index in range(n_images):
qx, qy, qz = hxrd.Ang2Q.area(
eta[index], phi[index], nu[index], delta[index])
q_ar[index, :, :, 0] = qx
q_ar[index, :, :, 1] = qy
q_ar[index, :, :, 2] = qz
return q_ar
class QSpaceConverter(object):
(READY, RUNNING, DONE,
ERROR, CANCELED, UNKNOWN) = __STATUSES = range(6)
......@@ -679,39 +747,13 @@ class QSpaceConverter(object):
center_chan = (center_chan[0] - image_roi_offset[0],
center_chan[1] - image_roi_offset[1])
# TODO : make this editable?
nx, ny, nz = qspace_dims
qconv = xu.experiment.QConversion(['y-', 'z-'],
['z-', 'y-'],
[1, 0, 0])
# convention for coordinate system:
# x downstream
# z upwards
# y to the "outside"
# (righthanded)
hxrd = xu.HXRD([1, 0, 0],
[0, 0, 1],
en=beam_energy,
qconv=qconv)
hxrd.Ang2Q.init_area('z-',
'y+',
cch1=center_chan[0],
cch2=center_chan[1],
Nch1=img_size[0],
Nch2=img_size[1],
chpdeg1=chan_per_deg[0],
chpdeg2=chan_per_deg[1])
# shape of the array that will store the qx/qy/qz for all
# rocking angles
q_shape = n_entries, img_size[0] * img_size[1], 3
# then the array
q_ar = np.zeros(q_shape, dtype=np.float64)
img_dtype = None
phi = []
eta = []
nu = []
delta = []
with XsocsH5.XsocsH5(xsocsH5_f, mode='r') as master_h5:
......@@ -735,15 +777,10 @@ class QSpaceConverter(object):
entry_file))
entry_files.append(entry_file)
phi = np.float64(master_h5.positioner(entry, 'phi'))
eta = np.float64(master_h5.positioner(entry, 'eta'))
nu = np.float64(master_h5.positioner(entry, 'nu'))
delta = np.float64(master_h5.positioner(entry, 'del'))
qx, qy, qz = hxrd.Ang2Q.area(eta, phi, nu, delta)
q_ar[entry_idx, :, 0] = qx.reshape(-1)
q_ar[entry_idx, :, 1] = qy.reshape(-1)
q_ar[entry_idx, :, 2] = qz.reshape(-1)
phi.append(master_h5.positioner(entry, 'phi'))
eta.append(master_h5.positioner(entry, 'eta'))
nu.append(master_h5.positioner(entry, 'nu'))
delta.append(master_h5.positioner(entry, 'del'))
entry_dtype = master_h5.image_dtype(entry=entry)
......@@ -755,6 +792,18 @@ class QSpaceConverter(object):
'be of the same type. Found {0} and {1}.'
''.format(img_dtype, entry_dtype))
# Convert image pixel positions to Q space
q_ar = qspace_conversion(img_size,
center_chan,
chan_per_deg,
beam_energy,
phi,
eta,
nu,
delta)
# Reshape array to flatten image
q_ar.shape = q_ar.shape[0], q_ar.shape[1] * q_ar.shape[2], q_ar.shape[3]
if mask is not None:
# Mark masked pixels (i.e., non zero in the mask) with NaN
q_ar[:, mask.reshape(-1) != 0, :] = np.nan
......
......@@ -30,5 +30,5 @@ __license__ = "MIT"
__date__ = "01/03/2016"
from .QSpaceConverter import QSpaceConverter
from .QSpaceConverter import QSpaceConverter, qspace_conversion
from .helpers import kmap_2_qspace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment