Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita883cd2

Browse files
committed
FIX: first pass at re-working image interpolation
- interpolate raw, not normed, data - should reduce memory footprint, only 1 or 2 copies of input data - this changes many tests in small ways
1 parent5f534ad commita883cd2

File tree

1 file changed

+85
-88
lines changed

1 file changed

+85
-88
lines changed

‎lib/matplotlib/image.py

Lines changed: 85 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -356,58 +356,87 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
356356
out_height=int(out_height_base)
357357

358358
ifnotunsampled:
359-
created_rgba_mask=False
360-
361359
ifA.ndimnotin (2,3):
362360
raiseValueError("Invalid dimensions, got {}".format(A.shape))
363361

364362
ifA.ndim==2:
365-
A=self.norm(A)
366-
ifA.dtype.kind=='f':
367-
# If the image is greyscale, convert to RGBA and
368-
# use the extra channels for resizing the over,
369-
# under, and bad pixels. This is needed because
370-
# Agg's resampler is very aggressive about
371-
# clipping to [0, 1] and we use out-of-bounds
372-
# values to carry the over/under/bad information
373-
rgba=np.empty((A.shape[0],A.shape[1],4),dtype=A.dtype)
374-
rgba[...,0]=A# normalized data
375-
# this is to work around spurious warnings coming
376-
# out of masked arrays.
377-
withnp.errstate(invalid='ignore'):
378-
rgba[...,1]=np.where(A<0,np.nan,1)# under data
379-
rgba[...,2]=np.where(A>1,np.nan,1)# over data
380-
# Have to invert mask, Agg knows what alpha means
381-
# so if you put this in as 0 for 'good' points, they
382-
# all get zeroed out
383-
rgba[...,3]=1
384-
ifA.mask.shape==A.shape:
385-
# this is the case of a nontrivial mask
386-
mask=np.where(A.mask,np.nan,1)
387-
else:
388-
# this is the case that the mask is a
389-
# numpy.bool_ of False
390-
mask=A.mask
391-
# ~A.mask # masked data
392-
A=rgba
393-
output=np.zeros((out_height,out_width,4),
394-
dtype=A.dtype)
395-
alpha=1.0
396-
created_rgba_mask=True
363+
# if we are a 2D array, then we are running through the
364+
# norm + colormap transformation. However, in general the
365+
# input data is not going to match the size on the screen so we
366+
# have to resample to the correct number of pixels
367+
# need to
368+
369+
# TODO slice input array first
370+
371+
# make a working array up here, re-use twice to save memory
372+
working_array=np.empty(A.shape,dtype=np.float32)
373+
374+
a_min=np.nanmin(A)
375+
a_max=np.nanmax(A)
376+
# scale the input data to [.1, .9]. The Agg
377+
# interpolators clip to [0, 1] internally, use a
378+
# smaller input scale to identify which of the
379+
# interpolated points need to be should be flagged as
380+
# over / under.
381+
# This may introduce numeric instabilities in very broadly
382+
# scaled data
383+
A_scaled=working_array
384+
A_scaled[:]=A
385+
A_scaled-=a_min
386+
A_scaled/= ((a_max-a_min)/0.8)
387+
A_scaled+=0.1
388+
A_resampled=np.empty((out_height,out_width),dtype=A_scaled.dtype)
389+
A_resampled[:]=np.nan
390+
# resample the input data to the correct resolution and shape
391+
_image.resample(A_scaled,A_resampled,
392+
t,
393+
_interpd_[self.get_interpolation()],
394+
self.get_resample(),1.0,
395+
self.get_filternorm()or0.0,
396+
self.get_filterrad()or0.0)
397+
398+
# we are done with A_scaled now, remove from namespace to be sure!
399+
delA_scaled
400+
# un-scale the resampled data to approximatly the
401+
# original range things that interpolated to above /
402+
# below the original min/max will still be above /
403+
# below, but possibly clipped in the case of higher order
404+
# interpolation + drastically changing data.
405+
A_resampled-=0.1
406+
A_resampled*= ((a_max-a_min)/0.8)
407+
A_resampled+=a_min
408+
# if using NoNorm, cast back to the original datatype
409+
ifisinstance(self.norm,mcolors.NoNorm):
410+
A_resampled=A_resampled.astype(A.dtype)
411+
412+
mask=working_array
413+
ifA.mask.shape==A.shape:
414+
# this is the case of a nontrivial mask
415+
mask[:]=np.where(A.mask,np.float32(np.nan),
416+
np.float32(1))
397417
else:
398-
# colormap norms that output integers (ex NoNorm
399-
# and BoundaryNorm) to RGBA space before
400-
# interpolating. This is needed due to the
401-
# Agg resampler only working on floats in the
402-
# range [0, 1] and because interpolating indexes
403-
# into an arbitrary LUT may be problematic.
404-
#
405-
# This falls back to interpolating in RGBA space which
406-
# can produce it's own artifacts of colors not in the map
407-
# showing up in the final image.
408-
A=self.cmap(A,alpha=self.get_alpha(),bytes=True)
409-
410-
ifnotcreated_rgba_mask:
418+
mask[:]=1
419+
420+
# we always have to interpolate the mask to account for
421+
# non-affine transformations
422+
out_mask=np.empty((out_height,out_width),
423+
dtype=mask.dtype)
424+
out_mask[:]=np.nan
425+
_image.resample(mask,out_mask,
426+
t,
427+
_interpd_[self.get_interpolation()],
428+
True,1,
429+
self.get_filternorm()or0.0,
430+
self.get_filterrad()or0.0)
431+
# we are done with the mask, delete from namespace to be sure!
432+
delmask
433+
# Agg tells us a pixel has no value not setting a value into it
434+
# thus, if we didn't set it, should still be nan.
435+
out_mask=np.isnan(out_mask)
436+
437+
# mask and run through the norm
438+
output=self.norm(np.ma.masked_array(A_resampled,out_mask))
439+
else:
411440
# Always convert to RGBA, even if only RGB input
412441
ifA.shape[2]==3:
413442
A=_rgb_to_rgba(A)
@@ -420,57 +449,25 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
420449
ifalphaisNone:
421450
alpha=1.0
422451

423-
_image.resample(
424-
A,output,t,_interpd_[self.get_interpolation()],
425-
self.get_resample(),alpha,
426-
self.get_filternorm()or0.0,self.get_filterrad()or0.0)
427-
428-
ifcreated_rgba_mask:
429-
# Convert back to a masked greyscale array so
430-
# colormapping works correctly
431-
hid_output=output
432-
# any pixel where the a masked pixel is included
433-
# in the kernel (pulling this down from 1) needs to
434-
# be masked in the output
435-
iflen(mask.shape)==2:
436-
out_mask=np.empty((out_height,out_width),
437-
dtype=mask.dtype)
438-
_image.resample(mask,out_mask,t,
439-
_interpd_[self.get_interpolation()],
440-
True,1,
441-
self.get_filternorm()or0.0,
442-
self.get_filterrad()or0.0)
443-
out_mask=np.isnan(out_mask)
444-
else:
445-
out_mask=mask
446-
# we need to mask both pixels which came in as masked
447-
# and the pixels that Agg is telling us to ignore (relavent
448-
# to non-affine transforms)
449-
# Use half alpha as the threshold for pixels to mask.
450-
out_mask=out_mask| (hid_output[...,3]<.5)
451-
output=np.ma.masked_array(
452-
hid_output[...,0],
453-
out_mask)
454-
# 'unshare' the mask array to
455-
# needed to suppress numpy warning
456-
delout_mask
457-
invalid_mask=~output.mask*~np.isnan(output.data)
458-
# relabel under data. If any of the input data for
459-
# the pixel has input out of the norm bounds,
460-
output[np.isnan(hid_output[...,1])*invalid_mask]=-1
461-
# relabel over data
462-
output[np.isnan(hid_output[...,2])*invalid_mask]=2
452+
_image.resample(
453+
A,output,t,_interpd_[self.get_interpolation()],
454+
self.get_resample(),alpha,
455+
self.get_filternorm()or0.0,self.get_filterrad()or0.0)
463456

457+
# at this point output is either a 2D array of normed data (of int or float)
458+
# or an RGBA array of re-sampled input
464459
output=self.to_rgba(output,bytes=True,norm=False)
460+
# output is now a correctly sized RGBA array of uint8
465461

466462
# Apply alpha *after* if the input was greyscale without a mask
467-
ifA.ndim==2orcreated_rgba_mask:
463+
ifA.ndim==2:
468464
alpha=self.get_alpha()
469465
ifalphaisnotNoneandalpha!=1.0:
470466
alpha_channel=output[:, :,3]
471467
alpha_channel[:]=np.asarray(
472468
np.asarray(alpha_channel,np.float32)*alpha,
473469
np.uint8)
470+
474471
else:
475472
ifself._imcacheisNone:
476473
self._imcache=self.to_rgba(A,bytes=True,norm=(A.ndim==2))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp