From 0a5cb62a6f6f8687f52037f80691e719abe75d81 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 20 Nov 2013 13:51:39 -0500 Subject: [PATCH] ImageItem now has auto downsampling; seems to be working properly. Still need auto clipping as well. --- pyqtgraph/functions.py | 40 ++++++++++++++++++++++++++++ pyqtgraph/graphicsItems/ImageItem.py | 39 ++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 337dfb67..859ff758 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1068,6 +1068,46 @@ def colorToAlpha(data, color): #raise Exception() return np.clip(output, 0, 255).astype(np.ubyte) +def downsample(data, n, axis=0, xvals='subsample'): + """Downsample by averaging points together across axis. + If multiple axes are specified, runs once per axis. + If a metaArray is given, then the axis values can be either subsampled + or downsampled to match. + """ + ma = None + if (hasattr(data, 'implements') and data.implements('MetaArray')): + ma = data + data = data.view(np.ndarray) + + + if hasattr(axis, '__len__'): + if not hasattr(n, '__len__'): + n = [n]*len(axis) + for i in range(len(axis)): + data = downsample(data, n[i], axis[i]) + return data + + nPts = int(data.shape[axis] / n) + s = list(data.shape) + s[axis] = nPts + s.insert(axis+1, n) + sl = [slice(None)] * data.ndim + sl[axis] = slice(0, nPts*n) + d1 = data[tuple(sl)] + #print d1.shape, s + d1.shape = tuple(s) + d2 = d1.mean(axis+1) + + if ma is None: + return d2 + else: + info = ma.infoCopy() + if 'values' in info[axis]: + if xvals == 'subsample': + info[axis]['values'] = info[axis]['values'][::n][:nPts] + elif xvals == 'downsample': + info[axis]['values'] = downsample(info[axis]['values'], n) + return MetaArray(d2, info=info) def arrayToQPath(x, y, connect='all'): diff --git a/pyqtgraph/graphicsItems/ImageItem.py b/pyqtgraph/graphicsItems/ImageItem.py index 530db7fb..47250cf2 100644 --- a/pyqtgraph/graphicsItems/ImageItem.py +++ b/pyqtgraph/graphicsItems/ImageItem.py @@ -1,3 +1,4 @@ +import pyqtgraph as pg from pyqtgraph.Qt import QtGui, QtCore import numpy as np import collections @@ -44,6 +45,7 @@ class ImageItem(GraphicsObject): self.levels = None ## [min, max] or [[redMin, redMax], ...] self.lut = None + self.autoDownsample = False #self.clipLevel = None self.drawKernel = None @@ -140,6 +142,11 @@ class ImageItem(GraphicsObject): if update: self.updateImage() + def setAutoDownsample(self, ads): + self.autoDownsample = ads + self.qimage = None + self.update() + def setOpts(self, update=True, **kargs): if 'lut' in kargs: self.setLookupTable(kargs['lut'], update=update) @@ -156,6 +163,10 @@ class ImageItem(GraphicsObject): if 'removable' in kargs: self.removable = kargs['removable'] self.menu = None + if 'autoDownsample' in kargs: + self.setAutoDownsample(kargs['autoDownsample']) + if update: + self.update() def setRect(self, rect): """Scale and translate the image to fit within rect (must be a QRect or QRectF).""" @@ -198,6 +209,9 @@ class ImageItem(GraphicsObject): gotNewData = True shapeChanged = (self.image is None or image.shape != self.image.shape) self.image = image.view(np.ndarray) + if self.image.shape[0] > 2**15-1 or self.image.shape[1] > 2**15-1: + if 'autoDownsample' not in kargs: + kargs['autoDownsample'] = True if shapeChanged: self.prepareGeometryChange() self.informViewBoundsChanged() @@ -259,8 +273,22 @@ class ImageItem(GraphicsObject): lut = self.lut #print lut.shape #print self.lut - - argb, alpha = fn.makeARGB(self.image, lut=lut, levels=self.levels) + if self.autoDownsample: + # reduce dimensions of image based on screen resolution + o = self.mapToDevice(QtCore.QPointF(0,0)) + x = self.mapToDevice(QtCore.QPointF(1,0)) + y = self.mapToDevice(QtCore.QPointF(0,1)) + w = pg.Point(x-o).length() + h = pg.Point(y-o).length() + xds = max(1, int(1/w)) + yds = max(1, int(1/h)) + image = fn.downsample(self.image, xds, axis=0) + image = fn.downsample(image, yds, axis=1) + else: + image = self.image + + + argb, alpha = fn.makeARGB(image, lut=lut, levels=self.levels) self.qimage = fn.makeQImage(argb, alpha) prof.finish() @@ -278,7 +306,7 @@ class ImageItem(GraphicsObject): p.setCompositionMode(self.paintMode) prof.mark('set comp mode') - p.drawImage(QtCore.QPointF(0,0), self.qimage) + p.drawImage(QtCore.QRectF(0,0,self.image.shape[0],self.image.shape[1]), self.qimage) prof.mark('p.drawImage') if self.border is not None: p.setPen(self.border) @@ -327,6 +355,11 @@ class ImageItem(GraphicsObject): if self.image is None: return 1,1 return br.width()/self.width(), br.height()/self.height() + + def viewTransformChanged(self): + if self.autoDownsample: + self.qimage = None + self.update() #def mousePressEvent(self, ev): #if self.drawKernel is not None and ev.button() == QtCore.Qt.LeftButton: