new stuff

This commit is contained in:
Jerome Kieffer 2013-10-21 16:35:17 +02:00
parent fb5ab342bb
commit d240dc1dcf
3 changed files with 208 additions and 245 deletions

201
plugins/Lima/pyFAI_lima_1d.py Executable file
View File

@ -0,0 +1,201 @@
#!/usr/bin/python
from __future__ import with_statement, print_function
"""
pyFAI_lima
A graphical tool (based on PyQt4) for performing azimuthal integration of images coming from a camera.
No data are saved !
"""
__author__ = "Jerome Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "GPLv3+"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "11/10/2013"
__satus__ = "development"
import sys
import time
import signal
import threading
import numpy
import pyFAI.worker
from pyFAI.io import HDF5Writer, h5py
import pyopencl
import os
op = os.path
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("pyFAI")
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtCore import SIGNAL
import pyqtgraph as pg
UIC = os.path.splitext(os.path.abspath(__file__))[0] + ".ui"
window = None
class DoubleView(QtGui.QWidget):
def __init__(self, ip="192.168.5.19", fps=30, poni=None, json=None, writer=None):
QtGui.QWidget.__init__(self)
try:
uic.loadUi(UIC, self)
except AttributeError as error:
logger.error("I looks like your installation suffers from this bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=697348")
raise RuntimeError("Please upgrade your installation of PyQt (or apply the patch)")
self.ip = str(ip)
self.fps = float(fps)
self.label_ip.setText(str(ip))
self.label_fps.setText(str(fps))
self.cam = self.iface = self.ctrl = self.acq = None
self.cam = Basler.Camera(self.ip)
self.iface = Basler.Interface(self.cam)
self.ctrl = Core.CtControl(self.iface)
self.is_playing = False
self.connect(self.pushButton_play, SIGNAL("clicked()"), self.start_acq)
self.connect(self.pushButton_stop, SIGNAL("clicked()"), self.stop_acq)
self.last_frame = None
self.last_plotItem = None
self.last = time.time()
if poni:
worker = pyFAI.worker.Worker(ai=pyFAI.load(poni))
elif json:
worker = pyFAI.worker.Worker()
worker.setJsonConfig(json)
else:
worker = None
self.processLink = LinkPyFAI(worker, writer)
self.extMgr = self.ctrl.externalOperation()
self.myOp = self.extMgr.addOp(Core.USER_LINK_TASK, "pyFAILink", 0)
self.myOp.setLinkTask(self.processLink)
self.callback = StartAcqCallback(self.ctrl, self.processLink)
self.myOp.registerCallback(self.callback)
self.timer = QtCore.QTimer()
self.connect(self.timer, SIGNAL("timeout()"), self.update_img)
self.writer = writer
def start_acq(self):
if self.is_playing: return
self.is_playing = True
self.acq = self.ctrl.acquisition()
self.acq.setAcqNbFrames(0)
self.acq.setAcqExpoTime(1.0 / self.fps)
self.ctrl.prepareAcq()
self.ctrl.startAcq()
while self.ctrl.getStatus().ImageCounters.LastImageReady < 1:
time.sleep(0.1)
self.last_frame = self.ctrl.getStatus().ImageCounters.LastImageReady
raw_img = self.ctrl.ReadBaseImage().buffer
fai_img = self.ctrl.ReadImage().buffer
self.RawImg.setImage(raw_img.T)#, levels=[0, 4096])#, autoLevels=False, autoRange=False)
self.last_plotItem = pg.PlotDataItem(fai_img[:, 0], fai_img[:, 1])
self.FaiPlot.addItem(fai_img.T)#, levels=[0, 4096])#, autoLevels=False, autoRange=False)
self.last = time.time()
self.timer.start(1000.0 / self.fps)
def stop_acq(self):
if self.is_playing:
self.is_playing = False
self.ctrl.stopAcq()
self.timer.stop()
def update_img(self):
last_frame = self.ctrl.getStatus().ImageCounters.LastImageReady
if last_frame == self.last_frame:
return
if self.is_playing:
raw_img = self.ctrl.ReadBaseImage().buffer
fai_img = self.ctrl.ReadImage().buffer
self.RawImg.setImage(raw_img.T)#, levels=[0, 4096])#, autoLevels=False, autoRange=False)
# with self._sem:
plotItem = pg.PlotDataItem(fai_img[:, 0], fai_img[:, 1])
self.FaiPlot.removeItem(self.last_plotItem)
self.FaiPlot.addItem(self.last_plotItem)#, levels=[0, 4096])#, autoLevels=False, autoRange=False)
self.last_plotItem = plotItem
# self.FaiImg.setImage(fai_img.T)#, levels=[0, 4096])#, autoLevels=False, autoRange=False)
print("Measured display speed: %5.2f fps" % (1.0 / (time.time() - self.last)))
self.last = time.time()
if __name__ == "__main__":
from optparse import OptionParser
usage = "usage: %prog [options] "
version = "%prog " + pyFAI.version
description = """
pyFAI-lima is a graphical interface (based on Python/Qt4) to perform azimuthal integration
on a set of files grabbed from a Basler camera using LImA."""
epilog = """ """
parser = OptionParser(usage=usage, version=version, description=description, epilog=epilog)
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose", default=False,
help="switch to verbose/debug mode")
parser.add_option("-p", "--poni",
dest="poni", default=None,
help="PONI file containing the setup")
parser.add_option("-j", "--json",
dest="json", default=None,
help="json file containing the setup")
parser.add_option("-f", "--fps",
dest="fps", default="30",
help="Number of frames per seconds")
parser.add_option("-i", "--ip",
dest="ip", default="192.168.5.19",
help="IP address of the Basler camera")
parser.add_option("-l", "--lima",
dest="lima", default=None,
help="Base installation of LImA")
parser.add_option("-s", "--scan",
dest="scan", default=None,
help="Size of scan of the fastest motor")
parser.add_option("--no-gui",
dest="gui", default=True, action="store_false",
help="Process the dataset without showing the user interface.")
(options, args) = parser.parse_args()
if len(args) == 1:
hurl = args[0]
if hurl.startswith("hdf5:"):
hurl = hurl[5:]
if ":" in hurl:
hsplit = hurl.split(":")
hdfpath = hsplit[-1]
hdffile = ":".join(hsplit[:-1]) #special windows
else:
hdfpath = "test_LImA+pyFAI"
hdffile = hurl
writer = HDF5Writer(hdffile, hdfpath, options.scan)
elif len(args) > 1 :
logger.error("Specify the HDF5 output file like hdf5:///home/user/filename.h5:/path/to/group")
sys.exit(1)
else:
writer = None
if options.verbose:
logger.info("setLevel: debug")
logger.setLevel(logging.DEBUG)
if options.lima:
sys.path.insert(0, options.lima)
try:
from Lima import Core, Basler
except ImportError:
print("Is the PYTHONPATH correctly setup? I did not manage to import Lima")
sys.exit(1)
from limaFAI import LinkPyFAI, StartAcqCallback
if options.gui:
app = QtGui.QApplication([])
window = DoubleView(ip=options.ip, fps=options.fps, writer=writer)
#window.set_input_data(args)
window.show()
sys.exit(app.exec_())
else:
raise Exception("No sense!")
pass

View File

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>PyFAI live 2D integration</string>
<string>PyFAI live 1D integration</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@ -28,7 +28,7 @@
<widget class="ImageView" name="RawImg"/>
</item>
<item>
<widget class="ImageView" name="FaiImg"/>
<widget class="PlotWidget" name="FaiPlot"/>
</item>
</layout>
</item>
@ -88,6 +88,11 @@
<extends>QGraphicsView</extends>
<header>pyqtgraph</header>
</customwidget>
<customwidget>
<class>PlotWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -1,243 +0,0 @@
# !/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Project: Azimuthal integration
# https://github.com/kif
#
#
# Copyright (C) European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Jérôme Kieffer (Jerome.Kieffer@ESRF.eu)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__author__ = "Jerome Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "GPLv3+"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "15/10/2013"
__status__ = "beta"
__docformat__ = 'restructuredtext'
__doc__ = """
Stand-alone module which tries to offer interface to HDF5 via H5Py.
Can be imported without h5py but completely useless.
"""
import sys
import os
import time
import threading
import logging
logger = logging.getLogger("pyFAI.hdf5")
import types
import numpy
import posixpath
import json
#import threading
try:
import h5py
except ImportError:
h5py = None
logger.debug("h5py is missing")
def getIsoTime(forceTime=None):
"""
@param forceTime: enforce a given time (current by default)
@type forceTime: float
@return: the current time as an ISO8601 string
@rtype: string
"""
if forceTime is None:
forceTime = time.time()
localtime = time.localtime(forceTime)
gmtime = time.gmtime(forceTime)
tz_h = localtime.tm_hour - gmtime.tm_hour
tz_m = localtime.tm_min - gmtime.tm_min
return "%s%+03i:%02i" % (time.strftime("%Y-%m-%dT%H:%M:%S", localtime), tz_h, tz_m)
class HDF5Writer(object):
"""
Class allowing to write HDF5 Files.
"""
CONFIG = "pyFAI"
DATA = "data"
def __init__(self, filename, hpath="data", fast_scan_width=None):
"""
Constructor of an HDF5 writer:
@param filename: name of the file
@param hpath: name of the group: it will contain data (2-4D dataset), [tth|q|r] and pyFAI, group containing the configuration
@param fast_scan_width: set it to define the width of
"""
self.filename = filename
self.hpath = hpath
self.fast_scan_width = None
if fast_scan_width is not None:
try:
self.fast_scan_width = int(fast_scan_width)
except:
pass
self.hdf5 = None
self.group = None
self.dataset = None
self.pyFAI_grp = None
self.radial_values = None
self.azimuthal_values = None
self.chunk = None
self.shape = None
self.ndim = None
self._sem = threading.Semaphore()
self.config = {}
def __repr__(self):
return "HDF5 writer on file %s:%s %sinitialized" % (self.filename, self.hpath, "" if self._initialized else "un")
def init(self, config=None, lima_cfg=None):
"""
Initializes the HDF5 file for writing
@param config: the configuration of the worker as a dictionary
"""
with self._sem:
if not config:
config = self.config
self.config = config
open("config.json", "w").write(json.dumps(config, indent=4))
config["nbpt_rad"] = config.get("nbpt_rad", 1000)
dirname = os.path.dirname(self.filename)
if not os.path.exists(dirname):
os.makedirs(dirname)
if h5py:
try:
self.hdf5 = h5py.File(self.filename)
except IOError: #typically a corrupted HDF5 file !
os.unlink(self.filename)
self.hdf5 = h5py.File(self.filename)
else:
logger.error("No h5py library, no chance")
raise RuntimeError("No h5py library, no chance")
self.group = self.hdf5.require_group(self.hpath)
self.group.attrs["NX_class"] = "NXentry"
self.pyFAI_grp = self.hdf5.require_group(posixpath.join(self.hpath, self.CONFIG))
self.pyFAI_grp.attrs["desc"] = "PyFAI worker configuration"
for key, value in config.items():
if value is None:
continue
try:
self.pyFAI_grp[key] = value
except:
print("Unable to set %s: %s" % (key, value))
self.close()
sys.exit(1)
rad_name, rad_unit = str(config.get("unit", "2th_deg")).split("_", 1)
self.radial_values = self.group.require_dataset(rad_name, (config["nbpt_rad"],), numpy.float32)
if config.get("nbpt_azim", 0) > 1:
self.azimuthal_values = self.group.require_dataset("chi", (config["nbpt_azim"],), numpy.float32)
self.azimuthal_values.attrs["unit"] = "deg"
self.radial_values.attrs["interpretation"] = "scalar"
self.radial_values.attrs["long name"] = "Azimuthal angle"
self.radial_values.attrs["unit"] = rad_unit
self.radial_values.attrs["interpretation"] = "scalar"
self.radial_values.attrs["long name"] = "diffraction radial direction"
if self.fast_scan_width:
self.fast_motor = self.group.require_dataset("fast", (self.fast_scan_width,) , numpy.float32)
self.fast_motor.attrs["long name"] = "Fast motor position"
self.fast_motor.attrs["interpretation"] = "scalar"
self.fast_motor.attrs["axis"] = "1"
self.radial_values.attrs["axis"] = "2"
if self.azimuthal_values is not None:
chunk = 1, self.fast_scan_width, config["nbpt_azim"], config["nbpt_rad"]
self.ndim = 4
self.azimuthal_values.attrs["axis"] = "3"
else:
chunk = 1, self.fast_scan_width, config["nbpt_rad"]
self.ndim = 3
else:
self.radial_values.attrs["axis"] = "1"
if self.azimuthal_values is not None:
chunk = 1, config["nbpt_azim"], config["nbpt_rad"]
self.ndim = 3
self.azimuthal_values.attrs["axis"] = "2"
else:
chunk = 1, config["nbpt_rad"]
self.ndim = 2
if self.DATA in self.group:
del self.group[self.DATA]
self.dataset = self.group.require_dataset(self.DATA, chunk, dtype=numpy.float32, chunks=chunk,
maxshape=(None,) + chunk[1:])
if config.get("nbpt_azim", 0) > 1:
self.dataset.attrs["interpretation"] = "image"
else:
self.dataset.attrs["interpretation"] = "spectrum"
self.dataset.attrs["signal"] = "1"
self.chunk = chunk
self.shape = chunk
name = "Mapping " if self.fast_scan_width else "Scanning "
name += "2D" if config.get("nbpt_azim", 0) > 1 else "1D"
name += " experiment"
self.group["title"] = name
self.group["program"] = "PyFAI"
self.group["start_time"] = getIsoTime()
def flush(self, radial=None, azimuthal=None):
"""
Update some data like axis units and so on.
@param radial: position in radial direction
@param azimuthal: position in azimuthal direction
"""
if not self.hdf5:
raise RuntimeError('No opened file')
if radial is not None:
if radial.shape == self.radial_values.shape:
self.radial_values[:] = radial
else:
logger.warning("Unable to assign radial axis position")
if azimuthal is not None:
if azimuthal.shape == self.azimuthal_values.shape:
self.azimuthal_values[:] = azimuthal
else:
logger.warning("Unable to assign azimuthal axis position")
self.hdf5.flush()
def close(self):
if self.hdf5:
self.flush()
self.hdf5.close()
self.hdf5 = None
def write(self, data, index=0):
"""
Minimalistic method to limit the overhead.
"""
with self._sem:
if self.dataset is None:
logger.warning("Writer not initialized !")
return
if self.fast_scan_width:
index0, index1 = (index // self.fast_scan_width, index % self.fast_scan_width)
if index0 >= self.dataset.shape[0]:
self.dataset.resize(index0 + 1, axis=0)
self.dataset[index0, index1] = data
else:
if index >= self.dataset.shape[0]:
self.dataset.resize(index + 1, axis=0)
self.dataset[index] = data