Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit acd19a1

Browse files
committed
speed up Normalize, LogNorm, colormapping; modified patch by C. Gohlke
svn path=/trunk/matplotlib/; revision=8946
1 parent 358855c commit acd19a1

File tree

3 files changed

+80
-41
lines changed

3 files changed

+80
-41
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2011-02-05 Speed up Normalize and LogNorm by using in-place
2+
operations and by using float32 for float32 inputs
3+
and for ints of 2 bytes or shorter; based on
4+
patch by Christoph Gohlke. - EF
5+
16
2011-02-04 Changed imshow to use rgba as uint8 from start to
27
finish, instead of going through an intermediate
38
step as double precision; thanks to Christoph Gohlke. - EF

examples/pylab_examples/image_slices_viewer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import numpy
2-
from pylab import figure, show
2+
from matplotlib.pyplot import figure, show
33

44

55

66

77
class IndexTracker:
88
def __init__(self, ax, X):
99
self.ax = ax
10-
ax.set_title('use scroll wheen to navigate images')
10+
ax.set_title('use scroll wheel to navigate images')
1111

1212
self.X = X
1313
rows,cols,self.slices = X.shape
@@ -28,7 +28,7 @@ def onscroll(self, event):
2828

2929
def update(self):
3030
self.im.set_data(self.X[:,:,self.ind])
31-
ax.set_ylabel('slice %s'%self.ind)
31+
ax.set_ylabel('slice %s'%self.ind)
3232
self.im.axes.figure.canvas.draw()
3333

3434

lib/matplotlib/colors.py

Lines changed: 72 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -509,20 +509,22 @@ def __call__(self, X, alpha=None, bytes=False):
509509
xa = np.array([X])
510510
else:
511511
vtype = 'array'
512-
# force a copy here -- the ma.array and filled functions
513-
# do force a cop of the data by default - JDH
514-
xma = ma.array(X, copy=True)
515-
xa = xma.filled(0)
516-
mask_bad = ma.getmask(xma)
512+
xma = ma.array(X, copy=False)
513+
mask_bad = xma.mask
514+
xa = xma.data.copy() # Copy here to avoid side effects.
515+
del xma
516+
# masked values are substituted below; no need to fill them here
517+
517518
if xa.dtype.char in np.typecodes['Float']:
518519
np.putmask(xa, xa==1.0, 0.9999999) #Treat 1.0 as slightly less than 1.
519520
# The following clip is fast, and prevents possible
520521
# conversion of large positive values to negative integers.
521522

523+
xa *= self.N
522524
if NP_CLIP_OUT:
523-
np.clip(xa * self.N, -1, self.N, out=xa)
525+
np.clip(xa, -1, self.N, out=xa)
524526
else:
525-
xa = np.clip(xa * self.N, -1, self.N)
527+
xa = np.clip(xa, -1, self.N)
526528

527529
# ensure that all 'under' values will still have negative
528530
# value after casting to int
@@ -532,8 +534,11 @@ def __call__(self, X, alpha=None, bytes=False):
532534
# otherwise the under-range values get converted to over-range.
533535
np.putmask(xa, xa>self.N-1, self._i_over)
534536
np.putmask(xa, xa<0, self._i_under)
535-
if mask_bad is not None and mask_bad.shape == xa.shape:
536-
np.putmask(xa, mask_bad, self._i_bad)
537+
if mask_bad is not None:
538+
if mask_bad.shape == xa.shape:
539+
np.putmask(xa, mask_bad, self._i_bad)
540+
elif mask_bad:
541+
xa.fill(self._i_bad)
537542
if bytes:
538543
lut = (self._lut * 255).astype(np.uint8)
539544
else:
@@ -797,32 +802,60 @@ def __init__(self, vmin=None, vmax=None, clip=False):
797802
self.vmax = vmax
798803
self.clip = clip
799804

805+
@staticmethod
806+
def process_value(value):
807+
"""
808+
Homogenize the input *value* for easy and efficient normalization.
809+
810+
*value* can be a scalar or sequence.
811+
812+
Returns *result*, *is_scalar*, where *result* is a
813+
masked array matching *value*. Float dtypes are preserved;
814+
integer types with two bytes or smaller are converted to
815+
np.float32, and larger types are converted to np.float.
816+
Preserving float32 when possible, and using in-place operations,
817+
can greatly improve speed for large arrays.
818+
819+
Experimental; we may want to add an option to force the
820+
use of float32.
821+
"""
822+
if cbook.iterable(value):
823+
is_scalar = False
824+
result = ma.asarray(value)
825+
if result.dtype.kind == 'f':
826+
if isinstance(value, np.ndarray):
827+
result = result.copy()
828+
elif result.dtype.itemsize > 2:
829+
result = result.astype(np.float)
830+
else:
831+
result = result.astype(np.float32)
832+
else:
833+
is_scalar = True
834+
result = ma.array([value]).astype(np.float)
835+
return result, is_scalar
836+
800837
def __call__(self, value, clip=None):
801838
if clip is None:
802839
clip = self.clip
803840

804-
if cbook.iterable(value):
805-
vtype = 'array'
806-
val = ma.asarray(value).astype(np.float)
807-
else:
808-
vtype = 'scalar'
809-
val = ma.array([value]).astype(np.float)
841+
result, is_scalar = self.process_value(value)
810842

811-
self.autoscale_None(val)
843+
self.autoscale_None(result)
812844
vmin, vmax = self.vmin, self.vmax
813845
if vmin > vmax:
814846
raise ValueError("minvalue must be less than or equal to maxvalue")
815-
elif vmin==vmax:
816-
result = 0.0 * val
847+
elif vmin == vmax:
848+
result.fill(0) # Or should it be all masked? Or 0.5?
817849
else:
818850
vmin = float(vmin)
819851
vmax = float(vmax)
820852
if clip:
821-
mask = ma.getmask(val)
822-
val = ma.array(np.clip(val.filled(vmax), vmin, vmax),
823-
mask=mask)
824-
result = (val-vmin) / (vmax-vmin)
825-
if vtype == 'scalar':
853+
mask = ma.getmask(result)
854+
result = ma.array(np.clip(result.filled(vmax), vmin, vmax),
855+
mask=mask)
856+
result -= vmin
857+
result /= vmax - vmin
858+
if is_scalar:
826859
result = result[0]
827860
return result
828861

@@ -847,8 +880,10 @@ def autoscale(self, A):
847880

848881
def autoscale_None(self, A):
849882
' autoscale only None-valued vmin or vmax'
850-
if self.vmin is None: self.vmin = ma.min(A)
851-
if self.vmax is None: self.vmax = ma.max(A)
883+
if self.vmin is None:
884+
self.vmin = ma.min(A)
885+
if self.vmax is None:
886+
self.vmax = ma.max(A)
852887

853888
def scaled(self):
854889
'return true if vmin and vmax set'
@@ -862,30 +897,29 @@ def __call__(self, value, clip=None):
862897
if clip is None:
863898
clip = self.clip
864899

865-
if cbook.iterable(value):
866-
vtype = 'array'
867-
val = ma.asarray(value).astype(np.float)
868-
else:
869-
vtype = 'scalar'
870-
val = ma.array([value]).astype(np.float)
900+
result, is_scalar = self.process_value(value)
871901

872-
val = ma.masked_less_equal(val, 0, copy=False)
902+
result = ma.masked_less_equal(result, 0, copy=False)
873903

874-
self.autoscale_None(val)
904+
self.autoscale_None(result)
875905
vmin, vmax = self.vmin, self.vmax
876906
if vmin > vmax:
877907
raise ValueError("minvalue must be less than or equal to maxvalue")
878908
elif vmin<=0:
879909
raise ValueError("values must all be positive")
880910
elif vmin==vmax:
881-
result = 0.0 * val
911+
result.fill(0)
882912
else:
883913
if clip:
884-
mask = ma.getmask(val)
885-
val = ma.array(np.clip(val.filled(vmax), vmin, vmax),
914+
mask = ma.getmask(result)
915+
val = ma.array(np.clip(result.filled(vmax), vmin, vmax),
886916
mask=mask)
887-
result = (ma.log(val)-np.log(vmin))/(np.log(vmax)-np.log(vmin))
888-
if vtype == 'scalar':
917+
#result = (ma.log(result)-np.log(vmin))/(np.log(vmax)-np.log(vmin))
918+
# in-place equivalent of above can be much faster
919+
np.ma.log(result, result)
920+
result -= np.log(vmin)
921+
result /= (np.log(vmax) - np.log(vmin))
922+
if is_scalar:
889923
result = result[0]
890924
return result
891925

0 commit comments

Comments
 (0)