def load_dataset_vsimem(data_element: DataElement) -> "gdal.Dataset": """ Load GDAL dataset from element by writing its bytes to a virtual file and loading a dataset from that virtual file. Requires GDAL major version 2 or greater. :param smqtk.representation.DataElement data_element: Element to load dataset from. :return: GDAL Dataset :rtype: gdal.Dataset """ # Unguarded next() call is OK in this case because the generator returned # by ``_get_candidate_names()`` does not terminate. # noinspection PyProtectedMember get_candidate_names = tempfile._get_candidate_names # type: ignore tmp_vsimem_path = '/vsimem/{}'.format( next(get_candidate_names()) # lgtm[py/unguarded-next-in-generator] ) gdal.FileFromMemBuffer(tmp_vsimem_path, data_element.get_bytes()) try: yield gdal.Open(tmp_vsimem_path) finally: rc = gdal.Unlink(tmp_vsimem_path) if rc != 0: raise RuntimeError("Failed to gdal.Unlink virtual file '{}' " "containing bytes from {}.".format( tmp_vsimem_path, data_element))
def is_loadable_image(data_element: DataElement) -> bool: """ Determine if an image is able to be loaded by PIL. :param data_element: A data element to check :type data_element: DataElement :return: Whether or not the image is loadable :rtype: bool Example: >>> """ log = logging.getLogger(__name__) try: PIL.Image.open(io.BytesIO(data_element.get_bytes())) return True except IOError as ex: # noinspection PyProtectedMember log.debug( "Failed to convert '%s' bytes into an image " "(error: %s). Skipping", data_element, str(ex)) return False
def _load_as_matrix( self, data_element: DataElement, pixel_crop: Optional[AxisAlignedBoundingBox] = None) \ -> numpy.ndarray: """ Internal method to be implemented that attempts loading an image from the given data element and returning it as an image matrix. Pre-conditions: - ``pixel_crop`` has a non-zero volume and is composed of integer types. :param smqtk.representation.DataElement data_element: DataElement to load image data from. :param None|smqtk.representation.AxisAlignedBoundingBox pixel_crop: Optional pixel crop region to load from the given data. If this is provided it must represent a valid sub-region within the loaded image, otherwise a RuntimeError is raised. :raises RuntimeError: A crop region was specified but did not specify a valid sub-region of the image. :return: Numpy ndarray of the image data. Specific return format is implementation dependant. :rtype: numpy.ndarray """ # We may have to add a mode where we use write_temp and load from that # if loading large images straight from bytes-in-memory is a problem # and that approach actually alleviates anything. # Catch and raise alternate IOError exception for readability. try: #: :type: PIL.Image.Image img = PIL.Image.open(BytesIO(data_element.get_bytes())) except IOError as ex: ex_str = str(ex) if 'cannot identify image file' in ex_str: raise IOError("Failed to identify image from bytes provided " "by {}".format(data_element)) else: # pass through other exceptions raise if pixel_crop: if not crop_in_bounds(pixel_crop, *img.size): raise RuntimeError("Crop provided not within input image. " "Image shape: {}, crop: {}".format( img.size, pixel_crop)) img = img.crop(pixel_crop.min_vertex.tolist() + pixel_crop.max_vertex.tolist()) # If the loaded image is not already the optionally provided # explicit mode, convert it. if self._explicit_mode and img.mode != self._explicit_mode: img = img.convert(mode=self._explicit_mode) # noinspection PyTypeChecker return numpy.asarray(img)