Пример #1
0
    def load(self, **kwargs):
        '''Loads entire image into memory in a :class:`spectral.ImageArray`.

        Keyword Arguments:

            `dtype` (numpy.dtype):

                An optional dtype to which the loaded array should be cast.

            `scale` (bool, default True):

                Specifies whether any applicable scale factor should be applied
                to the data after loading.

        :class:`spectral.ImageArray` is derived from both
        :class:`spectral.Image` and :class:`numpy.ndarray` so it supports the
        full :class:`numpy.ndarray` interface.  The returns object will have
        shape `(M,N,B)`, where `M`, `N`, and `B` are the numbers of rows,
        columns, and bands in the image.
        '''
        import spectral
        from spectral.spectral import ImageArray
        from spectral.utilities.python23 import typecode
        from array import array
        import warnings
        from spectral.algorithms.spymath import has_nan, NaNValueWarning

        for k in list(kwargs.keys()):
            if k not in ('dtype', 'scale'):
                raise ValueError('Invalid keyword %s.' % str(k))
        dtype = kwargs.get('dtype', ImageArray.format)
        data = array(typecode('b'))
        self.fid.seek(self.offset)
        data.fromfile(self.fid, self.nrows * self.ncols *
                      self.nbands * self.sample_size)
        npArray = np.fromstring(data.tostring(), dtype=self.dtype)
        if self.interleave == spectral.BIL:
            npArray.shape = (self.nrows, self.nbands, self.ncols)
            npArray = npArray.transpose([0, 2, 1])
        elif self.interleave == spectral.BSQ:
            npArray.shape = (self.nbands, self.nrows, self.ncols)
            npArray = npArray.transpose([1, 2, 0])
        else:
            npArray.shape = (self.nrows, self.ncols, self.nbands)
        npArray = npArray.astype(dtype)
        if self.scale_factor != 1 and kwargs.get('scale', True):
            npArray = npArray / float(self.scale_factor)
        imarray = ImageArray(npArray, self)
        if has_nan(imarray):
            warnings.warn('Image data contains NaN values.', NaNValueWarning)
        return imarray        
Пример #2
0
def kmeans_ndarray(image, nclusters=10, max_iterations=20, **kwargs):
    '''
    Performs iterative clustering using the k-means algorithm.

    Arguments:

        `image` (:class:`numpy.ndarray` or :class:`spectral.Image`):

            The `MxNxB` image on which to perform clustering.

        `nclusters` (int) [default 10]:

            Number of clusters to create.  The number produced may be less than
            `nclusters`.

        `max_iterations` (int) [default 20]:

            Max number of iterations to perform.

    Keyword Arguments:

        `start_clusters` (:class:`numpy.ndarray`) [default None]:

            `nclusters x B` array of initial cluster centers.  If not provided,
            initial cluster centers will be spaced evenly along the diagonal of
            the N-dimensional bounding box of the image data.

        `compare` (callable object) [default None]:

            Optional comparison function. `compare` must be a callable object
            that takes 2 `MxN` :class:`numpy.ndarray` objects as its arguments
            and returns non-zero when clustering is to be terminated. The two
            arguments are the cluster maps for the previous and current cluster
            cycle, respectively.

        `distance` (callable object) [default :func:`~spectral.clustering.L2`]:

            The distance measure to use for comparison. The default is to use
            **L2** (Euclidean) distance. For Manhattan distance, specify
            :func:`~spectral.clustering.L1`.

        `frames` (list) [default None]:

            If this argument is given and is a list object, each intermediate
            cluster map is appended to the list.

    Returns a 2-tuple containing:

        `class_map` (:class:`numpy.ndarray`):

            An `MxN` array whos values are the indices of the cluster for the
            corresponding element of `image`.

        `centers` (:class:`numpy.ndarray`):

            An `nclusters x B` array of cluster centers.

    Iterations are performed until clusters converge (no pixels reassigned
    between iterations), `max_iterations` is reached, or `compare` returns
    nonzero. If :exc:`KeyboardInterrupt` is generated (i.e., CTRL-C pressed)
    while the algorithm is executing, clusters are returned from the previously
    completed iteration.
    '''
    import spectral
    import numpy as np
    from spectral.algorithms.spymath import has_nan, NaNValueError

    if has_nan(image):
        raise NaNValueError('Image data contains NaN values.')

    status = spectral._status
    
    # defaults for kwargs
    start_clusters = None
    compare = None
    distance = L2
    iterations = None

    for (key, val) in list(kwargs.items()):
        if key == 'start_clusters':
            start_clusters = val
        elif key == 'compare':
            compare = val
        elif key == 'distance':
            if val in (L1, 'L1'):
                distance = L1
            elif val in (L2, 'L2'):
                distance = L2
            else:
                raise ValueError('Unrecognized keyword argument.')
        elif key == 'frames':
            if not hasattr(val, 'append'):
                raise TypeError('"frames" keyword argument must have "append"'
                                'attribute.')
            iterations = val
        else:
            raise NameError('Unsupported keyword argument.')

    (nrows, ncols, nbands) = image.shape
    N = nrows * ncols
    image = image.reshape((N, nbands))
    clusters = numpy.zeros((N,), int)
    if start_clusters is not None:
        assert (start_clusters.shape[0] == nclusters), 'There must be \
        nclusters clusters in the startCenters array.'
        centers = numpy.array(start_clusters)
    else:
        print('Initializing clusters along diagonal of N-dimensional bounding box.')
        boxMin = np.amin(image, 0)
        boxMax = np.amax(image, 0)
        delta = (boxMax - boxMin) / (nclusters - 1)
        centers = np.empty((nclusters, nbands), float)
        for i in range(nclusters):
            centers[i] = boxMin + i * delta

    distances = np.empty((N, nclusters), float)
    old_centers = np.array(centers)
    clusters = np.zeros((N,), int)
    old_clusters = np.copy(clusters)
    diffs = np.empty_like(image, dtype=np.float64)
    itnum = 1
    while (itnum <= max_iterations):
        try:
            status.display_percentage('Iteration %d...' % itnum)

            # Assign all pixels
            for i in range(nclusters):
                diffs = np.subtract(image, centers[i], out=diffs)
                if distance == L2:
                    distances[:, i] = np.einsum('ij,ij->i', diffs, diffs)
                else:
                    diffs = np.abs(diffs, out=diffs)
                    distances[:, i] = np.einsum('ij->i', diffs)
            clusters[:] = np.argmin(distances, 1)

            # Update cluster centers
            old_centers[:] = centers
            for i in range(nclusters):
                inds = np.argwhere(clusters == i)[:, 0]
                if len(inds) > 0:
                    centers[i] = np.mean(image[inds], 0, float)

            if iterations is not None:
                iterations.append(clusters.reshape(nrows, ncols))

            if compare and compare(old_clusters, clusters):
                status.end_percentage('done.')
                break
            else:
                nChanged = numpy.sum(clusters != old_clusters)
                if nChanged == 0:
                    status.end_percentage('0 pixels reassigned.')
                    break
                else:
                    status.end_percentage('%d pixels reassigned.' \
                                          % (nChanged))

            old_clusters[:] = clusters
            old_centers[:] = centers
            itnum += 1

        except KeyboardInterrupt:
            print("KeyboardInterrupt: Returning clusters from previous iteration.")
            return (old_clusters.reshape(nrows, ncols), old_centers)

    print('kmeans terminated with', len(set(old_clusters.ravel())), \
        'clusters after', itnum - 1, 'iterations.', file=status)
    return (old_clusters.reshape(nrows, ncols), centers)