2012-03-02 02:55:32 +00:00
|
|
|
from pyqtgraph.Qt import QtGui, QtCore
|
|
|
|
import numpy as np
|
2012-05-11 22:05:41 +00:00
|
|
|
import collections
|
2012-03-02 02:55:32 +00:00
|
|
|
try:
|
|
|
|
import scipy.weave as weave
|
|
|
|
from scipy.weave import converters
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
import pyqtgraph.functions as fn
|
|
|
|
import pyqtgraph.debug as debug
|
2012-05-11 22:05:41 +00:00
|
|
|
from .GraphicsObject import GraphicsObject
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
__all__ = ['ImageItem']
|
|
|
|
class ImageItem(GraphicsObject):
|
|
|
|
"""
|
2012-04-16 20:45:55 +00:00
|
|
|
**Bases:** :class:`GraphicsObject <pyqtgraph.GraphicsObject>`
|
2012-03-02 02:55:32 +00:00
|
|
|
|
2012-04-16 20:45:55 +00:00
|
|
|
GraphicsObject displaying an image. Optimized for rapid update (ie video display).
|
|
|
|
This item displays either a 2D numpy array (height, width) or
|
|
|
|
a 3D array (height, width, RGBa). This array is optionally scaled (see
|
|
|
|
:func:`setLevels <pyqtgraph.ImageItem.setLevels>`) and/or colored
|
|
|
|
with a lookup table (see :func:`setLookupTable <pyqtgraph.ImageItem.setLookupTable>`)
|
|
|
|
before being displayed.
|
|
|
|
|
|
|
|
ImageItem is frequently used in conjunction with
|
|
|
|
:class:`HistogramLUTItem <pyqtgraph.HistogramLUTItem>` or
|
|
|
|
:class:`HistogramLUTWidget <pyqtgraph.HistogramLUTWidget>` to provide a GUI
|
|
|
|
for controlling the levels and lookup table used to display the image.
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
sigImageChanged = QtCore.Signal()
|
|
|
|
|
|
|
|
## performance gains from this are marginal, and it's rather unreliable.
|
|
|
|
useWeave = False
|
|
|
|
|
|
|
|
def __init__(self, image=None, **kargs):
|
|
|
|
"""
|
2012-04-16 20:45:55 +00:00
|
|
|
See :func:`setImage <pyqtgraph.ImageItem.setImage>` for all allowed initialization arguments.
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
|
|
|
GraphicsObject.__init__(self)
|
|
|
|
#self.pixmapItem = QtGui.QGraphicsPixmapItem(self)
|
|
|
|
#self.qimage = QtGui.QImage()
|
|
|
|
#self._pixmap = None
|
|
|
|
|
|
|
|
self.image = None ## original image data
|
|
|
|
self.qimage = None ## rendered image for display
|
|
|
|
#self.clipMask = None
|
|
|
|
|
|
|
|
self.paintMode = None
|
|
|
|
#self.useWeave = True
|
|
|
|
|
|
|
|
self.levels = None ## [min, max] or [[redMin, redMax], ...]
|
|
|
|
self.lut = None
|
|
|
|
|
|
|
|
#self.clipLevel = None
|
|
|
|
self.drawKernel = None
|
|
|
|
self.border = None
|
|
|
|
|
|
|
|
if image is not None:
|
|
|
|
self.setImage(image, **kargs)
|
|
|
|
else:
|
|
|
|
self.setOpts(**kargs)
|
|
|
|
|
|
|
|
def setCompositionMode(self, mode):
|
2012-04-16 20:45:55 +00:00
|
|
|
"""Change the composition mode of the item (see QPainter::CompositionMode
|
|
|
|
in the Qt documentation). This is useful when overlaying multiple ImageItems.
|
|
|
|
|
|
|
|
============================================ ============================================================
|
|
|
|
**Most common arguments:**
|
|
|
|
QtGui.QPainter.CompositionMode_SourceOver Default; image replaces the background if it
|
|
|
|
is opaque. Otherwise, it uses the alpha channel to blend
|
|
|
|
the image with the background.
|
|
|
|
QtGui.QPainter.CompositionMode_Overlay The image color is mixed with the background color to
|
|
|
|
reflect the lightness or darkness of the background.
|
|
|
|
QtGui.QPainter.CompositionMode_Plus Both the alpha and color of the image and background pixels
|
|
|
|
are added together.
|
|
|
|
QtGui.QPainter.CompositionMode_Multiply The output is the image color multiplied by the background.
|
|
|
|
============================================ ============================================================
|
|
|
|
"""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.paintMode = mode
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
## use setOpacity instead.
|
|
|
|
#def setAlpha(self, alpha):
|
|
|
|
#self.setOpacity(alpha)
|
|
|
|
#self.updateImage()
|
|
|
|
|
|
|
|
def setBorder(self, b):
|
|
|
|
self.border = fn.mkPen(b)
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
def width(self):
|
|
|
|
if self.image is None:
|
|
|
|
return None
|
|
|
|
return self.image.shape[0]
|
|
|
|
|
|
|
|
def height(self):
|
|
|
|
if self.image is None:
|
|
|
|
return None
|
|
|
|
return self.image.shape[1]
|
|
|
|
|
|
|
|
def boundingRect(self):
|
|
|
|
if self.image is None:
|
|
|
|
return QtCore.QRectF(0., 0., 0., 0.)
|
|
|
|
return QtCore.QRectF(0., 0., float(self.width()), float(self.height()))
|
|
|
|
|
|
|
|
#def setClipLevel(self, level=None):
|
|
|
|
#self.clipLevel = level
|
|
|
|
#self.updateImage()
|
|
|
|
|
|
|
|
#def paint(self, p, opt, widget):
|
|
|
|
#pass
|
|
|
|
#if self.pixmap is not None:
|
|
|
|
#p.drawPixmap(0, 0, self.pixmap)
|
|
|
|
#print "paint"
|
|
|
|
|
|
|
|
def setLevels(self, levels, update=True):
|
|
|
|
"""
|
2012-04-16 20:45:55 +00:00
|
|
|
Set image scaling levels. Can be one of:
|
|
|
|
|
|
|
|
* [blackLevel, whiteLevel]
|
|
|
|
* [[minRed, maxRed], [minGreen, maxGreen], [minBlue, maxBlue]]
|
|
|
|
|
|
|
|
Only the first format is compatible with lookup tables. See :func:`makeARGB <pyqtgraph.makeARGB>`
|
|
|
|
for more details on how levels are applied.
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
|
|
|
self.levels = levels
|
|
|
|
if update:
|
|
|
|
self.updateImage()
|
|
|
|
|
|
|
|
def getLevels(self):
|
|
|
|
return self.levels
|
|
|
|
#return self.whiteLevel, self.blackLevel
|
|
|
|
|
|
|
|
def setLookupTable(self, lut, update=True):
|
|
|
|
"""
|
2012-04-16 20:45:55 +00:00
|
|
|
Set the lookup table (numpy array) to use for this image. (see
|
|
|
|
:func:`makeARGB <pyqtgraph.makeARGB>` for more information on how this is used).
|
|
|
|
Optionally, lut can be a callable that accepts the current image as an
|
|
|
|
argument and returns the lookup table to use.
|
|
|
|
|
|
|
|
Ordinarily, this table is supplied by a :class:`HistogramLUTItem <pyqtgraph.HistogramLUTItem>`
|
|
|
|
or :class:`GradientEditorItem <pyqtgraph.GradientEditorItem>`.
|
|
|
|
"""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.lut = lut
|
|
|
|
if update:
|
|
|
|
self.updateImage()
|
|
|
|
|
|
|
|
def setOpts(self, update=True, **kargs):
|
|
|
|
if 'lut' in kargs:
|
|
|
|
self.setLookupTable(kargs['lut'], update=update)
|
|
|
|
if 'levels' in kargs:
|
|
|
|
self.setLevels(kargs['levels'], update=update)
|
|
|
|
#if 'clipLevel' in kargs:
|
|
|
|
#self.setClipLevel(kargs['clipLevel'])
|
|
|
|
if 'opacity' in kargs:
|
|
|
|
self.setOpacity(kargs['opacity'])
|
|
|
|
if 'compositionMode' in kargs:
|
|
|
|
self.setCompositionMode(kargs['compositionMode'])
|
|
|
|
if 'border' in kargs:
|
|
|
|
self.setBorder(kargs['border'])
|
|
|
|
|
|
|
|
def setRect(self, rect):
|
2012-04-16 20:45:55 +00:00
|
|
|
"""Scale and translate the image to fit within rect (must be a QRect or QRectF)."""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.resetTransform()
|
|
|
|
self.translate(rect.left(), rect.top())
|
2012-03-17 16:10:51 +00:00
|
|
|
self.scale(rect.width() / self.width(), rect.height() / self.height())
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
def setImage(self, image=None, autoLevels=None, **kargs):
|
|
|
|
"""
|
2012-04-16 20:45:55 +00:00
|
|
|
Update the image displayed by this item. For more information on how the image
|
|
|
|
is processed before displaying, see :func:`makeARGB <pyqtgraph.makeARGB>`
|
2012-03-02 02:55:32 +00:00
|
|
|
|
2012-04-16 20:45:55 +00:00
|
|
|
================= =========================================================================
|
|
|
|
**Arguments:**
|
|
|
|
image (numpy array) Specifies the image data. May be 2D (width, height) or
|
|
|
|
3D (width, height, RGBa). The array dtype must be integer or floating
|
|
|
|
point of any bit depth. For 3D arrays, the third dimension must
|
|
|
|
be of length 3 (RGB) or 4 (RGBA).
|
|
|
|
autoLevels (bool) If True, this forces the image to automatically select
|
|
|
|
levels based on the maximum and minimum values in the data.
|
|
|
|
By default, this argument is true unless the levels argument is
|
|
|
|
given.
|
|
|
|
lut (numpy array) The color lookup table to use when displaying the image.
|
|
|
|
See :func:`setLookupTable <pyqtgraph.ImageItem.setLookupTable>`.
|
|
|
|
levels (min, max) The minimum and maximum values to use when rescaling the image
|
|
|
|
data. By default, this will be set to the minimum and maximum values
|
|
|
|
in the image. If the image array has dtype uint8, no rescaling is necessary.
|
|
|
|
opacity (float 0.0-1.0)
|
|
|
|
compositionMode see :func:`setCompositionMode <pyqtgraph.ImageItem.setCompositionMode>`
|
|
|
|
border Sets the pen used when drawing the image border. Default is None.
|
|
|
|
================= =========================================================================
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
|
|
|
prof = debug.Profiler('ImageItem.setImage', disabled=True)
|
|
|
|
|
|
|
|
gotNewData = False
|
|
|
|
if image is None:
|
|
|
|
if self.image is None:
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
gotNewData = True
|
|
|
|
if self.image is None or image.shape != self.image.shape:
|
|
|
|
self.prepareGeometryChange()
|
|
|
|
self.image = image.view(np.ndarray)
|
|
|
|
|
|
|
|
prof.mark('1')
|
|
|
|
|
|
|
|
if autoLevels is None:
|
|
|
|
if 'levels' in kargs:
|
|
|
|
autoLevels = False
|
|
|
|
else:
|
|
|
|
autoLevels = True
|
|
|
|
if autoLevels:
|
|
|
|
img = self.image
|
|
|
|
while img.size > 2**16:
|
|
|
|
img = img[::2, ::2]
|
|
|
|
mn, mx = img.min(), img.max()
|
|
|
|
if mn == mx:
|
|
|
|
mn = 0
|
|
|
|
mx = 255
|
|
|
|
kargs['levels'] = [mn,mx]
|
|
|
|
prof.mark('2')
|
|
|
|
|
|
|
|
self.setOpts(update=False, **kargs)
|
|
|
|
prof.mark('3')
|
|
|
|
|
|
|
|
self.qimage = None
|
|
|
|
self.update()
|
|
|
|
prof.mark('4')
|
|
|
|
|
|
|
|
if gotNewData:
|
|
|
|
self.sigImageChanged.emit()
|
|
|
|
|
|
|
|
|
|
|
|
prof.finish()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def updateImage(self, *args, **kargs):
|
|
|
|
## used for re-rendering qimage from self.image.
|
|
|
|
|
|
|
|
## can we make any assumptions here that speed things up?
|
|
|
|
## dtype, range, size are all the same?
|
|
|
|
defaults = {
|
|
|
|
'autoLevels': False,
|
|
|
|
}
|
|
|
|
defaults.update(kargs)
|
|
|
|
return self.setImage(*args, **defaults)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render(self):
|
|
|
|
prof = debug.Profiler('ImageItem.render', disabled=True)
|
|
|
|
if self.image is None:
|
|
|
|
return
|
2012-05-11 22:05:41 +00:00
|
|
|
if isinstance(self.lut, collections.Callable):
|
2012-03-02 02:55:32 +00:00
|
|
|
lut = self.lut(self.image)
|
|
|
|
else:
|
|
|
|
lut = self.lut
|
2012-04-25 17:12:40 +00:00
|
|
|
#print lut.shape
|
|
|
|
#print self.lut
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
argb, alpha = fn.makeARGB(self.image, lut=lut, levels=self.levels)
|
|
|
|
self.qimage = fn.makeQImage(argb, alpha)
|
|
|
|
#self.pixmap = QtGui.QPixmap.fromImage(self.qimage)
|
|
|
|
prof.finish()
|
|
|
|
|
|
|
|
|
|
|
|
def paint(self, p, *args):
|
|
|
|
prof = debug.Profiler('ImageItem.paint', disabled=True)
|
|
|
|
if self.image is None:
|
|
|
|
return
|
|
|
|
if self.qimage is None:
|
|
|
|
self.render()
|
2012-04-04 13:29:35 +00:00
|
|
|
prof.mark('render QImage')
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.paintMode is not None:
|
|
|
|
p.setCompositionMode(self.paintMode)
|
2012-04-04 13:29:35 +00:00
|
|
|
prof.mark('set comp mode')
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
p.drawImage(QtCore.QPointF(0,0), self.qimage)
|
2012-04-04 13:29:35 +00:00
|
|
|
prof.mark('p.drawImage')
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.border is not None:
|
|
|
|
p.setPen(self.border)
|
|
|
|
p.drawRect(self.boundingRect())
|
|
|
|
prof.finish()
|
|
|
|
|
|
|
|
|
|
|
|
def getHistogram(self, bins=500, step=3):
|
2012-04-16 20:45:55 +00:00
|
|
|
"""Returns x and y arrays containing the histogram values for the current image.
|
|
|
|
The step argument causes pixels to be skipped when computing the histogram to save time.
|
|
|
|
This method is also used when automatically computing levels.
|
|
|
|
"""
|
2012-03-02 02:55:32 +00:00
|
|
|
if self.image is None:
|
|
|
|
return None,None
|
|
|
|
stepData = self.image[::step, ::step]
|
|
|
|
hist = np.histogram(stepData, bins=bins)
|
|
|
|
return hist[1][:-1], hist[0]
|
|
|
|
|
|
|
|
def setPxMode(self, b):
|
2012-04-16 20:45:55 +00:00
|
|
|
"""
|
|
|
|
Set whether the item ignores transformations and draws directly to screen pixels.
|
|
|
|
If True, the item will not inherit any scale or rotation transformations from its
|
|
|
|
parent items, but its position will be transformed as usual.
|
|
|
|
(see GraphicsItem::ItemIgnoresTransformations in the Qt documentation)
|
|
|
|
"""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.setFlag(self.ItemIgnoresTransformations, b)
|
|
|
|
|
|
|
|
def setScaledMode(self):
|
|
|
|
self.setPxMode(False)
|
|
|
|
|
|
|
|
def getPixmap(self):
|
|
|
|
if self.qimage is None:
|
|
|
|
self.render()
|
|
|
|
if self.qimage is None:
|
|
|
|
return None
|
|
|
|
return QtGui.QPixmap.fromImage(self.qimage)
|
|
|
|
|
|
|
|
def pixelSize(self):
|
|
|
|
"""return scene-size of a single pixel in the image"""
|
|
|
|
br = self.sceneBoundingRect()
|
|
|
|
if self.image is None:
|
|
|
|
return 1,1
|
|
|
|
return br.width()/self.width(), br.height()/self.height()
|
|
|
|
|
|
|
|
def mousePressEvent(self, ev):
|
|
|
|
if self.drawKernel is not None and ev.button() == QtCore.Qt.LeftButton:
|
|
|
|
self.drawAt(ev.pos(), ev)
|
|
|
|
ev.accept()
|
|
|
|
else:
|
|
|
|
ev.ignore()
|
|
|
|
|
|
|
|
def mouseMoveEvent(self, ev):
|
|
|
|
#print "mouse move", ev.pos()
|
|
|
|
if self.drawKernel is not None:
|
|
|
|
self.drawAt(ev.pos(), ev)
|
|
|
|
|
|
|
|
def mouseReleaseEvent(self, ev):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def tabletEvent(self, ev):
|
2012-05-11 22:05:41 +00:00
|
|
|
print(ev.device())
|
|
|
|
print(ev.pointerType())
|
|
|
|
print(ev.pressure())
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
def drawAt(self, pos, ev=None):
|
|
|
|
pos = [int(pos.x()), int(pos.y())]
|
|
|
|
dk = self.drawKernel
|
|
|
|
kc = self.drawKernelCenter
|
|
|
|
sx = [0,dk.shape[0]]
|
|
|
|
sy = [0,dk.shape[1]]
|
|
|
|
tx = [pos[0] - kc[0], pos[0] - kc[0]+ dk.shape[0]]
|
|
|
|
ty = [pos[1] - kc[1], pos[1] - kc[1]+ dk.shape[1]]
|
|
|
|
|
|
|
|
for i in [0,1]:
|
|
|
|
dx1 = -min(0, tx[i])
|
|
|
|
dx2 = min(0, self.image.shape[0]-tx[i])
|
|
|
|
tx[i] += dx1+dx2
|
|
|
|
sx[i] += dx1+dx2
|
|
|
|
|
|
|
|
dy1 = -min(0, ty[i])
|
|
|
|
dy2 = min(0, self.image.shape[1]-ty[i])
|
|
|
|
ty[i] += dy1+dy2
|
|
|
|
sy[i] += dy1+dy2
|
|
|
|
|
|
|
|
#print sx
|
|
|
|
#print sy
|
|
|
|
#print tx
|
|
|
|
#print ty
|
|
|
|
#print self.image.shape
|
|
|
|
#print self.image[tx[0]:tx[1], ty[0]:ty[1]].shape
|
|
|
|
#print dk[sx[0]:sx[1], sy[0]:sy[1]].shape
|
|
|
|
ts = (slice(tx[0],tx[1]), slice(ty[0],ty[1]))
|
|
|
|
ss = (slice(sx[0],sx[1]), slice(sy[0],sy[1]))
|
|
|
|
#src = dk[sx[0]:sx[1], sy[0]:sy[1]]
|
|
|
|
#mask = self.drawMask[sx[0]:sx[1], sy[0]:sy[1]]
|
|
|
|
mask = self.drawMask
|
|
|
|
src = dk
|
|
|
|
#print self.image[ts].shape, src.shape
|
|
|
|
|
2012-05-11 22:05:41 +00:00
|
|
|
if isinstance(self.drawMode, collections.Callable):
|
2012-03-02 02:55:32 +00:00
|
|
|
self.drawMode(dk, self.image, mask, ss, ts, ev)
|
|
|
|
else:
|
|
|
|
src = src[ss]
|
|
|
|
if self.drawMode == 'set':
|
|
|
|
if mask is not None:
|
|
|
|
mask = mask[ss]
|
|
|
|
self.image[ts] = self.image[ts] * (1-mask) + src * mask
|
|
|
|
else:
|
|
|
|
self.image[ts] = src
|
|
|
|
elif self.drawMode == 'add':
|
|
|
|
self.image[ts] += src
|
|
|
|
else:
|
|
|
|
raise Exception("Unknown draw mode '%s'" % self.drawMode)
|
|
|
|
self.updateImage()
|
|
|
|
|
|
|
|
def setDrawKernel(self, kernel=None, mask=None, center=(0,0), mode='set'):
|
|
|
|
self.drawKernel = kernel
|
|
|
|
self.drawKernelCenter = center
|
|
|
|
self.drawMode = mode
|
|
|
|
self.drawMask = mask
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#def setImage(self, image=None, copy=True, autoRange=True, clipMask=None, white=None, black=None, axes=None):
|
|
|
|
#prof = debug.Profiler('ImageItem.updateImage 0x%x' %id(self), disabled=True)
|
|
|
|
##debug.printTrace()
|
|
|
|
#if axes is None:
|
|
|
|
#axh = {'x': 0, 'y': 1, 'c': 2}
|
|
|
|
#else:
|
|
|
|
#axh = axes
|
|
|
|
##print "Update image", black, white
|
|
|
|
#if white is not None:
|
|
|
|
#self.whiteLevel = white
|
|
|
|
#if black is not None:
|
|
|
|
#self.blackLevel = black
|
|
|
|
|
|
|
|
#gotNewData = False
|
|
|
|
#if image is None:
|
|
|
|
#if self.image is None:
|
|
|
|
#return
|
|
|
|
#else:
|
|
|
|
#gotNewData = True
|
|
|
|
#if self.image is None or image.shape != self.image.shape:
|
|
|
|
#self.prepareGeometryChange()
|
|
|
|
#if copy:
|
|
|
|
#self.image = image.view(np.ndarray).copy()
|
|
|
|
#else:
|
|
|
|
#self.image = image.view(np.ndarray)
|
|
|
|
##print " image max:", self.image.max(), "min:", self.image.min()
|
|
|
|
#prof.mark('1')
|
|
|
|
|
|
|
|
## Determine scale factors
|
|
|
|
#if autoRange or self.blackLevel is None:
|
|
|
|
#if self.image.dtype is np.ubyte:
|
|
|
|
#self.blackLevel = 0
|
|
|
|
#self.whiteLevel = 255
|
|
|
|
#else:
|
|
|
|
#self.blackLevel = self.image.min()
|
|
|
|
#self.whiteLevel = self.image.max()
|
|
|
|
##print "Image item using", self.blackLevel, self.whiteLevel
|
|
|
|
|
|
|
|
#if self.blackLevel != self.whiteLevel:
|
|
|
|
#scale = 255. / (self.whiteLevel - self.blackLevel)
|
|
|
|
#else:
|
|
|
|
#scale = 0.
|
|
|
|
|
|
|
|
#prof.mark('2')
|
|
|
|
|
|
|
|
### Recolor and convert to 8 bit per channel
|
|
|
|
## Try using weave, then fall back to python
|
|
|
|
#shape = self.image.shape
|
|
|
|
#black = float(self.blackLevel)
|
|
|
|
#white = float(self.whiteLevel)
|
|
|
|
|
|
|
|
#if black == 0 and white == 255 and self.image.dtype == np.ubyte:
|
|
|
|
#im = self.image
|
|
|
|
#elif self.image.dtype in [np.ubyte, np.uint16]:
|
|
|
|
## use lookup table instead
|
|
|
|
#npts = 2**(self.image.itemsize * 8)
|
|
|
|
#lut = self.getLookupTable(npts, black, white)
|
|
|
|
#im = lut[self.image]
|
|
|
|
#else:
|
|
|
|
#im = self.applyColorScaling(self.image, black, scale)
|
|
|
|
|
|
|
|
#prof.mark('3')
|
|
|
|
|
|
|
|
#try:
|
|
|
|
#im1 = np.empty((im.shape[axh['y']], im.shape[axh['x']], 4), dtype=np.ubyte)
|
|
|
|
#except:
|
|
|
|
#print im.shape, axh
|
|
|
|
#raise
|
|
|
|
#alpha = np.clip(int(255 * self.alpha), 0, 255)
|
|
|
|
#prof.mark('4')
|
|
|
|
## Fill image
|
|
|
|
#if im.ndim == 2:
|
|
|
|
#im2 = im.transpose(axh['y'], axh['x'])
|
|
|
|
#im1[..., 0] = im2
|
|
|
|
#im1[..., 1] = im2
|
|
|
|
#im1[..., 2] = im2
|
|
|
|
#im1[..., 3] = alpha
|
|
|
|
#elif im.ndim == 3: #color image
|
|
|
|
#im2 = im.transpose(axh['y'], axh['x'], axh['c'])
|
|
|
|
#if im2.shape[2] > 4:
|
|
|
|
#raise Exception("ImageItem got image with more than 4 color channels (shape is %s; axes are %s)" % (str(im.shape), str(axh)))
|
|
|
|
### [B G R A] Reorder colors
|
|
|
|
#order = [2,1,0,3] ## for some reason, the colors line up as BGR in the final image.
|
|
|
|
|
|
|
|
#for i in range(0, im.shape[axh['c']]):
|
|
|
|
#im1[..., order[i]] = im2[..., i]
|
|
|
|
|
|
|
|
### fill in unused channels with 0 or alpha
|
|
|
|
#for i in range(im.shape[axh['c']], 3):
|
|
|
|
#im1[..., i] = 0
|
|
|
|
#if im.shape[axh['c']] < 4:
|
|
|
|
#im1[..., 3] = alpha
|
|
|
|
|
|
|
|
#else:
|
|
|
|
#raise Exception("Image must be 2 or 3 dimensions")
|
|
|
|
##self.im1 = im1
|
|
|
|
## Display image
|
|
|
|
#prof.mark('5')
|
|
|
|
#if self.clipLevel is not None or clipMask is not None:
|
|
|
|
#if clipMask is not None:
|
|
|
|
#mask = clipMask.transpose()
|
|
|
|
#else:
|
|
|
|
#mask = (self.image < self.clipLevel).transpose()
|
|
|
|
#im1[..., 0][mask] *= 0.5
|
|
|
|
#im1[..., 1][mask] *= 0.5
|
|
|
|
#im1[..., 2][mask] = 255
|
|
|
|
#prof.mark('6')
|
|
|
|
##print "Final image:", im1.dtype, im1.min(), im1.max(), im1.shape
|
|
|
|
##self.ims = im1.tostring() ## Must be held in memory here because qImage won't do it for us :(
|
|
|
|
#prof.mark('7')
|
|
|
|
#try:
|
|
|
|
#buf = im1.data
|
|
|
|
#except AttributeError:
|
|
|
|
#im1 = np.ascontiguousarray(im1)
|
|
|
|
#buf = im1.data
|
|
|
|
|
|
|
|
#qimage = QtGui.QImage(buf, im1.shape[1], im1.shape[0], QtGui.QImage.Format_ARGB32)
|
|
|
|
#self.qimage = qimage
|
|
|
|
#self.qimage.data = im1
|
|
|
|
#self._pixmap = None
|
|
|
|
#prof.mark('8')
|
|
|
|
|
|
|
|
##self.pixmap = QtGui.QPixmap.fromImage(qimage)
|
|
|
|
#prof.mark('9')
|
|
|
|
###del self.ims
|
|
|
|
##self.item.setPixmap(self.pixmap)
|
|
|
|
|
|
|
|
#self.update()
|
|
|
|
#prof.mark('10')
|
|
|
|
|
|
|
|
#if gotNewData:
|
|
|
|
##self.emit(QtCore.SIGNAL('imageChanged'))
|
|
|
|
#self.sigImageChanged.emit()
|
|
|
|
|
|
|
|
#prof.finish()
|
|
|
|
|
|
|
|
#def getLookupTable(self, num, black, white):
|
|
|
|
#num = int(num)
|
|
|
|
#black = int(black)
|
|
|
|
#white = int(white)
|
|
|
|
#if white < black:
|
|
|
|
#b = black
|
|
|
|
#black = white
|
|
|
|
#white = b
|
|
|
|
#key = (num, black, white)
|
|
|
|
#lut = np.empty(num, dtype=np.ubyte)
|
|
|
|
#lut[:black] = 0
|
|
|
|
#rng = lut[black:white]
|
|
|
|
#try:
|
|
|
|
#rng[:] = np.linspace(0, 255, white-black)[:len(rng)]
|
|
|
|
#except:
|
|
|
|
#print key, rng.shape
|
|
|
|
#lut[white:] = 255
|
|
|
|
#return lut
|
|
|
|
|
|
|
|
|
|
|
|
#def applyColorScaling(self, img, offset, scale):
|
|
|
|
#try:
|
|
|
|
#if not ImageItem.useWeave:
|
|
|
|
#raise Exception('Skipping weave compile')
|
|
|
|
##sim = np.ascontiguousarray(self.image) ## should not be needed
|
|
|
|
#sim = img.reshape(img.size)
|
|
|
|
##sim.shape = sim.size
|
|
|
|
#im = np.empty(sim.shape, dtype=np.ubyte)
|
|
|
|
#n = im.size
|
|
|
|
|
|
|
|
#code = """
|
|
|
|
#for( int i=0; i<n; i++ ) {
|
|
|
|
#float a = (sim(i)-offset) * (float)scale;
|
|
|
|
#if( a > 255.0 )
|
|
|
|
#a = 255.0;
|
|
|
|
#else if( a < 0.0 )
|
|
|
|
#a = 0.0;
|
|
|
|
#im(i) = a;
|
|
|
|
#}
|
|
|
|
#"""
|
|
|
|
|
|
|
|
#weave.inline(code, ['sim', 'im', 'n', 'offset', 'scale'], type_converters=converters.blitz, compiler = 'gcc')
|
|
|
|
##sim.shape = shape
|
|
|
|
#im.shape = img.shape
|
|
|
|
#except:
|
|
|
|
#if ImageItem.useWeave:
|
|
|
|
#ImageItem.useWeave = False
|
|
|
|
##sys.excepthook(*sys.exc_info())
|
|
|
|
##print "=============================================================================="
|
|
|
|
##print "Weave compile failed, falling back to slower version."
|
|
|
|
##img.shape = shape
|
|
|
|
#im = ((img - offset) * scale).clip(0.,255.).astype(np.ubyte)
|
|
|
|
#return im
|
|
|
|
|