From c3996827efc002d7551f85a162294c25ea56cff8 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Mon, 24 Jun 2019 17:19:18 +0930 Subject: [PATCH] ENH: New Bitmap class, for loading bitmap images --- fsl/data/bitmap.py | 155 +++++++++++++++++++++++++++++++++++++++++++++ fsl/data/utils.py | 3 + 2 files changed, 158 insertions(+) create mode 100644 fsl/data/bitmap.py diff --git a/fsl/data/bitmap.py b/fsl/data/bitmap.py new file mode 100644 index 000000000..1f6641716 --- /dev/null +++ b/fsl/data/bitmap.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# +# bitmap.py - The Bitmap class +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# +"""This module contains the :class:`Bitmap` class, for loading bitmap image +files. +""" + + +import os.path as op +import logging +import six + +import numpy as np +import PIL.Image as Image + +from . import image as fslimage + + +log = logging.getLogger(__name__) + + +BITMAP_EXTENSIONS = ['.bmp', '.png', '.jpg', '.jpeg', + '.tif', '.tiff', '.gif', '.rgba'] +"""File extensions we understand. """ + + +BITMAP_DESCRIPTIONS = [ + 'Bitmap', + 'Portable Network Graphics', + 'JPEG', + 'JPEG', + 'TIFF', + 'TIFF', + 'Graphics Interchange Format', + 'Raw RGBA'] +"""A description for each :attr:`BITMAP_EXTENSION`. """ + + +class Bitmap(object): + """The ``Bitmap`` class can be used to load a bitmap image. The + :meth:`asImage` method will convert the bitmap into an :class:`.Image` + instance. + """ + + def __init__(self, bmp): + """Create a ``Bitmap``. + + :arg bmp: File name of an image, or a ``numpy`` array containing image + data. + """ + + if isinstance(bmp, six.string_types): + source = bmp + data = np.array(Image.open(source)) + + elif isinstance(bmp, np.ndarray): + source = 'array' + data = np.copy(bmp) + + else: + raise ValueError('unknown bitmap: {}'.format(bmp)) + + # Make the array (w, h, c) + data = data.transpose((1, 0, 2)) + w, h = data.shape[:2] + data = np.array(data, dtype=np.uint8, order='C') + + self.__data = data + self.__dataSource = source + self.__name = op.basename(source) + + + def __hash__(self): + """Returns a number which uniquely idenfities this ``Bitmap`` instance + (the result of ``id(self)``). + """ + return id(self) + + + def __str__(self): + """Return a string representation of this ``Bitmap`` instance.""" + return '{}({}, {})'.format(self.__class__.__name__, + self.dataSource, + self.shape) + + + def __repr__(self): + """See the :meth:`__str__` method. """ + return self.__str__() + + + @property + def name(self): + """Returns the name of this ``Bitmap``, typically the base name of the + file. + """ + return self.__name + + + @property + def dataSource(self): + """Returns the bitmap data source - typically the file name. """ + return self.__dataSource + + + @property + def data(self): + """Convenience method which returns the bitmap data as a ``(w, h, c)`` + array, where ``c`` is either 3 or 4. + """ + return self.__data + + + @property + def shape(self): + """Returns the bitmap shape - ``(width, height, nchannels)``. """ + return self.__data.shape + + + def asImage(self): + """Convert this ``Bitmap`` into an :class:`.Image` instance. """ + + width, height, nchannels = self.shape + + if nchannels == 1: + dtype = np.uint8 + + + elif nchannels == 3: + dtype = np.dtype([('R', 'uint8'), + ('G', 'uint8'), + ('B', 'uint8')]) + + elif nchannels == 4: + dtype = np.dtype([('R', 'uint8'), + ('G', 'uint8'), + ('B', 'uint8'), + ('A', 'uint8')]) + + else: + raise ValueError('Cannot convert bitmap with {} ' + 'channels into nifti image'.format(nchannels)) + + if nchannels == 1: + data = self.data.reshape((width, height)) + + else: + data = np.zeros((width, height), dtype=dtype) + for ch, ci in enumerate(dtype.names): + data[ch] = self.data[..., ci] + + return fslimage.Image(data, np.eye(4)) diff --git a/fsl/data/utils.py b/fsl/data/utils.py index 13aae313b..bc2c1e334 100644 --- a/fsl/data/utils.py +++ b/fsl/data/utils.py @@ -27,6 +27,7 @@ def guessType(path): import fsl.data.gifti as fslgifti import fsl.data.freesurfer as fslfs import fsl.data.mghimage as fslmgh + import fsl.data.bitmap as fslbmp import fsl.data.featimage as featimage import fsl.data.melodicimage as melimage import fsl.data.dtifit as dtifit @@ -56,6 +57,8 @@ def guessType(path): return fslfs.FreesurferMesh, path elif fslpath.hasExt(path, fslmgh.ALLOWED_EXTENSIONS): return fslmgh.MGHImage, path + elif fslpath.hasExt(path, fslbmp.BITMAP_EXTENSIONS): + return fslbmp.Bitmap, path # Other specialised image types elif melanalysis .isMelodicImage(path): -- GitLab