summaryrefslogtreecommitdiff
path: root/silx/third_party
diff options
context:
space:
mode:
Diffstat (limited to 'silx/third_party')
-rw-r--r--silx/third_party/EdfFile.py18
-rw-r--r--silx/third_party/TiffIO.py12
-rw-r--r--silx/third_party/modest_image.py322
3 files changed, 338 insertions, 14 deletions
diff --git a/silx/third_party/EdfFile.py b/silx/third_party/EdfFile.py
index b20e5bb..d06a211 100644
--- a/silx/third_party/EdfFile.py
+++ b/silx/third_party/EdfFile.py
@@ -434,7 +434,7 @@ class EdfFile(object):
self.Images[Index].DataType = 'UnsignedShort'
try:
self.__data = numpy.reshape(
- numpy.fromstring(binary, numpy.uint16),
+ numpy.copy(numpy.frombuffer(binary, numpy.uint16)),
(self.Images[Index].Dim2, self.Images[Index].Dim1))
except ValueError:
msg = 'Size spec in ADSC-header does not match size of image data field'
@@ -587,12 +587,12 @@ class EdfFile(object):
if self.Images[Index].NumDim == 3:
image = self.Images[Index]
sizeToRead = image.Dim1 * image.Dim2 * image.Dim3 * datasize
- Data = numpy.fromstring(self.File.read(sizeToRead), datatype)
+ Data = numpy.copy(numpy.frombuffer(self.File.read(sizeToRead), datatype))
Data = numpy.reshape(Data, (self.Images[Index].Dim3, self.Images[Index].Dim2, self.Images[Index].Dim1))
elif self.Images[Index].NumDim == 2:
image = self.Images[Index]
sizeToRead = image.Dim1 * image.Dim2 * datasize
- Data = numpy.fromstring(self.File.read(sizeToRead), datatype)
+ Data = numpy.copy(numpy.frombuffer(self.File.read(sizeToRead), datatype))
# print "datatype = ",datatype
# print "Data.type = ", Data.dtype.char
# print "self.Images[Index].DataType ", self.Images[Index].DataType
@@ -603,7 +603,7 @@ class EdfFile(object):
Data = numpy.reshape(Data, (self.Images[Index].Dim2, self.Images[Index].Dim1))
elif self.Images[Index].NumDim == 1:
sizeToRead = self.Images[Index].Dim1 * datasize
- Data = numpy.fromstring(self.File.read(sizeToRead), datatype)
+ Data = numpy.copy(numpy.frombuffer(self.File.read(sizeToRead), datatype))
elif self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE:
return self.__data[Pos[1]:(Pos[1] + Size[1]),
Pos[0]:(Pos[0] + Size[0])]
@@ -628,7 +628,7 @@ class EdfFile(object):
if Size[0] == 0:
Size[0] = sizex - Pos[0]
self.File.seek((Pos[0] * size_pixel) + self.Images[Index].DataPosition, 0)
- Data = numpy.fromstring(self.File.read(Size[0] * size_pixel), type_)
+ Data = numpy.copy(numpy.frombuffer(self.File.read(Size[0] * size_pixel), type_))
elif self.Images[Index].NumDim == 2:
if Pos is None:
Pos = (0, 0)
@@ -646,7 +646,7 @@ class EdfFile(object):
dataindex = 0
for y in range(Pos[1], Pos[1] + Size[1]):
self.File.seek((((y * sizex) + Pos[0]) * size_pixel) + self.Images[Index].DataPosition, 0)
- line = numpy.fromstring(self.File.read(Size[0] * size_pixel), type_)
+ line = numpy.copy(numpy.frombuffer(self.File.read(Size[0] * size_pixel), type_))
Data[dataindex, :] = line[:]
# Data=numpy.concatenate((Data,line))
dataindex += 1
@@ -669,7 +669,7 @@ class EdfFile(object):
for z in range(Pos[2], Pos[2] + Size[2]):
for y in range(Pos[1], Pos[1] + Size[1]):
self.File.seek(((((z * sizey + y) * sizex) + Pos[0]) * size_pixel) + self.Images[Index].DataPosition, 0)
- line = numpy.fromstring(self.File.read(Size[0] * size_pixel), type_)
+ line = numpy.copy(numpy.frombuffer(self.File.read(Size[0] * size_pixel), type_))
Data = numpy.concatenate((Data, line))
Data = numpy.reshape(Data, (Size[2], Size[1], Size[0]))
@@ -698,7 +698,9 @@ class EdfFile(object):
size_img = size_row * self.Images[Index].Dim2
offset = offset + (Position[2] * size_img)
self.File.seek(self.Images[Index].DataPosition + offset, 0)
- Data = numpy.fromstring(self.File.read(size_pixel), self.__GetDefaultNumpyType__(self.Images[Index].DataType, index=Index))
+ Data = numpy.copy(numpy.frombuffer(self.File.read(size_pixel),
+ self.__GetDefaultNumpyType__(self.Images[Index].DataType,
+ index=Index)))
if self.SysByteOrder.upper() != self.Images[Index].ByteOrder.upper():
Data = Data.byteswap()
Data = self.__SetDataType__(Data, "DoubleValue")
diff --git a/silx/third_party/TiffIO.py b/silx/third_party/TiffIO.py
index 156ae11..8768cff 100644
--- a/silx/third_party/TiffIO.py
+++ b/silx/third_party/TiffIO.py
@@ -653,9 +653,9 @@ class TiffIO(object):
fd.seek(stripOffsets[0] + rowMin * bytesPerRow)
nBytes = (rowMax-rowMin+1) * bytesPerRow
if self._swap:
- readout = numpy.fromstring(fd.read(nBytes), dtype).byteswap()
+ readout = numpy.copy(numpy.frombuffer(fd.read(nBytes), dtype)).byteswap()
else:
- readout = numpy.fromstring(fd.read(nBytes), dtype)
+ readout = numpy.copy(numpy.frombuffer(fd.read(nBytes), dtype))
if hasattr(nBits, 'index'):
readout.shape = -1, nColumns, len(nBits)
elif info['colormap'] is not None:
@@ -704,9 +704,9 @@ class TiffIO(object):
#if read -128 ignore the byte
continue
if self._swap:
- readout = numpy.fromstring(bufferBytes, dtype).byteswap()
+ readout = numpy.copy(numpy.frombuffer(bufferBytes, dtype)).byteswap()
else:
- readout = numpy.fromstring(bufferBytes, dtype)
+ readout = numpy.copy(numpy.frombuffer(bufferBytes, dtype))
if hasattr(nBits, 'index'):
readout.shape = -1, nColumns, len(nBits)
elif info['colormap'] is not None:
@@ -719,9 +719,9 @@ class TiffIO(object):
if 1:
#use numpy
if self._swap:
- readout = numpy.fromstring(fd.read(nBytes), dtype).byteswap()
+ readout = numpy.copy(numpy.frombuffer(fd.read(nBytes), dtype)).byteswap()
else:
- readout = numpy.fromstring(fd.read(nBytes), dtype)
+ readout = numpy.copy(numpy.frombuffer(fd.read(nBytes), dtype))
if hasattr(nBits, 'index'):
readout.shape = -1, nColumns, len(nBits)
elif colormap is not None:
diff --git a/silx/third_party/modest_image.py b/silx/third_party/modest_image.py
new file mode 100644
index 0000000..3a64d1a
--- /dev/null
+++ b/silx/third_party/modest_image.py
@@ -0,0 +1,322 @@
+"""
+Taken from https://github.com/ChrisBeaumont/mpl-modest-image,
+commit 0545d2c58970bed9ac366193eaf771cc3247d250
+
+Copyright (c) 2013 Chris Beaumont
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+Modification of Chris Beaumont's mpl-modest-image package to allow the use of
+set_extent.
+"""
+from __future__ import print_function, division
+
+import matplotlib
+rcParams = matplotlib.rcParams
+
+import matplotlib.image as mi
+import matplotlib.colors as mcolors
+import matplotlib.cbook as cbook
+from matplotlib.transforms import IdentityTransform, Affine2D
+
+import numpy as np
+
+IDENTITY_TRANSFORM = IdentityTransform()
+
+
+class ModestImage(mi.AxesImage):
+
+ """
+ Computationally modest image class.
+
+ ModestImage is an extension of the Matplotlib AxesImage class
+ better suited for the interactive display of larger images. Before
+ drawing, ModestImage resamples the data array based on the screen
+ resolution and view window. This has very little affect on the
+ appearance of the image, but can substantially cut down on
+ computation since calculations of unresolved or clipped pixels
+ are skipped.
+
+ The interface of ModestImage is the same as AxesImage. However, it
+ does not currently support setting the 'extent' property. There
+ may also be weird coordinate warping operations for images that
+ I'm not aware of. Don't expect those to work either.
+ """
+
+ def __init__(self, *args, **kwargs):
+ self._full_res = None
+ self._full_extent = kwargs.get('extent', None)
+ super(ModestImage, self).__init__(*args, **kwargs)
+ self.invalidate_cache()
+
+ def set_data(self, A):
+ """
+ Set the image array
+
+ ACCEPTS: numpy/PIL Image A
+ """
+ self._full_res = A
+ self._A = A
+
+ if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
+ np.float):
+ raise TypeError("Image data can not convert to float")
+
+ if (self._A.ndim not in (2, 3) or
+ (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
+ raise TypeError("Invalid dimensions for image data")
+
+ self.invalidate_cache()
+
+ def invalidate_cache(self):
+ self._bounds = None
+ self._imcache = None
+ self._rgbacache = None
+ self._oldxslice = None
+ self._oldyslice = None
+ self._sx, self._sy = None, None
+ self._pixel2world_cache = None
+ self._world2pixel_cache = None
+
+ def set_extent(self, extent):
+ self._full_extent = extent
+ self.invalidate_cache()
+ mi.AxesImage.set_extent(self, extent)
+
+ def get_array(self):
+ """Override to return the full-resolution array"""
+ return self._full_res
+
+ @property
+ def _pixel2world(self):
+
+ if self._pixel2world_cache is None:
+
+ # Pre-compute affine transforms to convert between the 'world'
+ # coordinates of the axes (what is shown by the axis labels) to
+ # 'pixel' coordinates in the underlying array.
+
+ extent = self._full_extent
+
+ if extent is None:
+
+ self._pixel2world_cache = IDENTITY_TRANSFORM
+
+ else:
+
+ self._pixel2world_cache = Affine2D()
+
+ self._pixel2world.translate(+0.5, +0.5)
+
+ self._pixel2world.scale((extent[1] - extent[0]) / self._full_res.shape[1],
+ (extent[3] - extent[2]) / self._full_res.shape[0])
+
+ self._pixel2world.translate(extent[0], extent[2])
+
+ self._world2pixel_cache = None
+
+ return self._pixel2world_cache
+
+ @property
+ def _world2pixel(self):
+ if self._world2pixel_cache is None:
+ self._world2pixel_cache = self._pixel2world.inverted()
+ return self._world2pixel_cache
+
+ def _scale_to_res(self):
+ """
+ Change self._A and _extent to render an image whose resolution is
+ matched to the eventual rendering.
+ """
+
+ # Find out how we need to slice the array to make sure we match the
+ # resolution of the display. We pass self._world2pixel which matters
+ # for cases where the extent has been set.
+ x0, x1, sx, y0, y1, sy = extract_matched_slices(axes=self.axes,
+ shape=self._full_res.shape,
+ transform=self._world2pixel)
+
+ # Check whether we've already calculated what we need, and if so just
+ # return without doing anything further.
+ if (self._bounds is not None and
+ sx >= self._sx and sy >= self._sy and
+ x0 >= self._bounds[0] and x1 <= self._bounds[1] and
+ y0 >= self._bounds[2] and y1 <= self._bounds[3]):
+ return
+
+ # Slice the array using the slices determined previously to optimally
+ # match the display
+ self._A = self._full_res[y0:y1:sy, x0:x1:sx]
+ self._A = cbook.safe_masked_invalid(self._A)
+
+ # We now determine the extent of the subset of the image, by determining
+ # it first in pixel space, and converting it to the 'world' coordinates.
+
+ # See https://github.com/matplotlib/matplotlib/issues/8693 for a
+ # demonstration of why origin='upper' and extent=None needs to be
+ # special-cased.
+
+ if self.origin == 'upper' and self._full_extent is None:
+ xmin, xmax, ymin, ymax = x0 - .5, x1 - .5, y1 - .5, y0 - .5
+ else:
+ xmin, xmax, ymin, ymax = x0 - .5, x1 - .5, y0 - .5, y1 - .5
+
+ xmin, ymin, xmax, ymax = self._pixel2world.transform([(xmin, ymin), (xmax, ymax)]).ravel()
+
+ mi.AxesImage.set_extent(self, [xmin, xmax, ymin, ymax])
+ # self.set_extent([xmin, xmax, ymin, ymax])
+
+ # Finally, we cache the current settings to avoid re-computing similar
+ # arrays in future.
+ self._sx = sx
+ self._sy = sy
+ self._bounds = (x0, x1, y0, y1)
+
+ self.changed()
+
+ def draw(self, renderer, *args, **kwargs):
+ if self._full_res.shape is None:
+ return
+ self._scale_to_res()
+ super(ModestImage, self).draw(renderer, *args, **kwargs)
+
+
+def main():
+ from time import time
+ import matplotlib.pyplot as plt
+ x, y = np.mgrid[0:2000, 0:2000]
+ data = np.sin(x / 10.) * np.cos(y / 30.)
+
+ f = plt.figure()
+ ax = f.add_subplot(111)
+
+ # try switching between
+ artist = ModestImage(ax, data=data)
+
+ ax.set_aspect('equal')
+ artist.norm.vmin = -1
+ artist.norm.vmax = 1
+
+ ax.add_artist(artist)
+
+ t0 = time()
+ plt.gcf().canvas.draw()
+ t1 = time()
+
+ print("Draw time for %s: %0.1f ms" % (artist.__class__.__name__,
+ (t1 - t0) * 1000))
+
+ plt.show()
+
+
+def imshow(axes, X, cmap=None, norm=None, aspect=None,
+ interpolation=None, alpha=None, vmin=None, vmax=None,
+ origin=None, extent=None, shape=None, filternorm=1,
+ filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
+ """Similar to matplotlib's imshow command, but produces a ModestImage
+
+ Unlike matplotlib version, must explicitly specify axes
+ """
+ if not axes._hold:
+ axes.cla()
+ if norm is not None:
+ assert(isinstance(norm, mcolors.Normalize))
+ if aspect is None:
+ aspect = rcParams['image.aspect']
+ axes.set_aspect(aspect)
+ im = ModestImage(axes, cmap=cmap, norm=norm, interpolation=interpolation,
+ origin=origin, extent=extent, filternorm=filternorm,
+ filterrad=filterrad, resample=resample, **kwargs)
+
+ im.set_data(X)
+ im.set_alpha(alpha)
+ axes._set_artist_props(im)
+
+ if im.get_clip_path() is None:
+ # image does not already have clipping set, clip to axes patch
+ im.set_clip_path(axes.patch)
+
+ # if norm is None and shape is None:
+ # im.set_clim(vmin, vmax)
+ if vmin is not None or vmax is not None:
+ im.set_clim(vmin, vmax)
+ elif norm is None:
+ im.autoscale_None()
+
+ im.set_url(url)
+
+ # update ax.dataLim, and, if autoscaling, set viewLim
+ # to tightly fit the image, regardless of dataLim.
+ im.set_extent(im.get_extent())
+
+ axes.images.append(im)
+ im._remove_method = lambda h: axes.images.remove(h)
+
+ return im
+
+
+def extract_matched_slices(axes=None, shape=None, extent=None,
+ transform=IDENTITY_TRANSFORM):
+ """Determine the slice parameters to use, matched to the screen.
+
+ :param ax: Axes object to query. It's extent and pixel size
+ determine the slice parameters
+
+ :param shape: Tuple of the full image shape to slice into. Upper
+ boundaries for slices will be cropped to fit within
+ this shape.
+
+ :rtype: tulpe of x0, x1, sx, y0, y1, sy
+
+ Indexing the full resolution array as array[y0:y1:sy, x0:x1:sx] returns
+ a view well-matched to the axes' resolution and extent
+ """
+
+ # Find extent in display pixels (this gives the resolution we need
+ # to sample the array to)
+ ext = (axes.transAxes.transform([(1, 1)]) - axes.transAxes.transform([(0, 0)]))[0]
+
+ # Find the extent of the axes in 'world' coordinates
+ xlim, ylim = axes.get_xlim(), axes.get_ylim()
+
+ # Transform the limits to pixel coordinates
+ ind0 = transform.transform([min(xlim), min(ylim)])
+ ind1 = transform.transform([max(xlim), max(ylim)])
+
+ def _clip(val, lo, hi):
+ return int(max(min(val, hi), lo))
+
+ # Determine the range of pixels to extract from the array, including a 5
+ # pixel margin all around. We ensure that the shape of the resulting array
+ # will always be at least (1, 1) even if there is really no overlap, to
+ # avoid issues.
+ y0 = _clip(ind0[1] - 5, 0, shape[0] - 1)
+ y1 = _clip(ind1[1] + 5, 1, shape[0])
+ x0 = _clip(ind0[0] - 5, 0, shape[1] - 1)
+ x1 = _clip(ind1[0] + 5, 1, shape[1])
+
+ # Determine the strides that can be used when extracting the array
+ sy = int(max(1, min((y1 - y0) / 5., np.ceil(abs((ind1[1] - ind0[1]) / ext[1])))))
+ sx = int(max(1, min((x1 - x0) / 5., np.ceil(abs((ind1[0] - ind0[0]) / ext[0])))))
+
+ return x0, x1, sx, y0, y1, sy
+
+
+if __name__ == "__main__":
+ main()