Commit c6aa6fbc authored by Julia Garriga Ferrer's avatar Julia Garriga Ferrer
Browse files

Merge branch 'export_grainplot_data' into 'master'

Export grainplot data

See merge request !132
parents 5075e98e d0625ddc
Pipeline #52378 failed with stage
in 2 minutes and 14 seconds
......@@ -84,7 +84,7 @@ if __name__ == '__main__':
type=str,
default=None)
parser.add_argument(
'-tr',
'-td',
'--treated_data',
help='Directory to save treated data',
type=str,
......
......@@ -26,7 +26,7 @@
__authors__ = ["J. Garriga"]
__license__ = "MIT"
__date__ = "07/06/2021"
__date__ = "10/08/2021"
from matplotlib.colors import hsv_to_rgb
import numpy
......@@ -37,6 +37,7 @@ from silx.gui.plot import Plot2D
from silx.image.marchingsquares import find_contours
from silx.math.medianfilter import medfilt2d
from silx.utils.enum import Enum as _Enum
from silx.io.dictdump import dicttonx
import darfix
from .operationThread import OperationThread
......@@ -61,7 +62,7 @@ class GrainPlotWidget(qt.QMainWindow):
sigComputed = qt.Signal()
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
qt.QMainWindow.__init__(self, parent)
self._methodCB = qt.QComboBox()
self._methodCB.addItems(Method.values())
......@@ -90,10 +91,14 @@ class GrainPlotWidget(qt.QMainWindow):
levelsLayout.addWidget(self._contoursPlot, 2, 0, 1, 3)
self._levelsWidget.setLayout(levelsLayout)
self._mosaicityPlot = Plot2D(parent=self)
self._exportButton = qt.QPushButton("Export maps")
self._exportButton.setEnabled(False)
self._exportButton.clicked.connect(self.exportMaps)
layout.addWidget(self._methodCB)
layout.addWidget(self._levelsWidget)
layout.addWidget(self._plotWidget)
layout.addWidget(self._mosaicityPlot)
layout.addWidget(self._exportButton)
self._plotWidget.hide()
self._mosaicityPlot.hide()
self._mosaicityPlot.getColorBarWidget().hide()
......@@ -122,6 +127,7 @@ class GrainPlotWidget(qt.QMainWindow):
self.origin = (px, py)
self.scale = (xscale, yscale)
if self.dataset.dims.ndim > 1:
self._curves = {}
self.ori_dist, self.hsv_key = self.dataset.compute_mosaicity_colorkey()
xdim = self.dataset.dims.get(1)
ydim = self.dataset.dims.get(0)
......@@ -163,6 +169,7 @@ class GrainPlotWidget(qt.QMainWindow):
for i in range(rg):
self._methodCB.model().item(i).setEnabled(True)
self._methodCB.setCurrentIndex(0)
self._exportButton.setEnabled(True)
else:
print("\nComputation aborted")
......@@ -196,6 +203,7 @@ class GrainPlotWidget(qt.QMainWindow):
colors = self._curvesColormap.applyToData(levels)
xdim = self.dataset.dims.get(1)
ydim = self.dataset.dims.get(0)
self._curves = {}
for ipolygon, polygon in enumerate(polygons):
# iso contours
for icontour, contour in enumerate(polygon):
......@@ -204,15 +212,20 @@ class GrainPlotWidget(qt.QMainWindow):
# isClosed = numpy.allclose(contour[0], contour[-1])
x = contour[:, 1]
y = contour[:, 0]
x *= (xdim.unique_values[-1] - xdim.unique_values[0]) / (xdim.size - 1)
y *= (ydim.unique_values[-1] - ydim.unique_values[0]) / (ydim.size - 1)
rescale_x = (xdim.unique_values[-1] - xdim.unique_values[0]) / (xdim.size - 1)
rescale_y = (ydim.unique_values[-1] - ydim.unique_values[0]) / (ydim.size - 1)
x *= rescale_x
y *= rescale_y
if self._centerDataCheckbox.isChecked():
xcenter = (xdim.unique_values[-1] - xdim.unique_values[0]) / 2
x -= xcenter
ycenter = (ydim.unique_values[-1] - ydim.unique_values[0]) / 2
y -= ycenter
legend = "custom-polygon-%d" % icontour * (ipolygon + 1)
legend = "poly{}.{}".format(icontour, ipolygon)
self._curves[legend] = {
"points": (x.copy(), y.copy()),
"color": colors[ipolygon]
}
self._contoursPlot.addCurve(x=x, y=y, linestyle="-", linewidth=2.0,
legend=legend, resetzoom=False,
color=colors[ipolygon])
......@@ -273,3 +286,77 @@ class GrainPlotWidget(qt.QMainWindow):
img[img < Imin] = Imin
return medfilt2d(img)
def exportMaps(self):
"""
Creates dictionay with maps information and exports it to a nexus file
"""
if self.dataset and self.dataset.dims.ndim > 1:
nx = {
"entry": {
"MOSAICITY": {
">" + Method.MOSAICITY.name: "../maps/" + Method.MOSAICITY.name,
"@signal": Method.MOSAICITY.name,
"@NX_class": "NXdata"
},
"maps": {
Method.MOSAICITY.name: hsv_to_rgb(self._computeMosaicity()),
Method.MOSAICITY.name + "@interpretation": "rgba-image",
"@NX_class": "NXcollection"
},
Method.ORI_DIST.name: {
"key": {
"image": hsv_to_rgb(self.hsv_key),
"scale": self._contoursPlot.getImage().getScale(),
"xlabel": self._contoursPlot.getImage().getXLabel(),
"ylabel": self._contoursPlot.getImage().getYLabel(),
"image@interpretation": "rgba-image",
},
"curves": self._curves
},
"@NX_class": "NXentry",
"@default": "data",
},
"@NX_class": "NXroot",
"@default": "entry"
}
for axis, dim in self.dataset.dims:
nx["entry"]["maps"].update(
{
Method.COM.name: self._moments[axis][0],
Method.FWHM.name: self._moments[axis][1],
Method.SKEWNESS.name: self._moments[axis][2],
Method.KURTOSIS.name: self._moments[axis][3]
}
)
else:
nx = {
"entry": {
"data": {
">" + Method.COM.name: "../maps/" + Method.COM.name,
"@signal": Method.COM.name,
"@NX_class": "NXdata"
},
"maps": {
Method.COM.name: self._moments[0][0],
Method.FWHM.name: self._moments[0][1],
Method.SKEWNESS.name: self._moments[0][2],
Method.KURTOSIS.name: self._moments[0][3],
"@NX_class": "NXcollection"
},
"@NX_class": "NXentry",
"@default": "data",
},
"@NX_class": "NXroot",
"@default": "entry"
}
fileDialog = qt.QFileDialog()
fileDialog.setFileMode(fileDialog.AnyFile)
fileDialog.setAcceptMode(fileDialog.AcceptSave)
fileDialog.setOption(fileDialog.DontUseNativeDialog)
fileDialog.setDefaultSuffix(".h5")
if fileDialog.exec_():
dicttonx(nx, fileDialog.selectedFiles()[0])
......@@ -26,7 +26,7 @@
__authors__ = ["J. Garriga"]
__license__ = "MIT"
__date__ = "06/02/2020"
__date__ = "03/07/2021"
import logging
import h5py
......@@ -72,6 +72,92 @@ def advancement_display(iteration, total, prefix='', suffix='', decimals=1, leng
print()
def write_maps(h5_file, list_of_maps, default_map, entry, processing_order, data_path='/',
overwrite=True):
"""
Write a stack of components and its parameters into .h5
:param str h5_file: path to the hdf5 file
:param str entry: entry name
:param dict dimensions: Dictionary with the dimensions names and values
:param numpy.ndarray W: Matrix with the rocking curves values
:param numpy.ndarray data: Stack with the components
:param int processing_order: processing order of treatment
:param str data_path: path to store the data
"""
process_name = 'process_' + str(processing_order)
def get_interpretation(my_data):
"""Return hdf5 attribute for this type of data"""
if isinstance(my_data, numpy.ndarray):
if my_data.ndim == 1:
return 'spectrum'
elif my_data.ndim in (2, 3):
return 'image'
return None
def save_key(path_name, key_path, value, overwrite=True):
"""Save the given value to the associated path. Manage numpy arrays
and dictionaries"""
key_path = key_path.replace('.', '/')
# save if is dict
if isinstance(value, dict):
h5_path = '/'.join((path_name, key_path))
dicttoh5(value, h5file=h5_file, h5path=h5_path,
overwrite_data=True, mode='a')
else:
with h5py.File(h5_file, 'a') as h5f:
nx = h5f.require_group(path_name)
if overwrite and key_path in nx:
del nx[key_path]
try:
nx[key_path] = value
except TypeError as e:
_logger.error('Unable to write', str(key_path), 'reason is', str(e))
else:
interpretation = get_interpretation(value)
if interpretation:
nx[key_path].attrs['interpretation'] = interpretation
with h5py.File(h5_file, 'a') as h5f:
h5f.attrs["default"] = entry
nx_entry = h5f.require_group('/'.join((data_path, entry)))
nx_entry.attrs["NX_class"] = "NXentry"
nx_entry.attrs["default"] = "data"
nx_process = nx_entry.require_group(process_name)
nx_process.attrs['NX_class'] = "NXprocess"
if overwrite:
for key in ('program', 'version', 'date', 'processing_order'):
if key in nx_process:
del nx_process[key]
nx_process['program'] = 'darfix'
nx_process['version'] = '0.2'
nx_process['date'] = datetime.now().replace(microsecond=0).isoformat()
nx_process['processing_order'] = numpy.int32(processing_order)
results = nx_process.require_group("results")
results.attrs["NX_class"] = "NXcollection"
nx_data = nx_entry.require_group("data")
nx_data.attrs["NX_class"] = "NXdata"
default = list_of_maps[default_map]
source_addr = entry + "/" + process_name + "/results/" + default_map
results.attrs["target"] = default_map
save_key(results.name, default_map, default)
save_key(nx_data.name, default_map, h5f[source_addr])
for _map in list_of_maps:
if _map == default_map:
continue
if isinstance(list_of_maps[_map], dict):
maps = results.require_group(_map)
maps.attrs["NX_class"] = "NXcollection"
for method in list_of_maps[_map]:
save_key(maps.name, method, list_of_maps[_map][method])
else:
save_key(results.name, _map, list_of_maps[_map])
def read_components(h5_file):
"""
Read a stack of components and its parameters from a Nexus file.
......@@ -142,7 +228,7 @@ def write_components(h5_file, entry, dimensions, W, data, processing_order,
nx[key_path].attrs['interpretation'] = interpretation
with h5py.File(h5_file, 'a') as h5f:
h5f.attrs["default"] = "entry"
h5f.attrs["default"] = entry
nx_entry = h5f.require_group('/'.join((data_path, entry)))
nx_entry.attrs["NX_class"] = "NXentry"
nx_entry.attrs["default"] = "data"
......@@ -169,7 +255,7 @@ def write_components(h5_file, entry, dimensions, W, data, processing_order,
nx_data = nx_entry.require_group("data")
nx_data.attrs["NX_class"] = "NXdata"
nx_data.attrs["signal"] = "components"
source_addr = "entry/" + process_name + "/results/components"
source_addr = entry + "/" + process_name + "/results/components"
results.attrs["target"] = "components"
save_key(results.name, "W", W)
......
#!/usr/bin/env python
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
"""Sample code illustrating how to custom silx view into another application.
"""
__authors__ = ["J. Garriga"]
__license__ = "MIT"
__date__ = "10/08/2021"
import argparse
import signal
import sys
from silx.gui import qt
from silx.gui.plot import Plot2D
from silx.io.dictdump import nxtodict
from darfix.gui.grainPlotWidget import Method
class PlotOriDistContours(qt.QMainWindow):
def __init__(self, filename, parent=None):
qt.QMainWindow.__init__(self, parent)
plot = Plot2D(parent=self)
try:
h5file = nxtodict(filename)
key = h5file["entry"][Method.ORI_DIST.name]["key"]
curves = h5file["entry"][Method.ORI_DIST.name]["curves"]
plot.addImage(key["image"], xlabel=key["xlabel"], ylabel=key["ylabel"],
scale=tuple(key["scale"]))
for legend in curves:
if legend != "@NX_class":
curve = curves[legend]
plot.addCurve(x=curve["points"][0], y=curve["points"][1], linestyle="-", linewidth=2.0,
legend=legend, resetzoom=False, color=curve["color"])
except KeyError:
raise KeyError("Make sure orientation distribution key has been saved")
self.setCentralWidget(plot)
def createParser():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'file',
type=str,
nargs=argparse.ZERO_OR_MORE,
help='Data file to show (h5 file)')
return parser
def exec_(argv):
qapp = qt.QApplication([])
# add connection with ctrl + c signal
qt.QLocale.setDefault(qt.QLocale.c())
signal.signal(signal.SIGINT, sigintHandler)
sys.excepthook = qt.exceptionHandler
timer = qt.QTimer()
timer.start(500)
# Application have to wake up Python interpreter, else SIGINT is not
# catch
timer.timeout.connect(lambda: None)
parser = createParser()
options = parser.parse_args(argv[1:])
w = PlotOriDistContours(options.file[0])
w.show()
qapp.exec_()
def sigintHandler(*args):
"""Handler for the SIGINT signal."""
qt.QApplication.quit()
if __name__ == "__main__":
exec_(sys.argv)
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