Commit 21d88630 authored by payno's avatar payno Committed by Henri Payno
Browse files

[normalization] add processing from a dataset.

parent 1391c6a4
......@@ -45,6 +45,7 @@ from tomwer.core.process.baseprocess import SingleProcess, _input_desc, _output_
from processview.core.superviseprocess import SuperviseProcess
from tomwer.core.scan.scanbase import TomwerScanBase
from silx.io.url import DataUrl
from silx.io.utils import get_data
from processview.core.dataset import DatasetIdentifier
import tomoscan.esrf.utils
import tomwer.version
......@@ -92,6 +93,8 @@ class IntensityNormalizationProcess(SingleProcess, SuperviseProcess):
return self._compute_from_manual_roi(scan)
elif params.method is Method.AUTO_ROI:
return self._compute_from_automatic_roi(scan)
elif params.method is Method.DATASET:
return self._compute_from_dataset()
def _compute_from_manual_roi(self, scan):
params = IntensityNormalizationParams.from_dict(self.get_configuration())
......@@ -122,7 +125,7 @@ class IntensityNormalizationProcess(SingleProcess, SuperviseProcess):
# "computing ROI on a single projection")
try:
return self._global_compute_from_manual_roi(
return self._cache_compute_from_manual_roi(
dataset_identifier=scan.get_dataset_identifier(),
start_x=start_x,
start_y=start_y,
......@@ -137,15 +140,15 @@ class IntensityNormalizationProcess(SingleProcess, SuperviseProcess):
@staticmethod
@functools.lru_cache(
maxsize=6
) # maxsize=2 to at most keep info at volume and at frame level for 3 scans
def _global_compute_from_manual_roi(
) # maxsize=6 to at most keep info at volume and at frame level for 3 scans
def _cache_compute_from_manual_roi(
dataset_identifier: DatasetIdentifier,
start_x,
end_x,
start_y,
end_y,
calc_area: _CalculationArea,
):
) -> dict:
"""
compute mean and median on a volume or a slice.
To improve io performances compute both mean and median for both
......@@ -228,21 +231,80 @@ class IntensityNormalizationProcess(SingleProcess, SuperviseProcess):
)
else:
raise ValueError("{} is not handled".format(calc_area))
return IntensityNormalizationProcess.compute_stats(roi_area)
@staticmethod
def compute_stats(data):
results = {}
for calc_method in _ValueCalculationMethod:
results[calc_method.value] = {}
for calc_fct in _ValueCalculationFct:
if calc_method is _ValueCalculationMethod.SCALAR_VALUE:
res = getattr(numpy, calc_fct.value)(roi_area)
res = getattr(numpy, calc_fct.value)(data)
elif calc_method is _ValueCalculationMethod.ONE_PER_FRAME:
res = getattr(numpy, calc_fct.value)(roi_area, axis=(-2, -1))
if data.ndim == 3:
res = getattr(numpy, calc_fct.value)(data, axis=(-2, -1))
elif data.ndim == 2:
res = getattr(numpy, calc_fct.value)(data, axis=(-1))
elif data.ndim in (0, 1):
res = data
else:
raise ValueError(
"dataset dimension not handled ({})".format(data.ndim)
)
else:
raise ValueError("{} is not handled".format(calc_method.value))
results[calc_method.value][calc_fct.value] = res
return results
def _compute_from_automatic_roi(self, scan):
raise NotImplementedError("")
raise NotImplementedError("Not implemented yet")
def _compute_from_dataset(self):
params = IntensityNormalizationParams.from_dict(self.get_configuration())
extra_info = params.extra_infos
calc_fct = extra_info.get("calc_fct", None)
if calc_fct is None:
raise ValueError("calc_fct should be provided")
else:
calc_fct = _ValueCalculationFct.from_value(calc_fct)
calc_area = extra_info.get("calc_area", None)
if calc_area is None:
raise ValueError("calc_area should be provided")
else:
calc_area = _CalculationArea.from_value(calc_area)
calc_method = extra_info.get("calc_method", None)
if calc_method is None:
raise ValueError("calc_method should be provided")
else:
calc_method = _ValueCalculationMethod.from_value(calc_method)
url = extra_info.get("dataset_url", None)
if url is None:
raise ValueError("dataset_url should be provided")
elif isinstance(url, DataUrl):
url = url.path()
try:
return self._cache_compute_from_dataset(calc_area=calc_area, url=url,)[
calc_method.value
][calc_fct.value]
except Exception as e:
_logger.error(e)
return None
@staticmethod
@functools.lru_cache(
maxsize=6
) # maxsize=6 to at most keep info at volume and at frame level for 3 scans
def _cache_compute_from_dataset(calc_area: _CalculationArea, url: str) -> dict:
if calc_area is _CalculationArea.VOLUME:
url = DataUrl(url)
data = get_data(url)
return IntensityNormalizationProcess.compute_stats(data=data)
else:
raise ValueError("{} is not managed".format(calc_area))
@staticmethod
def program_name():
......
......@@ -412,6 +412,12 @@ class _NormIntensityOptions(qt.QWidget):
def _modeChanged(self, *args, **kwargs):
mode = self.getCurrentMethod()
self._intensityCalcOpts.setVisible(mode in (Method.MANUAL_ROI, Method.DATASET))
self._intensityCalcOpts._calculationAreaCB.setVisible(
mode in (Method.MANUAL_ROI,)
)
self._intensityCalcOpts._calculationAreaLabel.setVisible(
mode in (Method.MANUAL_ROI,)
)
self._datasetWidget.setVisible(mode == Method.DATASET)
self.setManualROIVisible(mode == Method.MANUAL_ROI)
self._optsMethod.setVisible(
......@@ -465,11 +471,11 @@ class _NormIntensityOptions(qt.QWidget):
def getExtraInfos(self):
method = self.getCurrentMethod()
if method == Method.MANUAL_SCALAR:
if method is Method.MANUAL_SCALAR:
return {"value": self._scalarValueWidget.getValue()}
elif method == Method.AUTO_ROI:
elif method is Method.AUTO_ROI:
raise NotImplementedError("auto roi not implemented yet")
elif method == Method.MANUAL_ROI:
elif method is Method.MANUAL_ROI:
roi = self._getROI()
return {
"start_x": roi.getOrigin()[0],
......@@ -480,6 +486,13 @@ class _NormIntensityOptions(qt.QWidget):
"calc_area": self._intensityCalcOpts.getCalculationArea().value,
"calc_method": self._intensityCalcOpts.getCalculationMethod().value,
}
elif method is Method.DATASET:
return {
"calc_fct": self._intensityCalcOpts.getCalculationFct().value,
"calc_area": self._intensityCalcOpts.getCalculationArea().value,
"calc_method": self._intensityCalcOpts.getCalculationMethod().value,
"dataset_url": self._datasetWidget.getDatasetUrl().path(),
}
else:
return {}
......@@ -502,7 +515,8 @@ class _NormIntensityCalcOpts(qt.QWidget):
self._calculationAreaCB = qt.QComboBox(self)
for area in _normParams._CalculationArea.values():
self._calculationAreaCB.addItem(area)
self.layout().addRow("calculation area", self._calculationAreaCB)
self._calculationAreaLabel = qt.QLabel("calculation area", self)
self.layout().addRow(self._calculationAreaLabel, self._calculationAreaCB)
# calculation 'axis'
self._calculationMethodCB = qt.QComboBox(self)
for opt in _normParams._ValueCalculationMethod.values():
......@@ -683,6 +697,30 @@ class _NormIntensityDatasetWidget(qt.QWidget):
def setGlobalFilePath(self, file_path):
self._filePathQLE.setText(file_path)
def getDatasetUrl(self) -> DataUrl:
if self.getMode() is _normParams._DatasetScope.LOCAL:
if self.getScan() is not None:
scan = self.getScan()
file_path = scan.master_file
else:
file_path = None
else:
file_path = self.getGlobalFilePath()
data_path = self.getDataPath()
if file_path is not None and file_path.lower().endswith("edf"):
scheme = "fabio"
else:
scheme = "silx"
return DataUrl(
file_path=file_path,
data_path=data_path,
scheme=scheme,
)
def setDatasetUrl(self, url: DataUrl):
raise NotImplementedError("")
def _updateFilePathVisibility(self):
self._filePathQLE.setReadOnly(self.getMode() == _normParams._DatasetScope.LOCAL)
self._filePathQLE.setEnabled(self.getMode() == _normParams._DatasetScope.GLOBAL)
......@@ -724,7 +762,7 @@ class _NormIntensityDatasetWidget(qt.QWidget):
mess = qt.QMessageBox(
parent=self,
icon=qt.QMessageBox.Warning,
text="local mode is only available for EDF acquisitions",
text="local mode is only available for HDF5 acquisitions",
)
mess.setModal(False)
mess.show()
......
Supports Markdown
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