summaryrefslogtreecommitdiff
path: root/src/silx/gui/plot/CompareImages.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/gui/plot/CompareImages.py')
-rw-r--r--src/silx/gui/plot/CompareImages.py1040
1 files changed, 368 insertions, 672 deletions
diff --git a/src/silx/gui/plot/CompareImages.py b/src/silx/gui/plot/CompareImages.py
index 80e0db3..3823ae2 100644
--- a/src/silx/gui/plot/CompareImages.py
+++ b/src/silx/gui/plot/CompareImages.py
@@ -29,505 +29,30 @@ __license__ = "MIT"
__date__ = "23/07/2018"
-import enum
import logging
import numpy
-import weakref
-import collections
import math
import silx.image.bilinear
from silx.gui import qt
from silx.gui import plot
-from silx.gui import icons
from silx.gui.colors import Colormap
from silx.gui.plot import tools
+from silx.utils.deprecation import deprecated_warning
from silx.utils.weakref import WeakMethodProxy
+from silx.gui.plot.items import Scatter
+from silx.math.colormap import normalize
-_logger = logging.getLogger(__name__)
-
-from silx.opencl import ocl
-if ocl is not None:
- try:
- from silx.opencl import sift
- except ImportError:
- # sift module is not available (e.g., in official Debian packages)
- sift = None
-else: # No OpenCL device or no pyopencl
- sift = None
-
-
-@enum.unique
-class VisualizationMode(enum.Enum):
- """Enum for each visualization mode available."""
- ONLY_A = 'a'
- ONLY_B = 'b'
- VERTICAL_LINE = 'vline'
- HORIZONTAL_LINE = 'hline'
- COMPOSITE_RED_BLUE_GRAY = "rbgchannel"
- COMPOSITE_RED_BLUE_GRAY_NEG = "rbgnegchannel"
- COMPOSITE_A_MINUS_B = "aminusb"
-
-
-@enum.unique
-class AlignmentMode(enum.Enum):
- """Enum for each alignment mode available."""
- ORIGIN = 'origin'
- CENTER = 'center'
- STRETCH = 'stretch'
- AUTO = 'auto'
-
-
-AffineTransformation = collections.namedtuple("AffineTransformation",
- ["tx", "ty", "sx", "sy", "rot"])
-"""Contains a 2D affine transformation: translation, scale and rotation"""
-
-
-class CompareImagesToolBar(qt.QToolBar):
- """ToolBar containing specific tools to custom the configuration of a
- :class:`CompareImages` widget
-
- Use :meth:`setCompareWidget` to connect this toolbar to a specific
- :class:`CompareImages` widget.
-
- :param Union[qt.QWidget,None] parent: Parent of this widget.
- """
- def __init__(self, parent=None):
- qt.QToolBar.__init__(self, parent)
-
- self.__compareWidget = None
-
- menu = qt.QMenu(self)
- self.__visualizationToolButton = qt.QToolButton(self)
- self.__visualizationToolButton.setMenu(menu)
- self.__visualizationToolButton.setPopupMode(qt.QToolButton.InstantPopup)
- self.addWidget(self.__visualizationToolButton)
- self.__visualizationGroup = qt.QActionGroup(self)
- self.__visualizationGroup.setExclusive(True)
- self.__visualizationGroup.triggered.connect(self.__visualizationModeChanged)
-
- icon = icons.getQIcon("compare-mode-a")
- action = qt.QAction(icon, "Display the first image only", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_A))
- action.setProperty("mode", VisualizationMode.ONLY_A)
- menu.addAction(action)
- self.__aModeAction = action
- self.__visualizationGroup.addAction(action)
-
- icon = icons.getQIcon("compare-mode-b")
- action = qt.QAction(icon, "Display the second image only", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_B))
- action.setProperty("mode", VisualizationMode.ONLY_B)
- menu.addAction(action)
- self.__bModeAction = action
- self.__visualizationGroup.addAction(action)
-
- icon = icons.getQIcon("compare-mode-vline")
- action = qt.QAction(icon, "Vertical compare mode", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_V))
- action.setProperty("mode", VisualizationMode.VERTICAL_LINE)
- menu.addAction(action)
- self.__vlineModeAction = action
- self.__visualizationGroup.addAction(action)
-
- icon = icons.getQIcon("compare-mode-hline")
- action = qt.QAction(icon, "Horizontal compare mode", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_H))
- action.setProperty("mode", VisualizationMode.HORIZONTAL_LINE)
- menu.addAction(action)
- self.__hlineModeAction = action
- self.__visualizationGroup.addAction(action)
-
- icon = icons.getQIcon("compare-mode-rb-channel")
- action = qt.QAction(icon, "Blue/red compare mode (additive mode)", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_C))
- action.setProperty("mode", VisualizationMode.COMPOSITE_RED_BLUE_GRAY)
- menu.addAction(action)
- self.__brChannelModeAction = action
- self.__visualizationGroup.addAction(action)
-
- icon = icons.getQIcon("compare-mode-rbneg-channel")
- action = qt.QAction(icon, "Yellow/cyan compare mode (subtractive mode)", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_W))
- action.setProperty("mode", VisualizationMode.COMPOSITE_RED_BLUE_GRAY_NEG)
- menu.addAction(action)
- self.__ycChannelModeAction = action
- self.__visualizationGroup.addAction(action)
-
- icon = icons.getQIcon("compare-mode-a-minus-b")
- action = qt.QAction(icon, "Raw A minus B compare mode", self)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- action.setShortcut(qt.QKeySequence(qt.Qt.Key_W))
- action.setProperty("mode", VisualizationMode.COMPOSITE_A_MINUS_B)
- menu.addAction(action)
- self.__ycChannelModeAction = action
- self.__visualizationGroup.addAction(action)
-
- menu = qt.QMenu(self)
- self.__alignmentToolButton = qt.QToolButton(self)
- self.__alignmentToolButton.setMenu(menu)
- self.__alignmentToolButton.setPopupMode(qt.QToolButton.InstantPopup)
- self.addWidget(self.__alignmentToolButton)
- self.__alignmentGroup = qt.QActionGroup(self)
- self.__alignmentGroup.setExclusive(True)
- self.__alignmentGroup.triggered.connect(self.__alignmentModeChanged)
-
- icon = icons.getQIcon("compare-align-origin")
- action = qt.QAction(icon, "Align images on their upper-left pixel", self)
- action.setProperty("mode", AlignmentMode.ORIGIN)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- self.__originAlignAction = action
- menu.addAction(action)
- self.__alignmentGroup.addAction(action)
-
- icon = icons.getQIcon("compare-align-center")
- action = qt.QAction(icon, "Center images", self)
- action.setProperty("mode", AlignmentMode.CENTER)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- self.__centerAlignAction = action
- menu.addAction(action)
- self.__alignmentGroup.addAction(action)
-
- icon = icons.getQIcon("compare-align-stretch")
- action = qt.QAction(icon, "Stretch the second image on the first one", self)
- action.setProperty("mode", AlignmentMode.STRETCH)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- self.__stretchAlignAction = action
- menu.addAction(action)
- self.__alignmentGroup.addAction(action)
-
- icon = icons.getQIcon("compare-align-auto")
- action = qt.QAction(icon, "Auto-alignment of the second image", self)
- action.setProperty("mode", AlignmentMode.AUTO)
- action.setIconVisibleInMenu(True)
- action.setCheckable(True)
- self.__autoAlignAction = action
- menu.addAction(action)
- if sift is None:
- action.setEnabled(False)
- action.setToolTip("Sift module is not available")
- self.__alignmentGroup.addAction(action)
-
- icon = icons.getQIcon("compare-keypoints")
- action = qt.QAction(icon, "Display/hide alignment keypoints", self)
- action.setCheckable(True)
- action.triggered.connect(self.__keypointVisibilityChanged)
- self.addAction(action)
- self.__displayKeypoints = action
-
- def setCompareWidget(self, widget):
- """
- Connect this tool bar to a specific :class:`CompareImages` widget.
-
- :param Union[None,CompareImages] widget: The widget to connect with.
- """
- compareWidget = self.getCompareWidget()
- if compareWidget is not None:
- compareWidget.sigConfigurationChanged.disconnect(self.__updateSelectedActions)
- compareWidget = widget
- if compareWidget is None:
- self.__compareWidget = None
- else:
- self.__compareWidget = weakref.ref(compareWidget)
- if compareWidget is not None:
- widget.sigConfigurationChanged.connect(self.__updateSelectedActions)
- self.__updateSelectedActions()
-
- def getCompareWidget(self):
- """Returns the connected widget.
-
- :rtype: CompareImages
- """
- if self.__compareWidget is None:
- return None
- else:
- return self.__compareWidget()
-
- def __updateSelectedActions(self):
- """
- Update the state of this tool bar according to the state of the
- connected :class:`CompareImages` widget.
- """
- widget = self.getCompareWidget()
- if widget is None:
- return
-
- mode = widget.getVisualizationMode()
- action = None
- for a in self.__visualizationGroup.actions():
- actionMode = a.property("mode")
- if mode == actionMode:
- action = a
- break
- old = self.__visualizationGroup.blockSignals(True)
- if action is not None:
- # Check this action
- action.setChecked(True)
- else:
- action = self.__visualizationGroup.checkedAction()
- if action is not None:
- # Uncheck this action
- action.setChecked(False)
- self.__updateVisualizationMenu()
- self.__visualizationGroup.blockSignals(old)
-
- mode = widget.getAlignmentMode()
- action = None
- for a in self.__alignmentGroup.actions():
- actionMode = a.property("mode")
- if mode == actionMode:
- action = a
- break
- old = self.__alignmentGroup.blockSignals(True)
- if action is not None:
- # Check this action
- action.setChecked(True)
- else:
- action = self.__alignmentGroup.checkedAction()
- if action is not None:
- # Uncheck this action
- action.setChecked(False)
- self.__updateAlignmentMenu()
- self.__alignmentGroup.blockSignals(old)
-
- def __visualizationModeChanged(self, selectedAction):
- """Called when user requesting changes of the visualization mode.
- """
- self.__updateVisualizationMenu()
- widget = self.getCompareWidget()
- if widget is not None:
- mode = selectedAction.property("mode")
- widget.setVisualizationMode(mode)
-
- def __updateVisualizationMenu(self):
- """Update the state of the action containing visualization menu.
- """
- selectedAction = self.__visualizationGroup.checkedAction()
- if selectedAction is not None:
- self.__visualizationToolButton.setText(selectedAction.text())
- self.__visualizationToolButton.setIcon(selectedAction.icon())
- self.__visualizationToolButton.setToolTip(selectedAction.toolTip())
- else:
- self.__visualizationToolButton.setText("")
- self.__visualizationToolButton.setIcon(qt.QIcon())
- self.__visualizationToolButton.setToolTip("")
-
- def __alignmentModeChanged(self, selectedAction):
- """Called when user requesting changes of the alignment mode.
- """
- self.__updateAlignmentMenu()
- widget = self.getCompareWidget()
- if widget is not None:
- mode = selectedAction.property("mode")
- widget.setAlignmentMode(mode)
-
- def __updateAlignmentMenu(self):
- """Update the state of the action containing alignment menu.
- """
- selectedAction = self.__alignmentGroup.checkedAction()
- if selectedAction is not None:
- self.__alignmentToolButton.setText(selectedAction.text())
- self.__alignmentToolButton.setIcon(selectedAction.icon())
- self.__alignmentToolButton.setToolTip(selectedAction.toolTip())
- else:
- self.__alignmentToolButton.setText("")
- self.__alignmentToolButton.setIcon(qt.QIcon())
- self.__alignmentToolButton.setToolTip("")
-
- def __keypointVisibilityChanged(self):
- """Called when action managing keypoints visibility changes"""
- widget = self.getCompareWidget()
- if widget is not None:
- keypointsVisible = self.__displayKeypoints.isChecked()
- widget.setKeypointsVisible(keypointsVisible)
+from .tools.compare.core import sift
+from .tools.compare.core import VisualizationMode
+from .tools.compare.core import AlignmentMode
+from .tools.compare.core import AffineTransformation
+from .tools.compare.toolbar import CompareImagesToolBar
+from .tools.compare.statusbar import CompareImagesStatusBar
+from .tools.compare.core import _CompareImageItem
-class CompareImagesStatusBar(qt.QStatusBar):
- """StatusBar containing specific information contained in a
- :class:`CompareImages` widget
-
- Use :meth:`setCompareWidget` to connect this toolbar to a specific
- :class:`CompareImages` widget.
-
- :param Union[qt.QWidget,None] parent: Parent of this widget.
- """
- def __init__(self, parent=None):
- qt.QStatusBar.__init__(self, parent)
- self.setSizeGripEnabled(False)
- self.layout().setSpacing(0)
- self.__compareWidget = None
- self._label1 = qt.QLabel(self)
- self._label1.setFrameShape(qt.QFrame.WinPanel)
- self._label1.setFrameShadow(qt.QFrame.Sunken)
- self._label2 = qt.QLabel(self)
- self._label2.setFrameShape(qt.QFrame.WinPanel)
- self._label2.setFrameShadow(qt.QFrame.Sunken)
- self._transform = qt.QLabel(self)
- self._transform.setFrameShape(qt.QFrame.WinPanel)
- self._transform.setFrameShadow(qt.QFrame.Sunken)
- self.addWidget(self._label1)
- self.addWidget(self._label2)
- self.addWidget(self._transform)
- self._pos = None
- self._updateStatusBar()
-
- def setCompareWidget(self, widget):
- """
- Connect this tool bar to a specific :class:`CompareImages` widget.
-
- :param Union[None,CompareImages] widget: The widget to connect with.
- """
- compareWidget = self.getCompareWidget()
- if compareWidget is not None:
- compareWidget.getPlot().sigPlotSignal.disconnect(self.__plotSignalReceived)
- compareWidget.sigConfigurationChanged.disconnect(self.__dataChanged)
- compareWidget = widget
- if compareWidget is None:
- self.__compareWidget = None
- else:
- self.__compareWidget = weakref.ref(compareWidget)
- if compareWidget is not None:
- compareWidget.getPlot().sigPlotSignal.connect(self.__plotSignalReceived)
- compareWidget.sigConfigurationChanged.connect(self.__dataChanged)
-
- def getCompareWidget(self):
- """Returns the connected widget.
-
- :rtype: CompareImages
- """
- if self.__compareWidget is None:
- return None
- else:
- return self.__compareWidget()
-
- def __plotSignalReceived(self, event):
- """Called when old style signals at emmited from the plot."""
- if event["event"] == "mouseMoved":
- x, y = event["x"], event["y"]
- self.__mouseMoved(x, y)
-
- def __mouseMoved(self, x, y):
- """Called when mouse move over the plot."""
- self._pos = x, y
- self._updateStatusBar()
-
- def __dataChanged(self):
- """Called when internal data from the connected widget changes."""
- self._updateStatusBar()
-
- def _formatData(self, data):
- """Format pixel of an image.
-
- It supports intensity, RGB, and RGBA.
-
- :param Union[int,float,numpy.ndarray,str]: Value of a pixel
- :rtype: str
- """
- if data is None:
- return "No data"
- if isinstance(data, (int, numpy.integer)):
- return "%d" % data
- if isinstance(data, (float, numpy.floating)):
- return "%f" % data
- if isinstance(data, numpy.ndarray):
- # RGBA value
- if data.shape == (3,):
- return "R:%d G:%d B:%d" % (data[0], data[1], data[2])
- elif data.shape == (4,):
- return "R:%d G:%d B:%d A:%d" % (data[0], data[1], data[2], data[3])
- _logger.debug("Unsupported data format %s. Cast it to string.", type(data))
- return str(data)
-
- def _updateStatusBar(self):
- """Update the content of the status bar"""
- widget = self.getCompareWidget()
- if widget is None:
- self._label1.setText("Image1: NA")
- self._label2.setText("Image2: NA")
- self._transform.setVisible(False)
- else:
- transform = widget.getTransformation()
- self._transform.setVisible(transform is not None)
- if transform is not None:
- has_notable_translation = not numpy.isclose(transform.tx, 0.0, atol=0.01) \
- or not numpy.isclose(transform.ty, 0.0, atol=0.01)
- has_notable_scale = not numpy.isclose(transform.sx, 1.0, atol=0.01) \
- or not numpy.isclose(transform.sy, 1.0, atol=0.01)
- has_notable_rotation = not numpy.isclose(transform.rot, 0.0, atol=0.01)
-
- strings = []
- if has_notable_translation:
- strings.append("Translation")
- if has_notable_scale:
- strings.append("Scale")
- if has_notable_rotation:
- strings.append("Rotation")
- if strings == []:
- has_translation = not numpy.isclose(transform.tx, 0.0) \
- or not numpy.isclose(transform.ty, 0.0)
- has_scale = not numpy.isclose(transform.sx, 1.0) \
- or not numpy.isclose(transform.sy, 1.0)
- has_rotation = not numpy.isclose(transform.rot, 0.0)
- if has_translation or has_scale or has_rotation:
- text = "No big changes"
- else:
- text = "No changes"
- else:
- text = "+".join(strings)
- self._transform.setText("Align: " + text)
-
- strings = []
- if not numpy.isclose(transform.ty, 0.0):
- strings.append("Translation x: %0.3fpx" % transform.tx)
- if not numpy.isclose(transform.ty, 0.0):
- strings.append("Translation y: %0.3fpx" % transform.ty)
- if not numpy.isclose(transform.sx, 1.0):
- strings.append("Scale x: %0.3f" % transform.sx)
- if not numpy.isclose(transform.sy, 1.0):
- strings.append("Scale y: %0.3f" % transform.sy)
- if not numpy.isclose(transform.rot, 0.0):
- strings.append("Rotation: %0.3fdeg" % (transform.rot * 180 / numpy.pi))
- if strings == []:
- text = "No transformation"
- else:
- text = "\n".join(strings)
- self._transform.setToolTip(text)
-
- if self._pos is None:
- self._label1.setText("Image1: NA")
- self._label2.setText("Image2: NA")
- else:
- data1, data2 = widget.getRawPixelData(self._pos[0], self._pos[1])
- if isinstance(data1, str):
- self._label1.setToolTip(data1)
- text1 = "NA"
- else:
- self._label1.setToolTip("")
- text1 = self._formatData(data1)
- if isinstance(data2, str):
- self._label2.setToolTip(data2)
- text2 = "NA"
- else:
- self._label2.setToolTip("")
- text2 = self._formatData(data2)
- self._label1.setText("Image1: %s" % text1)
- self._label2.setText("Image2: %s" % text2)
+_logger = logging.getLogger(__name__)
class CompareImages(qt.QMainWindow):
@@ -550,22 +75,28 @@ class CompareImages(qt.QMainWindow):
sigConfigurationChanged = qt.Signal()
"""Emitted when the configuration of the widget (visualization mode,
- alignement mode...) have changed."""
+ alignment mode...) have changed."""
def __init__(self, parent=None, backend=None):
qt.QMainWindow.__init__(self, parent)
self._resetZoomActive = True
self._colormap = Colormap()
"""Colormap shared by all modes, except the compose images (rgb image)"""
- self._colormapKeyPoints = Colormap('spring')
+ self._colormapKeyPoints = Colormap("spring")
"""Colormap used for sift keypoints"""
+ self._colormap.sigChanged.connect(self.__colormapChanged)
+
if parent is None:
- self.setWindowTitle('Compare images')
+ self.setWindowTitle("Compare images")
else:
self.setWindowFlags(qt.Qt.Widget)
self.__transformation = None
+ self.__item = _CompareImageItem()
+ self.__item.setName("_virtual")
+ self.__item.setColormap(self._colormap)
+
self.__raw1 = None
self.__raw2 = None
self.__data1 = None
@@ -574,35 +105,44 @@ class CompareImages(qt.QMainWindow):
self.__plot = plot.PlotWidget(parent=self, backend=backend)
self.__plot.setDefaultColormap(self._colormap)
- self.__plot.getXAxis().setLabel('Columns')
- self.__plot.getYAxis().setLabel('Rows')
- if silx.config.DEFAULT_PLOT_IMAGE_Y_AXIS_ORIENTATION == 'downward':
+ self.__plot.getXAxis().setLabel("Columns")
+ self.__plot.getYAxis().setLabel("Rows")
+ if silx.config.DEFAULT_PLOT_IMAGE_Y_AXIS_ORIENTATION == "downward":
self.__plot.getYAxis().setInverted(True)
+ self.__plot.addItem(self.__item)
+ self.__plot.setActiveImage(self.__item)
self.__plot.setKeepDataAspectRatio(True)
self.__plot.sigPlotSignal.connect(self.__plotSlot)
self.__plot.setAxesDisplayed(False)
+ self.__scatter = Scatter()
+ self.__scatter.setZValue(1)
+ self.__scatter.setColormap(self._colormapKeyPoints)
+ self.__plot.addItem(self.__scatter)
+
self.setCentralWidget(self.__plot)
legend = VisualizationMode.VERTICAL_LINE.name
self.__plot.addXMarker(
- 0,
- legend=legend,
- text='',
- draggable=True,
- color='blue',
- constraint=WeakMethodProxy(self.__separatorConstraint))
+ 0,
+ legend=legend,
+ text="",
+ draggable=True,
+ color="blue",
+ constraint=WeakMethodProxy(self.__separatorConstraint),
+ )
self.__vline = self.__plot._getMarker(legend)
legend = VisualizationMode.HORIZONTAL_LINE.name
self.__plot.addYMarker(
- 0,
- legend=legend,
- text='',
- draggable=True,
- color='blue',
- constraint=WeakMethodProxy(self.__separatorConstraint))
+ 0,
+ legend=legend,
+ text="",
+ draggable=True,
+ color="blue",
+ constraint=WeakMethodProxy(self.__separatorConstraint),
+ )
self.__hline = self.__plot._getMarker(legend)
# default values
@@ -630,6 +170,26 @@ class CompareImages(qt.QMainWindow):
if self._statusBar is not None:
self.setStatusBar(self._statusBar)
+ def __getSealedColormap(self):
+ vrange = self._colormap.getColormapRange(
+ self.__item.getColormappedData(copy=False)
+ )
+ sealed = self._colormap.copy()
+ sealed.setVRange(*vrange)
+ return sealed
+
+ def __colormapChanged(self):
+ sealed = self.__getSealedColormap()
+ if self.__image1 is not None:
+ if self.__getImageMode(self.__image1.getData(copy=False)) == "intensity":
+ self.__image1.setColormap(sealed)
+ if self.__image2 is not None:
+ if self.__getImageMode(self.__image2.getData(copy=False)) == "intensity":
+ self.__image2.setColormap(sealed)
+
+ if "COMPOSITE" in self.__visualizationMode.name:
+ self.__updateData()
+
def _createStatusBar(self, plot):
self._statusBar = CompareImagesStatusBar(self)
self._statusBar.setCompareWidget(self)
@@ -644,6 +204,9 @@ class CompareImages(qt.QMainWindow):
toolBar.setCompareWidget(self)
self._compareToolBar = toolBar
+ def _getVirtualPlotItem(self):
+ return self.__item
+
def getPlot(self):
"""Returns the plot which is used to display the images.
@@ -676,10 +239,15 @@ class CompareImages(qt.QMainWindow):
It also could be a string containing information is some cases.
:rtype: Tuple(Union[int,float,numpy.ndarray,str],Union[int,float,numpy.ndarray,str])
"""
- data2 = None
alignmentMode = self.__alignmentMode
raw1, raw2 = self.__raw1, self.__raw2
- if alignmentMode == AlignmentMode.ORIGIN:
+
+ if raw1 is None or raw2 is None:
+ x1 = x
+ y1 = y
+ x2 = x
+ y2 = y
+ elif alignmentMode == AlignmentMode.ORIGIN:
x1 = x
y1 = y
x2 = x
@@ -700,22 +268,29 @@ class CompareImages(qt.QMainWindow):
x1 = x
y1 = y
# Not implemented
- data2 = "Not implemented with sift"
+ x2 = -1
+ y2 = -1
else:
- assert(False)
+ assert False
x1, y1 = int(x1), int(y1)
- if raw1 is None or y1 < 0 or y1 >= raw1.shape[0] or x1 < 0 or x1 >= raw1.shape[1]:
- data1 = None
+ x2, y2 = int(x2), int(y2)
+
+ if raw1 is None:
+ data1 = "No image A"
+ elif y1 < 0 or y1 >= raw1.shape[0] or x1 < 0 or x1 >= raw1.shape[1]:
+ data1 = ""
else:
data1 = raw1[y1, x1]
- if data2 is None:
- x2, y2 = int(x2), int(y2)
- if raw2 is None or y2 < 0 or y2 >= raw2.shape[0] or x2 < 0 or x2 >= raw2.shape[1]:
- data2 = None
- else:
- data2 = raw2[y2, x2]
+ if raw2 is None:
+ data2 = "No image B"
+ elif alignmentMode == AlignmentMode.AUTO:
+ data2 = "Not implemented with sift"
+ elif y2 < 0 or y2 >= raw2.shape[0] or x2 < 0 or x2 >= raw2.shape[1]:
+ data2 = None
+ else:
+ data2 = raw2[y2, x2]
return data1, data2
@@ -726,20 +301,31 @@ class CompareImages(qt.QMainWindow):
"""
if self.__visualizationMode == mode:
return
- previousMode = self.getVisualizationMode()
self.__visualizationMode = mode
- mode = self.getVisualizationMode()
+ self.__item.setVizualisationMode(mode)
self.__vline.setVisible(mode == VisualizationMode.VERTICAL_LINE)
self.__hline.setVisible(mode == VisualizationMode.HORIZONTAL_LINE)
- visModeRawDisplay = (VisualizationMode.ONLY_A,
- VisualizationMode.ONLY_B,
- VisualizationMode.VERTICAL_LINE,
- VisualizationMode.HORIZONTAL_LINE)
- updateColormap = not(previousMode in visModeRawDisplay and
- mode in visModeRawDisplay)
- self.__updateData(updateColormap=updateColormap)
+ self.__updateData()
self.sigConfigurationChanged.emit()
+ def centerLines(self):
+ """Center the line used to compare the 2 images."""
+ if self.__image1 is None:
+ return
+ data_range = self.__plot.getDataRange()
+
+ if data_range[0] is not None:
+ cx = (data_range[0][0] + data_range[0][1]) * 0.5
+ else:
+ cx = 0
+ if data_range[1] is not None:
+ cy = (data_range[1][0] + data_range[1][1]) * 0.5
+ else:
+ cy = 0
+ self.__vline.setPosition(cx, cy)
+ self.__hline.setPosition(cx, cy)
+ self.__updateSeparators()
+
def getVisualizationMode(self):
"""Returns the current interaction mode."""
return self.__visualizationMode
@@ -752,13 +338,17 @@ class CompareImages(qt.QMainWindow):
if self.__alignmentMode == mode:
return
self.__alignmentMode = mode
- self.__updateData(updateColormap=False)
+ self.__updateData()
self.sigConfigurationChanged.emit()
def getAlignmentMode(self):
"""Returns the current selected alignemnt mode."""
return self.__alignmentMode
+ def getKeypointsVisible(self):
+ """Returns true if the keypoints are displayed"""
+ return self.__keypointsVisible
+
def setKeypointsVisible(self, isVisible):
"""Set keypoints visibility.
@@ -776,16 +366,16 @@ class CompareImages(qt.QMainWindow):
def __plotSlot(self, event):
"""Handle events from the plot"""
- if event['event'] in ('markerMoving', 'markerMoved'):
+ if event["event"] in ("markerMoving", "markerMoved"):
mode = self.getVisualizationMode()
legend = mode.name
- if event['label'] == legend:
+ if event["label"] == legend:
if mode == VisualizationMode.VERTICAL_LINE:
- value = int(float(str(event['xdata'])))
+ value = int(float(str(event["xdata"])))
elif mode == VisualizationMode.HORIZONTAL_LINE:
- value = int(float(str(event['ydata'])))
+ value = int(float(str(event["ydata"])))
else:
- assert(False)
+ assert False
if self.__previousSeparatorPosition != value:
self.__separatorMoved(value)
self.__previousSeparatorPosition = value
@@ -807,8 +397,7 @@ class CompareImages(qt.QMainWindow):
return x, y
def __updateSeparators(self):
- """Redraw images according to the current state of the separators.
- """
+ """Redraw images according to the current state of the separators."""
mode = self.getVisualizationMode()
if mode == VisualizationMode.VERTICAL_LINE:
pos = self.__vline.getXPosition()
@@ -820,7 +409,8 @@ class CompareImages(qt.QMainWindow):
self.__previousSeparatorPosition = pos
else:
self.__image1.setOrigin((0, 0))
- self.__image2.setOrigin((0, 0))
+ if self.__image2 is not None:
+ self.__image2.setOrigin((0, 0))
def __separatorMoved(self, pos):
"""Called when vertical or horizontal separators have moved.
@@ -840,8 +430,9 @@ class CompareImages(qt.QMainWindow):
data1 = self.__data1[:, 0:pos]
data2 = self.__data2[:, pos:]
self.__image1.setData(data1, copy=False)
- self.__image2.setData(data2, copy=False)
- self.__image2.setOrigin((pos, 0))
+ if self.__image2 is not None:
+ self.__image2.setData(data2, copy=False)
+ self.__image2.setOrigin((pos, 0))
elif mode == VisualizationMode.HORIZONTAL_LINE:
pos = int(pos)
if pos <= 0:
@@ -851,150 +442,209 @@ class CompareImages(qt.QMainWindow):
data1 = self.__data1[0:pos, :]
data2 = self.__data2[pos:, :]
self.__image1.setData(data1, copy=False)
- self.__image2.setData(data2, copy=False)
- self.__image2.setOrigin((0, pos))
+ if self.__image2 is not None:
+ self.__image2.setData(data2, copy=False)
+ self.__image2.setOrigin((0, pos))
else:
- assert(False)
+ assert False
- def setData(self, image1, image2, updateColormap=True):
+ def clear(self):
+ self.setData(None, None)
+
+ def setData(self, image1, image2, updateColormap="deprecated"):
"""Set images to compare.
Images can contains floating-point or integer values, or RGB and RGBA
values, but should have comparable intensities.
RGB and RGBA images are provided as an array as `[width,height,channels]`
- of usigned integer 8-bits or floating-points between 0.0 to 1.0.
+ of unsigned integer 8-bits or floating-points between 0.0 to 1.0.
:param numpy.ndarray image1: The first image
:param numpy.ndarray image2: The second image
"""
+ if updateColormap != "deprecated":
+ deprecated_warning(
+ "Argument", "setData's updateColormap argument", since_version="2.0.0"
+ )
+
self.__raw1 = image1
self.__raw2 = image2
- self.__updateData(updateColormap=updateColormap)
+ self.__updateData()
if self.isAutoResetZoom():
self.__plot.resetZoom()
- def setImage1(self, image1, updateColormap=True):
+ def setImage1(self, image1, updateColormap="deprecated"):
"""Set image1 to be compared.
Images can contains floating-point or integer values, or RGB and RGBA
values, but should have comparable intensities.
RGB and RGBA images are provided as an array as `[width,height,channels]`
- of usigned integer 8-bits or floating-points between 0.0 to 1.0.
+ of unsigned integer 8-bits or floating-points between 0.0 to 1.0.
:param numpy.ndarray image1: The first image
"""
+ if updateColormap != "deprecated":
+ deprecated_warning(
+ "Argument", "setImage1's updateColormap argument", since_version="2.0.0"
+ )
+
self.__raw1 = image1
- self.__updateData(updateColormap=updateColormap)
+ self.__updateData()
if self.isAutoResetZoom():
self.__plot.resetZoom()
- def setImage2(self, image2, updateColormap=True):
+ def setImage2(self, image2, updateColormap="deprecated"):
"""Set image2 to be compared.
Images can contains floating-point or integer values, or RGB and RGBA
values, but should have comparable intensities.
RGB and RGBA images are provided as an array as `[width,height,channels]`
- of usigned integer 8-bits or floating-points between 0.0 to 1.0.
+ of unsigned integer 8-bits or floating-points between 0.0 to 1.0.
:param numpy.ndarray image2: The second image
"""
+ if updateColormap != "deprecated":
+ deprecated_warning(
+ "Argument", "setImage2's updateColormap argument", since_version="2.0.0"
+ )
+
self.__raw2 = image2
- self.__updateData(updateColormap=updateColormap)
+ self.__updateData()
if self.isAutoResetZoom():
self.__plot.resetZoom()
def __updateKeyPoints(self):
- """Update the displayed keypoints using cached keypoints.
- """
- if self.__keypointsVisible:
+ """Update the displayed keypoints using cached keypoints."""
+ if self.__keypointsVisible and self.__matching_keypoints:
data = self.__matching_keypoints
else:
data = [], [], []
- self.__plot.addScatter(x=data[0],
- y=data[1],
- z=1,
- value=data[2],
- colormap=self._colormapKeyPoints,
- legend="keypoints")
-
- def __updateData(self, updateColormap):
+ self.__scatter.setData(x=data[0], y=data[1], value=data[2])
+
+ def __updateData(self):
"""Compute aligned image when the alignment mode changes.
This function cache input images which are used when
vertical/horizontal separators moves.
"""
raw1, raw2 = self.__raw1, self.__raw2
- if raw1 is None or raw2 is None:
- return
alignmentMode = self.getAlignmentMode()
self.__transformation = None
- if alignmentMode == AlignmentMode.ORIGIN:
- yy = max(raw1.shape[0], raw2.shape[0])
- xx = max(raw1.shape[1], raw2.shape[1])
- size = yy, xx
- data1 = self.__createMarginImage(raw1, size, transparent=True)
- data2 = self.__createMarginImage(raw2, size, transparent=True)
- self.__matching_keypoints = [0.0], [0.0], [1.0]
- elif alignmentMode == AlignmentMode.CENTER:
- yy = max(raw1.shape[0], raw2.shape[0])
- xx = max(raw1.shape[1], raw2.shape[1])
- size = yy, xx
- data1 = self.__createMarginImage(raw1, size, transparent=True, center=True)
- data2 = self.__createMarginImage(raw2, size, transparent=True, center=True)
- self.__matching_keypoints = ([data1.shape[1] // 2],
- [data1.shape[0] // 2],
- [1.0])
- elif alignmentMode == AlignmentMode.STRETCH:
- data1 = raw1
- data2 = self.__rescaleImage(raw2, data1.shape)
- self.__matching_keypoints = ([0, data1.shape[1], data1.shape[1], 0],
- [0, 0, data1.shape[0], data1.shape[0]],
- [1.0, 1.0, 1.0, 1.0])
- elif alignmentMode == AlignmentMode.AUTO:
- # TODO: sift implementation do not support RGBA images
- yy = max(raw1.shape[0], raw2.shape[0])
- xx = max(raw1.shape[1], raw2.shape[1])
- size = yy, xx
- data1 = self.__createMarginImage(raw1, size)
- data2 = self.__createMarginImage(raw2, size)
- self.__matching_keypoints = [0.0], [0.0], [1.0]
- try:
- data1, data2 = self.__createSiftData(data1, data2)
- if data2 is None:
- raise ValueError("Unexpected None value")
- except Exception as e:
- # TODO: Display it on the GUI
- _logger.error(e)
- self.__setDefaultAlignmentMode()
- return
+ if raw1 is None or raw2 is None:
+ # No need to realign the 2 images
+ # But create a dummy image when there is None for simplification
+ if raw1 is None:
+ data1 = numpy.empty((0, 0))
+ else:
+ data1 = raw1
+ if raw2 is None:
+ data2 = numpy.empty((0, 0))
+ else:
+ data2 = raw2
+ self.__matching_keypoints = None
else:
- assert(False)
+ if alignmentMode == AlignmentMode.ORIGIN:
+ yy = max(raw1.shape[0], raw2.shape[0])
+ xx = max(raw1.shape[1], raw2.shape[1])
+ size = yy, xx
+ data1 = self.__createMarginImage(raw1, size, transparent=True)
+ data2 = self.__createMarginImage(raw2, size, transparent=True)
+ self.__matching_keypoints = [0.0], [0.0], [1.0]
+ elif alignmentMode == AlignmentMode.CENTER:
+ yy = max(raw1.shape[0], raw2.shape[0])
+ xx = max(raw1.shape[1], raw2.shape[1])
+ size = yy, xx
+ data1 = self.__createMarginImage(
+ raw1, size, transparent=True, center=True
+ )
+ data2 = self.__createMarginImage(
+ raw2, size, transparent=True, center=True
+ )
+ self.__matching_keypoints = (
+ [data1.shape[1] // 2],
+ [data1.shape[0] // 2],
+ [1.0],
+ )
+ elif alignmentMode == AlignmentMode.STRETCH:
+ data1 = raw1
+ data2 = self.__rescaleImage(raw2, data1.shape)
+ self.__matching_keypoints = (
+ [0, data1.shape[1], data1.shape[1], 0],
+ [0, 0, data1.shape[0], data1.shape[0]],
+ [1.0, 1.0, 1.0, 1.0],
+ )
+ elif alignmentMode == AlignmentMode.AUTO:
+ # TODO: sift implementation do not support RGBA images
+ yy = max(raw1.shape[0], raw2.shape[0])
+ xx = max(raw1.shape[1], raw2.shape[1])
+ size = yy, xx
+ data1 = self.__createMarginImage(raw1, size)
+ data2 = self.__createMarginImage(raw2, size)
+ self.__matching_keypoints = [0.0], [0.0], [1.0]
+ try:
+ data1, data2 = self.__createSiftData(data1, data2)
+ if data2 is None:
+ raise ValueError("Unexpected None value")
+ except Exception as e:
+ # TODO: Display it on the GUI
+ _logger.error(e)
+ self.__setDefaultAlignmentMode()
+ return
+ else:
+ assert False
+
+ self.__item.setImageData1(data1)
+ self.__item.setImageData2(data2)
mode = self.getVisualizationMode()
if mode == VisualizationMode.COMPOSITE_RED_BLUE_GRAY_NEG:
- data1 = self.__composeImage(data1, data2, mode)
- data2 = numpy.empty((0, 0))
+ data1 = self.__composeRgbImage(data1, data2, mode)
+ data2 = None
elif mode == VisualizationMode.COMPOSITE_RED_BLUE_GRAY:
- data1 = self.__composeImage(data1, data2, mode)
- data2 = numpy.empty((0, 0))
+ data1 = self.__composeRgbImage(data1, data2, mode)
+ data2 = None
elif mode == VisualizationMode.COMPOSITE_A_MINUS_B:
- data1 = self.__composeImage(data1, data2, mode)
- data2 = numpy.empty((0, 0))
+ data1 = self.__composeAMinusBImage(data1, data2)
+ data2 = None
elif mode == VisualizationMode.ONLY_A:
- data2 = numpy.empty((0, 0))
+ data2 = None
elif mode == VisualizationMode.ONLY_B:
data1 = numpy.empty((0, 0))
self.__data1, self.__data2 = data1, data2
- self.__plot.addImage(data1, z=0, legend="image1", resetzoom=False)
- self.__plot.addImage(data2, z=0, legend="image2", resetzoom=False)
+
+ colormap = self.__getSealedColormap()
+ mode1 = self.__getImageMode(self.__data1)
+ if mode1 == "intensity":
+ colormap1 = colormap
+ else:
+ colormap1 = None
+ self.__plot.addImage(
+ data1, z=0, legend="image1", resetzoom=False, colormap=colormap1
+ )
self.__image1 = self.__plot.getImage("image1")
- self.__image2 = self.__plot.getImage("image2")
+
+ if data2 is not None:
+ mode2 = self.__getImageMode(data2)
+ if mode2 == "intensity":
+ colormap2 = colormap
+ else:
+ colormap2 = None
+ self.__plot.addImage(
+ data2, z=0, legend="image2", resetzoom=False, colormap=colormap2
+ )
+ self.__image2 = self.__plot.getImage("image2")
+ self.__image2.setVisible(True)
+ else:
+ if self.__image2 is not None:
+ self.__image2.setVisible(False)
+ self.__image2 = None
+ self.__data2 = numpy.empty((0, 0))
self.__updateKeyPoints()
# Set the separator into the middle
@@ -1004,27 +654,6 @@ class CompareImages(qt.QMainWindow):
value = self.__data1.shape[0] // 2
self.__hline.setPosition(0, value)
self.__updateSeparators()
- if updateColormap:
- self.__updateColormap()
-
- def __updateColormap(self):
- # TODO: The colormap histogram will still be wrong
- mode1 = self.__getImageMode(self.__data1)
- mode2 = self.__getImageMode(self.__data2)
- if mode1 == "intensity" and mode1 == mode2:
- if self.__data1.size == 0:
- vmin = self.__data2.min()
- vmax = self.__data2.max()
- elif self.__data2.size == 0:
- vmin = self.__data1.min()
- vmax = self.__data1.max()
- else:
- vmin = min(self.__data1.min(), self.__data2.min())
- vmax = max(self.__data1.max(), self.__data2.max())
- colormap = self.getColormap()
- colormap.setVRange(vmin=vmin, vmax=vmax)
- self.__image1.setColormap(colormap)
- self.__image2.setColormap(colormap)
def __getImageMode(self, image):
"""Returns a value identifying the way the image is stored in the
@@ -1060,62 +689,117 @@ class CompareImages(qt.QMainWindow):
data[:, :, c] = self.__rescaleArray(image[:, :, c], shape)
return data
- def __composeImage(self, data1, data2, mode):
+ def __composeRgbImage(self, data1, data2, mode):
"""Returns an RBG image containing composition of data1 and data2 in 2
different channels
+ A data image of a size of 0 is considered as missing. This does not
+ interrupt the processing.
+
:param numpy.ndarray data1: First image
:param numpy.ndarray data1: Second image
:param VisualizationMode mode: Composition mode.
:rtype: numpy.ndarray
"""
- assert(data1.shape[0:2] == data2.shape[0:2])
- if mode == VisualizationMode.COMPOSITE_A_MINUS_B:
- # TODO: this calculation has no interest of generating a 'composed'
- # rgb image, this could be moved in an other function or doc
- # should be modified
- _type = data1.dtype
- result = data1.astype(numpy.float64) - data2.astype(numpy.float64)
- return result
- mode1 = self.__getImageMode(data1)
- if mode1 in ["rgb", "rgba"]:
- intensity1 = self.__luminosityImage(data1)
- vmin1, vmax1 = 0.0, 1.0
+ if data1.size != 0 and data2.size != 0:
+ assert data1.shape[0:2] == data2.shape[0:2]
+
+ sealed = self.__getSealedColormap()
+ vmin, vmax = sealed.getVRange()
+
+ if data1.size == 0:
+ intensity1 = numpy.zeros(data2.shape[0:2])
else:
- intensity1 = data1
- vmin1, vmax1 = data1.min(), data1.max()
+ mode1 = self.__getImageMode(data1)
+ if mode1 in ["rgb", "rgba"]:
+ intensity1 = self.__luminosityImage(data1)
+ else:
+ intensity1 = data1
- mode2 = self.__getImageMode(data2)
- if mode2 in ["rgb", "rgba"]:
- intensity2 = self.__luminosityImage(data2)
- vmin2, vmax2 = 0.0, 1.0
+ if data2.size == 0:
+ intensity2 = numpy.zeros(data1.shape[0:2])
else:
- intensity2 = data2
- vmin2, vmax2 = data2.min(), data2.max()
+ mode2 = self.__getImageMode(data2)
+ if mode2 in ["rgb", "rgba"]:
+ intensity2 = self.__luminosityImage(data2)
+ else:
+ intensity2 = data2
- vmin, vmax = min(vmin1, vmin2) * 1.0, max(vmax1, vmax2) * 1.0
- shape = data1.shape
+ shape = intensity1.shape
result = numpy.empty((shape[0], shape[1], 3), dtype=numpy.uint8)
- a = (intensity1 - vmin) * (1.0 / (vmax - vmin)) * 255.0
- b = (intensity2 - vmin) * (1.0 / (vmax - vmin)) * 255.0
+ a, _, _ = normalize(
+ intensity1,
+ norm=sealed.getNormalization(),
+ autoscale=sealed.getAutoscaleMode(),
+ vmin=sealed.getVMin(),
+ vmax=sealed.getVMax(),
+ gamma=sealed.getGammaNormalizationParameter(),
+ )
+ b, _, _ = normalize(
+ intensity2,
+ norm=sealed.getNormalization(),
+ autoscale=sealed.getAutoscaleMode(),
+ vmin=sealed.getVMin(),
+ vmax=sealed.getVMax(),
+ gamma=sealed.getGammaNormalizationParameter(),
+ )
if mode == VisualizationMode.COMPOSITE_RED_BLUE_GRAY:
result[:, :, 0] = a
- result[:, :, 1] = (a + b) / 2
+ result[:, :, 1] = a // 2 + b // 2
result[:, :, 2] = b
elif mode == VisualizationMode.COMPOSITE_RED_BLUE_GRAY_NEG:
result[:, :, 0] = 255 - b
- result[:, :, 1] = 255 - (a + b) / 2
+ result[:, :, 1] = 255 - (a // 2 + b // 2)
result[:, :, 2] = 255 - a
return result
- def __luminosityImage(self, image):
+ def __composeAMinusBImage(self, data1, data2):
+ """Returns an intensity image containing the composition of `A-B`.
+
+ A data image of a size of 0 is considered as missing. This does not
+ interrupt the processing.
+
+ :param numpy.ndarray data1: First image
+ :param numpy.ndarray data1: Second image
+ :rtype: numpy.ndarray
+ """
+ if data1.size != 0 and data2.size != 0:
+ assert data1.shape[0:2] == data2.shape[0:2]
+
+ data1 = self.__asIntensityImage(data1)
+ data2 = self.__asIntensityImage(data2)
+ if data1.size == 0:
+ result = data2
+ elif data2.size == 0:
+ result = data1
+ else:
+ result = data1.astype(numpy.float32) - data2.astype(numpy.float32)
+ return result
+
+ def __asIntensityImage(self, image: numpy.ndarray):
+ """Returns an intensity image.
+
+ If the image use a single channel, it will be returned as it is.
+
+ If the image is an RBG(A) image, the luminosity (0..1) is extracted and
+ returned. The alpha channel is ignored.
+
+ :rtype: numpy.ndarray
+ """
+ mode = self.__getImageMode(image)
+ if mode in ["rgb", "rgba"]:
+ return self.__luminosityImage(image)
+ return image
+
+ def __luminosityImage(self, image: numpy.ndarray):
"""Returns the luminosity channel from an RBG(A) image.
+
The alpha channel is ignored.
:rtype: numpy.ndarray
"""
mode = self.__getImageMode(image)
- assert(mode in ["rgb", "rgba"])
+ assert mode in ["rgb", "rgba"]
is_uint8 = image.dtype.type == numpy.uint8
# luminosity
image = 0.21 * image[..., 0] + 0.72 * image[..., 1] + 0.07 * image[..., 2]
@@ -1128,8 +812,10 @@ class CompareImages(qt.QMainWindow):
:rtype: numpy.ndarray
"""
- y, x = numpy.ogrid[:shape[0], :shape[1]]
- y, x = y * 1.0 * (image.shape[0] - 1) / (shape[0] - 1), x * 1.0 * (image.shape[1] - 1) / (shape[1] - 1)
+ y, x = numpy.ogrid[: shape[0], : shape[1]]
+ y, x = y * 1.0 * (image.shape[0] - 1) / (shape[0] - 1), x * 1.0 * (
+ image.shape[1] - 1
+ ) / (shape[1] - 1)
b = silx.image.bilinear.BilinearImage(image)
# TODO: could be optimized using strides
x2d = numpy.zeros_like(y) + x
@@ -1142,8 +828,8 @@ class CompareImages(qt.QMainWindow):
:rtype: numpy.ndarray
"""
- assert(image.shape[0] <= size[0])
- assert(image.shape[1] <= size[1])
+ assert image.shape[0] <= size[0]
+ assert image.shape[1] <= size[1]
if image.shape == size:
return image
mode = self.__getImageMode(image)
@@ -1156,7 +842,7 @@ class CompareImages(qt.QMainWindow):
if mode == "intensity":
data = numpy.zeros(size, dtype=image.dtype)
- data[pos0:pos0 + image.shape[0], pos1:pos1 + image.shape[1]] = image
+ data[pos0 : pos0 + image.shape[0], pos1 : pos1 + image.shape[1]] = image
# TODO: It is maybe possible to put NaN on the margin
else:
if transparent:
@@ -1164,9 +850,13 @@ class CompareImages(qt.QMainWindow):
else:
data = numpy.zeros((size[0], size[1], 3), dtype=numpy.uint8)
depth = min(data.shape[2], image.shape[2])
- data[pos0:pos0 + image.shape[0], pos1:pos1 + image.shape[1], 0:depth] = image[:, :, 0:depth]
+ data[
+ pos0 : pos0 + image.shape[0], pos1 : pos1 + image.shape[1], 0:depth
+ ] = image[:, :, 0:depth]
if transparent and depth == 3:
- data[pos0:pos0 + image.shape[0], pos1:pos1 + image.shape[1], 3] = 255
+ data[
+ pos0 : pos0 + image.shape[0], pos1 : pos1 + image.shape[1], 3
+ ] = 255
return data
def __toAffineTransformation(self, sift_result):
@@ -1190,7 +880,7 @@ class CompareImages(qt.QMainWindow):
return AffineTransformation(tx, ty, sx, sy, rot)
def getTransformation(self):
- """Retuns the affine transformation applied to the second image to align
+ """Returns the affine transformation applied to the second image to align
it to the first image.
This result is only valid for sift alignment.
@@ -1219,9 +909,11 @@ class CompareImages(qt.QMainWindow):
_logger.info("Number of Keypoints within image 1: %i" % keypoints.size)
_logger.info(" within image 2: %i" % second_keypoints.size)
- self.__matching_keypoints = (match[:].x[:, 0],
- match[:].y[:, 0],
- match[:].scale[:, 0])
+ self.__matching_keypoints = (
+ match[:].x[:, 0],
+ match[:].y[:, 0],
+ match[:].scale[:, 0],
+ )
matching_keypoints = match.shape[0]
_logger.info("Matching keypoints: %i" % matching_keypoints)
if matching_keypoints == 0:
@@ -1241,6 +933,10 @@ class CompareImages(qt.QMainWindow):
self.__transformation = self.__toAffineTransformation(result)
return data1, data2
+ def resetZoom(self, dataMargins=None):
+ """Reset the plot limits to the bounds of the data and redraw the plot."""
+ self.__plot.resetZoom(dataMargins)
+
def setAutoResetZoom(self, activate=True):
"""