@@ -460,15 +460,36 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
460
460
if newmax is not None or newmin is not None :
461
461
np .clip (A_scaled , newmin , newmax , out = A_scaled )
462
462
463
+ # used to rescale the raw data to [offset, 1-offset]
464
+ # so that the resampling code will run cleanly. Using
465
+ # dyadic numbers here could reduce the error, but
466
+ # would not full eliminate it and breaks a number of
467
+ # tests (due to the slightly different error bouncing
468
+ # some pixels across a boundary in the (very
469
+ # quantized) color mapping step).
470
+ offset = .1
471
+ frac = .8
472
+ # we need to run the vmin/vmax through the same rescaling
473
+ # that we run the raw data through because there are small
474
+ # errors in the round-trip due to float precision. If we
475
+ # do not run the vmin/vmax through the same pipeline we can
476
+ # have values close or equal to the boundaries end up on the
477
+ # wrong side.
478
+ vrange = np .array ([self .norm .vmin , self .norm .vmax ],
479
+ dtype = scaled_dtype )
480
+
463
481
A_scaled -= a_min
482
+ vrange -= a_min
464
483
# a_min and a_max might be ndarray subclasses so use
465
484
# item to avoid errors
466
485
a_min = a_min .astype (scaled_dtype ).item ()
467
486
a_max = a_max .astype (scaled_dtype ).item ()
468
487
469
488
if a_min != a_max :
470
- A_scaled /= ((a_max - a_min ) / 0.8 )
471
- A_scaled += 0.1
489
+ A_scaled /= ((a_max - a_min ) / frac )
490
+ vrange /= ((a_max - a_min ) / frac )
491
+ A_scaled += offset
492
+ vrange += offset
472
493
# resample the input data to the correct resolution and shape
473
494
A_resampled = _resample (self , A_scaled , out_shape , t )
474
495
# done with A_scaled now, remove from namespace to be sure!
@@ -478,10 +499,13 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
478
499
# below the original min/max will still be above /
479
500
# below, but possibly clipped in the case of higher order
480
501
# interpolation + drastically changing data.
481
- A_resampled -= 0.1
502
+ A_resampled -= offset
503
+ vrange -= offset
482
504
if a_min != a_max :
483
- A_resampled *= ((a_max - a_min ) / 0.8 )
505
+ A_resampled *= ((a_max - a_min ) / frac )
506
+ vrange *= ((a_max - a_min ) / frac )
484
507
A_resampled += a_min
508
+ vrange += a_min
485
509
# if using NoNorm, cast back to the original datatype
486
510
if isinstance (self .norm , mcolors .NoNorm ):
487
511
A_resampled = A_resampled .astype (A .dtype )
@@ -508,7 +532,14 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
508
532
out_alpha *= _resample (self , alpha , out_shape ,
509
533
t , resample = True )
510
534
# mask and run through the norm
511
- output = self .norm (np .ma .masked_array (A_resampled , out_mask ))
535
+ resampled_masked = np .ma .masked_array (A_resampled , out_mask )
536
+ # we have re-set the vmin/vmax to account for small errors
537
+ # that may have moved input values in/out of range
538
+ with cbook ._setattr_cm (self .norm ,
539
+ vmin = vrange [0 ],
540
+ vmax = vrange [1 ],
541
+ ):
542
+ output = self .norm (resampled_masked )
512
543
else :
513
544
if A .shape [2 ] == 3 :
514
545
A = _rgb_to_rgba (A )
0 commit comments