Пример #1
0
    def read(self):
        """
        Reads and parses the spectral data XML file path.

        Returns
        -------
        bool
            Definition success.

        Examples
        --------
        >>> from os.path import dirname, join
        >>> directory = join(dirname(__file__), 'tests', 'resources')
        >>> sd = SpectralDistribution_IESTM2714(
        ...     join(directory, 'Fluorescent.spdx'))
        >>> sd.read()
        True
        >>> sd.header.description
        'Rare earth fluorescent lamp'
        >>> # Doctests ellipsis for Python 2.x compatibility.
        >>> sd[400]  # doctest: +ELLIPSIS
        0.0339999...
        """

        formatter = './{{{0}}}{1}/{{{0}}}{2}'

        tree = ElementTree.parse(self._path)  # nosec
        root = tree.getroot()

        namespace = re.match('{(.*)}', root.tag).group(1)

        self.name = os.path.splitext(os.path.basename(self._path))[0]

        iterator = root.iter

        for header_element in (self.header, self):
            mapping = header_element.mapping
            for specification in mapping.elements:
                element = root.find(
                    formatter.format(namespace, mapping.element,
                                     specification.element))
                if element is not None:
                    setattr(header_element, specification.attribute,
                            specification.read_conversion(element.text))

        # Reading spectral data.
        wavelengths = []
        values = []
        for spectral_data in iterator('{{{0}}}{1}'.format(
                namespace, self.mapping.data.element)):
            wavelengths.append(
                DEFAULT_FLOAT_DTYPE(
                    spectral_data.attrib[self.mapping.data.attribute]))
            values.append(DEFAULT_FLOAT_DTYPE(spectral_data.text))

        self.wavelengths = wavelengths
        self.values = values

        return True
Пример #2
0
    def load_fixtures(file_name, fixtures_directory='fixtures'):
        """
        Loads the fixtures data with given name.

        Parameters
        ----------
        file_name : unicode
            '.csv' fixture file name.
        fixtures_directory : unicode
            Relative directory path containing the '.csv' fixtures files.

        Returns
        -------
        list
            Fixtures data as a *list* of *dict* where each *dict* is a fixture
            case.
        """

        path = os.path.dirname(__file__)
        with open(os.path.join(path, fixtures_directory,
                               file_name)) as in_file:
            result = []
            for case_data in csv.DictReader(in_file):
                for key in case_data:
                    try:
                        case_data[key] = DEFAULT_FLOAT_DTYPE(case_data[key])
                    except ValueError:
                        pass
                result.append(case_data)
            return result
Пример #3
0
def as_float(a):
    """
    Converts given :math:`a` variable to *numeric* using the type defined by
    :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. In the event where
    :math:`a` cannot be converted, it is converted to *ndarray* using the type
    defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute.

    Parameters
    ----------
    a : object
        Variable to convert.

    Returns
    -------
    ndarray
        :math:`a` variable converted to *numeric*.

    Warnings
    --------
    The behaviour of this definition is different than
    :func:`colour.utilities.as_numeric` definition when it comes to conversion
    failure: the former will forcibly convert :math:`a` variable to *ndarray*
    using the type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE`
    attribute while the later will pass the :math:`a` variable as is.

    Examples
    --------
    >>> as_float(np.array([1]))
    1.0
    >>> as_float(np.arange(10))
    array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
    """

    return DEFAULT_FLOAT_DTYPE(a)
Пример #4
0
def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs):
    """
    Returns a grid on *xy* plane made of quad geometric elements and its
    associated faces and edges colours. Ticks and labels are added to the
    given axes according to the extended grid settings.

    Parameters
    ----------
    limits : array_like, optional
        Extended grid limits.
    segments : int, optional
        Edge segments count for the extended grid.
    labels : array_like, optional
        Axis labels.
    axes : matplotlib.axes.Axes, optional
        Axes to add the grid.

    Other Parameters
    ----------------
    grid_face_colours : array_like, optional
        Grid face colours array such as
        `grid_face_colours = (0.25, 0.25, 0.25)`.
    grid_edge_colours : array_like, optional
        Grid edge colours array such as
        `grid_edge_colours = (0.25, 0.25, 0.25)`.
    grid_face_alpha : numeric, optional
        Grid face opacity value such as `grid_face_alpha = 0.1`.
    grid_edge_alpha : numeric, optional
        Grid edge opacity value such as `grid_edge_alpha = 0.5`.
    x_axis_colour : array_like, optional
        *X* axis colour array such as `x_axis_colour = (0.0, 0.0, 0.0, 1.0)`.
    y_axis_colour : array_like, optional
        *Y* axis colour array such as `y_axis_colour = (0.0, 0.0, 0.0, 1.0)`.
    x_ticks_colour : array_like, optional
        *X* axis ticks colour array such as
        `x_ticks_colour = (0.0, 0.0, 0.0, 0.85)`.
    y_ticks_colour : array_like, optional
        *Y* axis ticks colour array such as
        `y_ticks_colour = (0.0, 0.0, 0.0, 0.85)`.
    x_label_colour : array_like, optional
        *X* axis label colour array such as
        `x_label_colour = (0.0, 0.0, 0.0, 0.85)`.
    y_label_colour : array_like, optional
        *Y* axis label colour array such as
        `y_label_colour = (0.0, 0.0, 0.0, 0.85)`.
    ticks_and_label_location : array_like, optional
        Location of the *X* and *Y* axis ticks and labels such as
        `ticks_and_label_location = ('-x', '-y')`.

    Returns
    -------
    tuple
        Grid quads, faces colours, edges colours.

    Examples
    --------
    >>> nadir_grid(segments=1)
    (array([[[-1.   , -1.   ,  0.   ],
            [ 1.   , -1.   ,  0.   ],
            [ 1.   ,  1.   ,  0.   ],
            [-1.   ,  1.   ,  0.   ]],
    <BLANKLINE>
           [[-1.   , -1.   ,  0.   ],
            [ 0.   , -1.   ,  0.   ],
            [ 0.   ,  0.   ,  0.   ],
            [-1.   ,  0.   ,  0.   ]],
    <BLANKLINE>
           [[-1.   ,  0.   ,  0.   ],
            [ 0.   ,  0.   ,  0.   ],
            [ 0.   ,  1.   ,  0.   ],
            [-1.   ,  1.   ,  0.   ]],
    <BLANKLINE>
           [[ 0.   , -1.   ,  0.   ],
            [ 1.   , -1.   ,  0.   ],
            [ 1.   ,  0.   ,  0.   ],
            [ 0.   ,  0.   ,  0.   ]],
    <BLANKLINE>
           [[ 0.   ,  0.   ,  0.   ],
            [ 1.   ,  0.   ,  0.   ],
            [ 1.   ,  1.   ,  0.   ],
            [ 0.   ,  1.   ,  0.   ]],
    <BLANKLINE>
           [[-1.   , -0.001,  0.   ],
            [ 1.   , -0.001,  0.   ],
            [ 1.   ,  0.001,  0.   ],
            [-1.   ,  0.001,  0.   ]],
    <BLANKLINE>
           [[-0.001, -1.   ,  0.   ],
            [ 0.001, -1.   ,  0.   ],
            [ 0.001,  1.   ,  0.   ],
            [-0.001,  1.   ,  0.   ]]]), array([[ 0.25,  0.25,  0.25,  0.1 ],
           [ 0.  ,  0.  ,  0.  ,  0.  ],
           [ 0.  ,  0.  ,  0.  ,  0.  ],
           [ 0.  ,  0.  ,  0.  ,  0.  ],
           [ 0.  ,  0.  ,  0.  ,  0.  ],
           [ 0.  ,  0.  ,  0.  ,  1.  ],
           [ 0.  ,  0.  ,  0.  ,  1.  ]]), array([[ 0.5 ,  0.5 ,  0.5 ,  0.5 ],
           [ 0.75,  0.75,  0.75,  0.25],
           [ 0.75,  0.75,  0.75,  0.25],
           [ 0.75,  0.75,  0.75,  0.25],
           [ 0.75,  0.75,  0.75,  0.25],
           [ 0.  ,  0.  ,  0.  ,  1.  ],
           [ 0.  ,  0.  ,  0.  ,  1.  ]]))
    """

    if limits is None:
        limits = np.array([[-1, 1], [-1, 1]])

    if labels is None:
        labels = ('x', 'y')

    extent = np.max(np.abs(limits[..., 1] - limits[..., 0]))

    settings = Structure(
        **{
            'grid_face_colours': (0.25, 0.25, 0.25),
            'grid_edge_colours': (0.50, 0.50, 0.50),
            'grid_face_alpha': 0.1,
            'grid_edge_alpha': 0.5,
            'x_axis_colour': (0.0, 0.0, 0.0, 1.0),
            'y_axis_colour': (0.0, 0.0, 0.0, 1.0),
            'x_ticks_colour': (0.0, 0.0, 0.0, 0.85),
            'y_ticks_colour': (0.0, 0.0, 0.0, 0.85),
            'x_label_colour': (0.0, 0.0, 0.0, 0.85),
            'y_label_colour': (0.0, 0.0, 0.0, 0.85),
            'ticks_and_label_location': ('-x', '-y')
        })
    settings.update(**kwargs)

    # Outer grid.
    quads_g = grid(
        origin=(-extent / 2, -extent / 2),
        width=extent,
        height=extent,
        height_segments=segments,
        width_segments=segments)

    RGB_g = np.ones((quads_g.shape[0], quads_g.shape[-1]))
    RGB_gf = RGB_g * settings.grid_face_colours
    RGB_gf = np.hstack([
        RGB_gf,
        np.full((RGB_gf.shape[0], 1), settings.grid_face_alpha,
                DEFAULT_FLOAT_DTYPE)
    ])
    RGB_ge = RGB_g * settings.grid_edge_colours
    RGB_ge = np.hstack([
        RGB_ge,
        np.full((RGB_ge.shape[0], 1), settings.grid_edge_alpha,
                DEFAULT_FLOAT_DTYPE)
    ])

    # Inner grid.
    quads_gs = grid(
        origin=(-extent / 2, -extent / 2),
        width=extent,
        height=extent,
        height_segments=segments * 2,
        width_segments=segments * 2)

    RGB_gs = np.ones((quads_gs.shape[0], quads_gs.shape[-1]))
    RGB_gsf = RGB_gs * 0
    RGB_gsf = np.hstack(
        [RGB_gsf,
         np.full((RGB_gsf.shape[0], 1), 0, DEFAULT_FLOAT_DTYPE)])
    RGB_gse = np.clip(RGB_gs * settings.grid_edge_colours * 1.5, 0, 1)
    RGB_gse = np.hstack(
        (RGB_gse,
         np.full((RGB_gse.shape[0], 1), settings.grid_edge_alpha / 2,
                 DEFAULT_FLOAT_DTYPE)))

    # Axis.
    thickness = extent / 1000
    quad_x = grid(
        origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness)
    RGB_x = np.ones((quad_x.shape[0], quad_x.shape[-1] + 1))
    RGB_x = RGB_x * settings.x_axis_colour

    quad_y = grid(
        origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent)
    RGB_y = np.ones((quad_y.shape[0], quad_y.shape[-1] + 1))
    RGB_y = RGB_y * settings.y_axis_colour

    if axes is not None:
        # Ticks.
        x_s = 1 if '+x' in settings.ticks_and_label_location else -1
        y_s = 1 if '+y' in settings.ticks_and_label_location else -1
        for i, axis in enumerate('xy'):
            h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right'
            v_a = 'center'

            ticks = list(sorted(set(quads_g[..., 0, i])))
            ticks += [ticks[-1] + ticks[-1] - ticks[-2]]
            for tick in ticks:
                x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 25)
                     if i else tick)
                y = (tick if i else
                     limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 25))

                tick = DEFAULT_INT_DTYPE(tick) if DEFAULT_FLOAT_DTYPE(
                    tick).is_integer() else tick
                c = settings['{0}_ticks_colour'.format(axis)]

                axes.text(
                    x,
                    y,
                    0,
                    tick,
                    'x',
                    horizontalalignment=h_a,
                    verticalalignment=v_a,
                    color=c,
                    clip_on=True)

        # Labels.
        for i, axis in enumerate('xy'):
            h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right'
            v_a = 'center'

            x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 10)
                 if i else 0)
            y = (0 if i else
                 limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 10))

            c = settings['{0}_label_colour'.format(axis)]

            axes.text(
                x,
                y,
                0,
                labels[i],
                'x',
                horizontalalignment=h_a,
                verticalalignment=v_a,
                color=c,
                size=20,
                clip_on=True)

    quads = np.vstack([quads_g, quads_gs, quad_x, quad_y])
    RGB_f = np.vstack([RGB_gf, RGB_gsf, RGB_x, RGB_y])
    RGB_e = np.vstack([RGB_ge, RGB_gse, RGB_x, RGB_y])

    return quads, RGB_f, RGB_e
Пример #5
0
def read_spectral_data_from_csv_file(path,
                                     delimiter=',',
                                     fields=None,
                                     default=0):
    """
    Reads the spectral data from given *CSV* file in the following form:

    390,  4.15003E-04,  3.68349E-04,  9.54729E-03
    395,  1.05192E-03,  9.58658E-04,  2.38250E-02
    400,  2.40836E-03,  2.26991E-03,  5.66498E-02
    ...
    830,  9.74306E-07,  9.53411E-08,  0.00000

    and returns it as an *OrderedDict* of *dict* as follows:

    OrderedDict([
    ('field', {'wavelength': 'value', ..., 'wavelength': 'value'}),
    ...,
    ('field', {'wavelength': 'value', ..., 'wavelength': 'value'})])

    Parameters
    ----------
    path : unicode
        Absolute *CSV* file path.
    delimiter : unicode, optional
        *CSV* file content delimiter.
    fields : array_like, optional
        *CSV* file spectral data fields names. If no value is provided the
        first line of the file will be used as spectral data fields names.
    default : numeric, optional
        Default value for fields row with missing value.

    Returns
    -------
    OrderedDict
        *CSV* file content.

    Raises
    ------
    RuntimeError
        If the *CSV* spectral data file doesn't define the appropriate fields.

    Notes
    -----
    -   A *CSV* spectral data file should define at least define two fields:
        one for the wavelengths and one for the associated values of one
        spectral distribution.
    -   If no value is provided for the fields names, the first line of the
        file will be used as spectral data fields names.

    Examples
    --------
    >>> import os
    >>> from pprint import pprint
    >>> csv_file = os.path.join(os.path.dirname(__file__), 'tests',
    ...                         'resources', 'colorchecker_n_ohta.csv')
    >>> sds_data = read_spectral_data_from_csv_file(csv_file)
    >>> pprint(list(sds_data.keys()))
    ['1',
     '2',
     '3',
     '4',
     '5',
     '6',
     '7',
     '8',
     '9',
     '10',
     '11',
     '12',
     '13',
     '14',
     '15',
     '16',
     '17',
     '18',
     '19',
     '20',
     '21',
     '22',
     '23',
     '24']
    """

    with open(path, 'rU') as csv_file:
        reader = csv.DictReader(
            csv_file, delimiter=str(delimiter), fieldnames=fields)
        if len(reader.fieldnames) == 1:
            raise RuntimeError(('A "CSV" spectral data file should define '
                                'the following fields: '
                                '("wavelength", "field 1", ..., "field n")!'))

        wavelength = reader.fieldnames[0]
        fields = reader.fieldnames[1:]

        data = OrderedDict(zip(fields, ({} for _ in range(len(fields)))))
        for line in reader:
            for field in fields:
                try:
                    value = DEFAULT_FLOAT_DTYPE(line[field])
                except ValueError:
                    value = default

                data[field][DEFAULT_FLOAT_DTYPE(line[wavelength])] = value
        return data
Пример #6
0
def read_sds_from_xrite_file(path):
    """
    Reads the spectral data from given *X-Rite* file and returns it as an
    *OrderedDict* of :class:`colour.SpectralDistribution` classes.

    Parameters
    ----------
    path : unicode
        Absolute *X-Rite* file path.

    Returns
    -------
    OrderedDict
        :class:`colour.SpectralDistribution` classes of given *X-Rite*
        file.

    Notes
    -----
    -   This parser is minimalistic and absolutely not bullet proof.

    Examples
    --------
    >>> import os
    >>> from pprint import pprint
    >>> xrite_file = os.path.join(os.path.dirname(__file__), 'tests',
    ...                           'resources',
    ...                           'xrite_digital_colour_checker.txt')
    >>> sds_data = read_sds_from_xrite_file(xrite_file)
    >>> pprint(list(sds_data.keys()))  # doctest: +SKIP
    ['X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X9', 'X10']
    """

    with codecs.open(path, encoding=XRITE_FILE_ENCODING) as xrite_file:
        lines = xrite_file.read().strip().split('\n')

        xrite_sds = OrderedDict()
        is_spectral_data_format, is_spectral_data = False, False
        for line in lines:
            line = line.strip()

            if line == 'END_DATA_FORMAT':
                is_spectral_data_format = False

            if line == 'END_DATA':
                is_spectral_data = False

            if is_spectral_data_format:
                wavelengths = [
                    DEFAULT_FLOAT_DTYPE(x)
                    for x in re.findall('nm(\\d+)', line)
                ]
                index = len(wavelengths)

            if is_spectral_data:
                tokens = line.split()
                values = [DEFAULT_FLOAT_DTYPE(x) for x in tokens[-index:]]
                xrite_sds[tokens[1]] = (SpectralDistribution(
                    dict(zip(wavelengths, values)), name=tokens[1]))

            if line == 'BEGIN_DATA_FORMAT':
                is_spectral_data_format = True

            if line == 'BEGIN_DATA':
                is_spectral_data = True

        return xrite_sds