def points(self, vertices): vertices = np.ascontiguousarray(vertices) # Store this to keep its data from being garbage collected. self._vertices = vertices if vertices is None: self.vtk_polydata.SetPoints(None) else: points = self.vtk_polydata.GetPoints() or vtk.vtkPoints() points.SetData(numpy_to_vtk(vertices)) points._numpy_reference = vertices self.vtk_polydata.SetPoints(points)
def setter(self, colors): if colors is not None: if colors.ndim == 1: colors = colors[:, np.newaxis] if colors.ndim != 2: raise ValueError("`colors` must be either 1-D or 2-D") colors = np.ascontiguousarray(colors) if colors.shape[1] == 1: # treat colors as scalars to be passed through a colormap self.color_mode = vtk.VTK_COLOR_MODE_DEFAULT elif colors.shape[1] == 2: # treat colors as texture coordinates to be passed through a texturemap # currently texture maps haven't been properly implemented. The # colors are evaluated immediately here. if self.texture_map is None: raise ValueError( "A texture map must be provided in polydata.texture_map to use uv scalars." ) colors = self.texture_map(colors) # self.color_mode = vtk.VTK_COLOR_MODE_MAP_SCALARS self.color_mode = vtk.VTK_COLOR_MODE_DIRECT_SCALARS elif colors.shape[1] == 3: # treat colors as raw RGB values self.color_mode = vtk.VTK_COLOR_MODE_DIRECT_SCALARS else: raise ValueError("{} is an invalid shape.".format( colors.shape)) self._colors = colors colors = numpy_to_vtk(colors) colors._numpy_ref = self._colors getter_getter(self)().SetScalars(colors) setattr(self, "color_source", vpl_name)
def vtkimagedata_from_array(arr, image_data=None): """Convert a numpy array to a vtkImageData. :param arr: Array of colors. :type arr: np.ndarray with dtype ``np.uint8`` :param image_data: An image data to write into, a new one is created if not specified, defaults to ``None``. :type image_data: `vtkImageData`_, optional :return: A VTK image. :rtype: `vtkImageData`_ Grayscale images are allowed. ``arr.shape`` can be any of ``(m, n)`` or ``(m, n, 1)`` for greyscale, ``(m, n, 3)`` for RGB, or ``(m, n, 4)`` for RGBA. .. seealso:: :meth:`vtkimagedata_to_array` for the reverse. .. seealso:: :meth:`as_vtkimagedata` for converting from other types. """ assert arr.dtype == np.uint8 if image_data is None: image_data = vtk.vtkImageData() if arr.ndim == 2: arr = arr[..., np.newaxis] image_data.SetDimensions(arr.shape[1], arr.shape[0], 1) image_data.SetNumberOfScalarComponents(arr.shape[2], image_data.GetInformation()) pd = image_data.GetPointData() new_arr = arr[::-1].reshape((-1, arr.shape[2])) pd.SetScalars(numpy_to_vtk(new_arr)) pd._numpy_reference = new_arr.data return image_data
def read(path, raw_bytes=None, format=None, convert_to_array=True): """Read an image from a file using one of VTK's ``vtkFormatReader`` classes where ``Format`` is replaced by JPEG, PNG, BMP or TIFF. :param path: Filename or file handle or ``None`` if using the **raw_bytes** argument. :type path: str, os.PathLike, io.BytesIO :param raw_bytes: Image compressed binary data, defaults to ``None``. :type raw_bytes: bytes, optional :param format: Image format extension (e.g. jpg), not needed if format can be determined from **path**, defaults to ``None``. :type format: str, optional :param convert_to_array: If true, convert to numpy, otherwise leave as vtkImageData, defaults to ``True``. :type convert_to_array: bool, optional :return: Read image. :rtype: np.ndarray or `vtkImageData`_ The file format can be determined automatically from the **path** suffix or the beginning of **raw_bytes**. **format** can be any of JPEG, PNG, TIFF, BMP. It is case insensitive, tolerant to preceding '.'s e.g. ``format=".jpg"`` and understands the aliases JPG \u21d4 JPEG and TIF \u21d4 TIFF. The following demonstrates how to use pseudo file objects to avoid temporary files when reading an image from the web. .. code-block:: python import vtkplotlib as vpl # Link you're image url here url = "https://raw.githubusercontent.com/bwoodsend/vtkplotlib/master/vtkplotlib/data/icons/Right.jpg" # You can make the url request with either: from urllib import request raw_bytes = request.urlopen(url).read() # Or if you have the more modern requests library installed: # import requests # raw_bytes = requests.get(url).content # Pass the bytes to :meth:`read` using: image = vpl.image_io.read(path=None, raw_bytes=raw_bytes) # Visualize using matplotlib. from matplotlib import pyplot pyplot.imshow(image) pyplot.show() .. warning:: Some formats only support reading from disk. See ``vtkplotlib.image_io.BUFFERABLE_FORMAT_MODES`` or for which these are. .. note:: BytesIO and raw bytes functionality is new in vtkplotlib >= 1.3.0. Older versions are hardcoded to write to disk and therefore **path** must be a filename and not a BytesIO or similar pseudo file object. .. note:: There is a bug in VTK==9.0.0 """ if isinstance(raw_bytes, bool): raise TypeError( "The arguments for this method have changed. If you meant to set the `convert_to_array` argument, please treat it as keyword only. i.e ``convert_to_array={}``" .format(raw_bytes)) if (path is None) == (raw_bytes is None): raise TypeError( "Exactly one of `path` and `raw_bytes` should be specified.") if isinstance(path, io.IOBase): raw_bytes = path.read() format = _normalise_format(path, format, raw_bytes) try: Reader = getattr(vtk, "vtk{}Reader".format(format)) except AttributeError: return NotImplemented reader = Reader() with VTKErrorRaiser(reader): if nuts_and_bolts.isinstance_PathLike(path): with PathHandler(path) as path_handler: reader.SetFileName(path_handler.access_path) reader.Update() else: if raw_bytes is not None: bytes_arr = np.frombuffer(raw_bytes, dtype=np.uint8) vtk_bytes = numpy_to_vtk(bytes_arr) vtk_bytes._numpy_ref = bytes_arr reader.SetMemoryBuffer(vtk_bytes) reader.SetMemoryBufferLength(len(bytes_arr)) reader.Update() im_data = reader.GetOutput() if format == "TIFF" and VTK_VERSION_INFO >= (9, 0, 0): # There's a bug in VTK 9 that reads TIFFs upside-down. im_data = vtkimagedata_to_array(im_data)[::-1] return im_data if convert_to_array else vtkimagedata_from_array(im_data) if convert_to_array: return vtkimagedata_to_array(im_data) return im_data
def as_vtk_cmap(cmap, cache=True): """Colormaps are generally converted implicitly from any valid format to a ``vtk.vtkLookupTable`` using this method. `Any valid format` is defined as the following: #. A string matplotlib colormap name such as ``'RdYlGn'``. #. Anything out of the ``matplotlib.cm`` package. #. A list of named colors such as ``["red", "white", "blue"]``. See :meth:`cmap_from_list` for more details and flexibility. #. An ``(n, 3)`` or ``(n, 4)`` numpy array of RGB(A) int or float values. #. A callable that takes an array of scalars and returns an array of form **4**. Unless specified otherwise using ``cache=False``, named colormaps of form **1** are cached in ``vtkplotlib.colors.converted_cmaps``. If you intend to modify the vtkLookupTable then it's best not to allow caching. .. note:: VTK doesn't interpolate between colors. i.e if you use form **4** and only provide a short list of colors then the resulting heatmap will be block colors rather than a smooth gradient. .. note:: Whilst VTK appears to support opacity gradients in the colormaps, it doesn't actually use them. If your colormap says opacity should vary with scalars then the opacity is averaged for the plot. """ if isinstance(cmap, _future_utils.string_types): if cache and cmap in converted_cmaps: return converted_cmaps[cmap] cmap = cm.get_cmap(cmap) if isinstance(cmap, vtk.vtkLookupTable): return cmap if cache and isinstance( cmap, (colors.ListedColormap, colors.LinearSegmentedColormap)): name = cmap.name if name in converted_cmaps: return converted_cmaps[name] else: name = None if isinstance(cmap, colors.ListedColormap): cmap = np.array(cmap.colors) if callable(cmap): cmap = cmap(np.arange(256, dtype=np.uint8)) if isinstance(cmap, list): cmap = cmap_from_list(cmap) if not isinstance(cmap, np.ndarray): raise TypeError("cmap is of an invalid type {}.".format(type(cmap))) if cmap.ndim == 2 and 3 <= cmap.shape[1] <= 4: cmap = np.ascontiguousarray( (colors.to_rgba_array(cmap) * 255).astype(np.uint8)) table = vtk.vtkLookupTable() table.SetTable(numpy_to_vtk(cmap)) table._numpy_ref = cmap _temp.append(cmap) if name is not None: converted_cmaps[name] = table return table else: raise ValueError( "`cmap` should have shape (n, 3) or (n, 4). Received {}.".format( cmap.shape))