Source code for lavuelib.memoryBufferGroupBox

# Copyright (C) 2017  DESY, Notkestr. 85, D-22607 Hamburg
#
# lavue is an image viewing program for photon science imaging detectors.
# Its usual application is as a live viewer using hidra as data source.
#
# 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 in  version 2
# of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA  02110-1301, USA.
#
# Authors:
#     Jan Kotanski <jan.kotanski@desy.de>
#

""" Memory Buffer widget """

import numpy as np
import sys
from .qtuic import uic
from pyqtgraph import QtCore
import os

try:
    from pyqtgraph import QtWidgets
except Exception:
    from pyqtgraph import QtGui as QtWidgets

if sys.version_info > (3,):
    unicode = str
else:
    bytes = str

_formclass, _baseclass = uic.loadUiType(
    os.path.join(os.path.dirname(os.path.abspath(__file__)),
                 "ui", "MemoryBufferGroupBox.ui"))


[docs]class MemoryBufferGroupBox(QtWidgets.QGroupBox): """ Set circular memory buffer for images """ #: (:class:`pyqtgraph.QtCore.pyqtSignal`) state updated signal bufferSizeChanged = QtCore.pyqtSignal(int) def __init__(self, parent=None): """ constructor :param parent: parent object :type parent: :class:`pyqtgraph.QtCore.QObject` """ QtWidgets.QGroupBox.__init__(self, parent) #: (:class:`Ui_MemoryBufferGroupBox') # ui_groupbox object from qtdesigner self.__ui = _formclass() self.__ui.setupUi(self) #: (:obj:`int`) number of frames in memory buffer self.__maxindex = 10 #: (:obj:`int`) maximal number of frames in memory buffer self.__maxbuffersize = 1000 #: (:obj:`bool`) is on status self.__isOn = False #: (:obj:`bool`) compute sum flag self.__computeSum = False #: (:obj:`bool`) is buffer full self.__full = False #: (:class:`numpy.ndarray`) image stack self.__imagestack = None #: (:obj:`int`) the current size self.__current = 1 #: (:class:`numpy.ndarray`) the last image self.__lastimage = None #: (:class:`numpy.ndarray`) the image sum self.__imagesum = None #: (:obj:`bool`) self.__first = True self.__ui.sizeSpinBox.setStyleSheet("") self.__ui.sizeSpinBox.setEnabled(False) self.__ui.resetPushButton.setEnabled(False) self.__ui.sizeSpinBox.valueChanged.connect(self._onBufferSizeChanged) self.__ui.onoffCheckBox.stateChanged.connect(self.onOff) self.__ui.resetPushButton.clicked.connect(self._onBufferSizeChanged)
[docs] def bufferSize(self): """ provides buffer size :returns: buffer size if buffer is on :rtype: int """ size = 0 if self.__isOn: size = self.__maxindex return size
[docs] def isOn(self): """ is on flag :returns: is on flag :rtype: bool """ return self.__isOn
[docs] def setBufferSize(self, buffersize): """ sets buffer size :param buffersize: maximal number of images in the buffer :type buffersize: :obj:`int` or :obj:`str` """ try: self.__maxindex = int(buffersize) except Exception: self.__maxindex = 10 self.__ui.sizeSpinBox.setValue(self.__maxindex) self._onBufferSizeChanged(self.__maxindex)
[docs] def setMaxBufferSize(self, maxbuffersize): """ sets maximal buffer size :param maxbuffersize: maximal number of images in the buffer :type maxbuffersize: :obj:`int` or :obj:`str` """ try: self.__maxbuffersize = int(maxbuffersize) except Exception: self.__maxbuffersize = 1000 if self.__maxbuffersize < self.__maxindex: self.__ui.sizeSpinBox.setValue(self.__maxbuffersize) self._onBufferSizeChanged(self.__maxbuffersize)
[docs] def setComputeSum(self, computesum): """ sets compute sum flag :param computesum: compute sum flag :type computesum: :obj:`bool` """ self.initialize() self.__computeSum = bool(computesum) self.initialize()
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot() def _onBufferSizeChanged(self, size=None): """ set size of image buffer in frame numbers :param size: buffer size :type size: :obj:`int` """ if size is not None: if self.__maxindex != size: if self.__maxbuffersize >= size: self.__maxindex = size else: self.__maxindex = self.__maxbuffersize self.__ui.sizeSpinBox.setValue(self.__maxbuffersize) self.initialize() self.bufferSizeChanged.emit(size or 0)
[docs] @QtCore.pyqtSlot(int) def onOff(self, status): """ switch on/off the widget :param status: flag on/off :type status: :obj:`int` """ self.__isOn = True if status else False self.__ui.onoffCheckBox.setCheckState(2 if self.__isOn else 0) self.initialize() self.__ui.sizeSpinBox.setEnabled(status) self.__ui.resetPushButton.setEnabled(status) self.__ui.sizeSpinBox.setStyleSheet("") self.bufferSizeChanged.emit((self.__maxindex or 0) if status else 0)
[docs] def initialize(self): """ initialize the filter """ self.__imagestack = None self.__lastimage = None self.__imagesum = None self.__current = 1 self.__first = True self.__full = False self.__ui.sizeSpinBox.setStyleSheet("")
[docs] def process(self, image, imagename): """ append image to the buffer and returns image buffer and metadata :param image: numpy array with an image :type image: :class:`numpy.ndarray` :param imagename: image name :type imagename: :obj:`str` :returns: numpy array with an image :rtype: (:class:`numpy.ndarray`, :obj`dict`<:obj:`str`, :obj:`str`>) or `None` """ if self.__isOn and image is not None: mdata = {} ics = int(self.__computeSum) if ics: mdata["suminthelast"] = True while len(image.shape) > 2: image = np.nansum(image, 0) if self.__lastimage is None \ or self.__lastimage.shape != image.shape \ or self.__lastimage.dtype != image.dtype \ or np.nanmax((self.__lastimage - image)): shape = image.shape dtype = image.dtype if self.__imagestack is not None: if self.__imagestack.shape[1:] != shape or \ self.__imagestack.dtype != dtype: self.__imagestack = None self.__imagesum = None self.__first = True self.__current = 1 if self.__imagestack is None: newshape = np.concatenate( ([self.__maxindex + 1 + ics], list(shape))) self.__imagestack = np.zeros(dtype=dtype, shape=newshape) if self.__current > self.__maxindex: self.__current = 1 if self.__current >= self.__maxindex: self.__full = True self.__ui.sizeSpinBox.setStyleSheet( "color: black;" "background-color: paleGreen;") lshape = len(self.__imagestack.shape) theoldest = None if lshape == 3: if ics: theoldest = np.array( self.__imagestack[self.__current, :, :]) self.__imagestack[self.__current, :, :] = image self.__imagestack[0, :, :] = image if ics: if self.__imagesum is None: if self.__full is True: self.__imagesum = np.nansum( self.__imagestack[1:-1, :, :], 0) else: self.__imagesum = np.nansum( self.__imagestack[ 1:(self.__current + 1), :, :], 0) else: self.__imagesum = self.__imagesum + image if self.__full is True: self.__imagesum = self.__imagesum - theoldest self.__imagestack[-1, :, :] = self.__imagesum elif lshape == 2: if ics: theoldest = np.array( self.__imagestack[self.__current, :]) self.__imagestack[self.__current, :] = image self.__imagestack[0, :] = image if ics: if self.__imagesum is None: if self.__full is True: self.__imagesum = np.nansum( self.__imagestack[1:-1, :], 0) else: self.__imagesum = np.nansum( self.__imagestack[ 1:(self.__current + 1), :], 0) else: self.__imagesum = self.__imagesum + image if self.__full is True: self.__imagesum = self.__imagesum - theoldest self.__imagestack[-1, :] = self.__imagesum elif lshape == 1: if ics: theoldest = np.array(self.__imagestack[self.__current]) self.__imagestack[self.__current] = image self.__imagestack[0] = image if ics: if self.__imagesum is None: if self.__full is True: self.__imagesum = np.nansum( self.__imagestack[1:-1], 0) else: self.__imagesum = np.nansum( self.__imagestack[ 1:(self.__current + 1)], 0) else: self.__imagesum = self.__imagesum + image if self.__full is True: self.__imagesum = self.__imagesum - theoldest self.__imagestack[-1] = self.__imagesum self.__lastimage = np.array(image) self.__current += 1 if self.__first: cblbl = {key: "%s:" % key for key in range(self.__maxindex + 1)} else: cblbl = {} mdata["channellabels"] = cblbl mdata["skipfirst"] = True if ics: mdata["suminthelast"] = True cblbl[0] = "0: the last image" # if imagename: # imagename = imagename.replace("\n", " ") cblbl[self.__current - 1] = "%s: %s" % ( self.__current - 1, imagename.replace("\n", " ")) self.__first = False if self.__imagestack is not None: return (self.__imagestack, mdata)
[docs] def changeView(self, show=False): """ shows or hides the histogram widget :param show: if histogram should be shown :type show: :obj:`bool` """ if show: self.show() else: self.hide()