Added features from meganbkratz:

- isocurves
 - array processing through gradientwidget
This commit is contained in:
Luke Campagnola 2012-03-23 13:38:53 -04:00
parent 7e926ba136
commit c814499bee
4 changed files with 127 additions and 5 deletions

View File

@ -187,7 +187,7 @@ class HistogramDetrend(CtrlNode):
"""Removes baseline from data by computing mode (from histogram) of beginning and end of data.""" """Removes baseline from data by computing mode (from histogram) of beginning and end of data."""
nodeName = 'HistogramDetrend' nodeName = 'HistogramDetrend'
uiTemplate = [ uiTemplate = [
('windowSize', 'intSpin', {'value': 500, 'min': 10, 'max': 1000000}), ('windowSize', 'intSpin', {'value': 500, 'min': 10, 'max': 1000000, 'suffix': 'pts'}),
('numBins', 'intSpin', {'value': 50, 'min': 3, 'max': 1000000}) ('numBins', 'intSpin', {'value': 50, 'min': 3, 'max': 1000000})
] ]

View File

@ -409,12 +409,12 @@ def affineSlice(data, shape, origin, vectors, axes, **kargs):
def makeARGB(data, lut=None, levels=None): def makeARGB(data, lut=None, levels=None, useRGBA=False):
""" """
Convert a 2D or 3D array into an ARGB array suitable for building QImages Convert a 2D or 3D array into an ARGB array suitable for building QImages
Will optionally do scaling and/or table lookups to determine final colors. Will optionally do scaling and/or table lookups to determine final colors.
Returns the ARGB array and a boolean indicating whether there is alpha channel data. Returns the ARGB array (values 0-255) and a boolean indicating whether there is alpha channel data.
Arguments: Arguments:
data - 2D or 3D numpy array of int/float types data - 2D or 3D numpy array of int/float types
@ -433,6 +433,8 @@ def makeARGB(data, lut=None, levels=None):
Lookup tables can be built using GradientWidget. Lookup tables can be built using GradientWidget.
levels - List [min, max]; optionally rescale data before converting through the levels - List [min, max]; optionally rescale data before converting through the
lookup table. rescaled = (data-min) * len(lut) / (max-min) lookup table. rescaled = (data-min) * len(lut) / (max-min)
useRGBA - If True, the data is returned in RGBA order. The default is
False, which returns in BGRA order for use with QImage.
""" """
@ -580,8 +582,11 @@ def makeARGB(data, lut=None, levels=None):
prof.mark('4') prof.mark('4')
if useRGBA:
order = [0,1,2,3] ## array comes out RGBA
else:
order = [2,1,0,3] ## for some reason, the colors line up as BGR in the final image.
order = [2,1,0,3] ## for some reason, the colors line up as BGR in the final image.
if data.shape[2] == 1: if data.shape[2] == 1:
for i in xrange(3): for i in xrange(3):
imgData[..., order[i]] = data[..., 0] imgData[..., order[i]] = data[..., 0]
@ -732,6 +737,84 @@ def rescaleData(data, scale, offset):
#return facets #return facets
def isocurve(data, level):
"""
Generate isocurve from 2D data using marching squares algorithm.
*data* 2D numpy array of scalar values
*level* The level at which to generate an isosurface
This function is SLOW; plenty of room for optimization here.
"""
sideTable = [
[],
[0,1],
[1,2],
[0,2],
[0,3],
[1,3],
[0,1,2,3],
[2,3],
[2,3],
[0,1,2,3],
[1,3],
[0,3],
[0,2],
[1,2],
[0,1],
[]
]
edgeKey=[
[(0,1),(0,0)],
[(0,0), (1,0)],
[(1,0), (1,1)],
[(1,1), (0,1)]
]
lines = []
## mark everything below the isosurface level
mask = data < level
### make four sub-fields and compute indexes for grid cells
index = np.zeros([x-1 for x in data.shape], dtype=np.ubyte)
fields = np.empty((2,2), dtype=object)
slices = [slice(0,-1), slice(1,None)]
for i in [0,1]:
for j in [0,1]:
fields[i,j] = mask[slices[i], slices[j]]
#vertIndex = i - 2*j*i + 3*j + 4*k ## this is just to match Bourk's vertex numbering scheme
vertIndex = i+2*j
#print i,j,k," : ", fields[i,j,k], 2**vertIndex
index += fields[i,j] * 2**vertIndex
#print index
#print index
## add lines
for i in xrange(index.shape[0]): # data x-axis
for j in xrange(index.shape[1]): # data y-axis
sides = sideTable[index[i,j]]
for l in range(0, len(sides), 2): ## faces for this grid cell
edges = sides[l:l+2]
pts = []
for m in [0,1]: # points in this face
p1 = edgeKey[edges[m]][0] # p1, p2 are points at either side of an edge
p2 = edgeKey[edges[m]][1]
v1 = data[i+p1[0], j+p1[1]] # v1 and v2 are the values at p1 and p2
v2 = data[i+p2[0], j+p2[1]]
f = (level-v1) / (v2-v1)
fi = 1.0 - f
p = ( ## interpolate between corners
p1[0]*fi + p2[0]*f + i + 0.5,
p1[1]*fi + p2[1]*f + j + 0.5
)
pts.append(p)
lines.append(pts)
return lines ## a list of pairs of points
def isosurface(data, level): def isosurface(data, level):

View File

@ -0,0 +1,38 @@
from GraphicsObject import *
import pyqtgraph.functions as fn
from pyqtgraph.Qt import QtGui
class IsocurveItem(GraphicsObject):
"""
Item displaying an isocurve of a 2D array.
To align this item correctly with an ImageItem,
call isocurve.setParentItem(image)
"""
def __init__(self, data, level, pen='w'):
GraphicsObject.__init__(self)
lines = fn.isocurve(data, level)
self.path = QtGui.QPainterPath()
self.setPen(pen)
for line in lines:
self.path.moveTo(*line[0])
self.path.lineTo(*line[1])
def setPen(self, *args, **kwargs):
self.pen = fn.mkPen(*args, **kwargs)
self.update()
def boundingRect(self):
return self.path.boundingRect()
def paint(self, p, *args):
p.setPen(self.pen)
p.drawPath(self.path)

View File

@ -55,6 +55,7 @@ class GradientWidget(GraphicsView):
self.setMaximumHeight(16777215) self.setMaximumHeight(16777215)
def __getattr__(self, attr): def __getattr__(self, attr):
### wrap methods from GradientEditorItem
return getattr(self.item, attr) return getattr(self.item, attr)