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 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        
예제 #3
0
def find_related_clusters(image, min_correlation, **kwargs):
    from spectral.algorithms.spymath import has_nan, NaNValueError

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

    start_centers = None

    for (key, val) in list(kwargs.items()):
        if key == 'start_centers':
            start_centers = normalized(val)
        else:
            raise NameError('Unsupported keyword argument.')

    (nrows, ncols, nbands) = image.shape
    N = nrows * ncols
    image = normalized(image.reshape((N, nbands)))
    clusters = np.zeros((N,), int) - 1
    MAX_CENTERS = 65536
    centers = np.zeros((nbands, MAX_CENTERS))
    num_centers = 0
    if start_centers is not None:
        centers[:, :start_centers.shape[0]] = start_centers.T
        num_centers = start_centers.shape[0]
    else:
        centers[:, 0] = image[0]
        num_centers = 1

    percentage = 0.0
    max_exceeded_warning_printed = False
    for i in range(N):
        match_index = np.argmax(np.matmul(image[i], centers[:, :num_centers]))
        if np.dot(image[i], centers[:, match_index]) < min_correlation:
            if num_centers < MAX_CENTERS:
                clusters[i] = num_centers
                centers[:, num_centers] = image[i]
                num_centers += 1
            else:
                if not max_exceeded_warning_printed:
                    print('Exceeded max number of centers, pixels will be assigned to best existing match. Try with lower coefficient.')
                    max_exceeded_warning_printed = True
                clusters[i] = match_index
        else:
            clusters[i] = match_index

        if float(i)/N >= percentage + 0.2:
            percentage = float(i)/N
            print('\r%d%% completed' % int(percentage * 100), end='\r')

    return (clusters.reshape(nrows, ncols), centers[:, :num_centers].T.copy())
예제 #4
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)
예제 #5
0
def kmeans_cosine(image, nclusters=10, max_iterations=20, **kwargs):
    from spectral.algorithms.spymath import has_nan, NaNValueError

    if has_nan(image):
        raise NaNValueError('Image data contains NaN values.')
    #orig_image = image
    # defaults for kwargs
    start_clusters = None
    compare = None
    iterations = None

    for (key, val) in list(kwargs.items()):
        if key == 'start_clusters':
            start_clusters = val
        elif key == 'compare':
            compare = val
        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 = normalized(image.reshape((N, nbands)))
    #image = image.reshape((N, nbands))
    clusters = np.zeros((N,), int)
    centers = None
    if start_clusters is not None:
        assert (start_clusters.shape[0] == nclusters), 'There must be \
        nclusters clusters in the start_centers array.'
        centers = np.array(normalized(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
        random.seed(4)
        for i in range(nclusters):
            centers[i] = image[random.randrange(N)]

    #show_centers(centers, "Initial centers")
    #raw_input("Press Enter to continue...")

    centers = centers.T
    #distances = np.empty((N, nclusters), float)
    old_centers = np.array(centers)
    clusters = np.zeros((N,), int)
    old_clusters = np.copy(clusters)
    #old_n_changed = 1
    itnum = 1
    while (itnum <= max_iterations):
        try:
            if itnum % 10 == 0:
                print('\rIteration %d...' % itnum, end='')

            # Assign all pixels
            #distances[:] = np.matmul(image, centers)
            clusters[:] = np.argmax(np.matmul(image, centers), 1)

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

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

            if compare and compare(old_clusters, clusters):
                print('done.')
                break
            else:
                n_changed = np.sum(clusters != old_clusters)
                # print(np.abs(n_changed - old_n_changed)/(n_changed + old_n_changed))
                # if np.abs(float(n_changed - old_n_changed))/(n_changed + old_n_changed) > 0.5 and n_changed != old_n_changed:
                #     print(centers)
                #     viewt = imshow(orig_image, classes=clusters.reshape(nrows, ncols))
                #     viewt.set_display_mode('overlay')
                #     viewt.class_alpha = 0.85
                #     old_n_changed = n_changed
                #     # raw_input("Press Enter to continue...")
                if itnum % 10 == 0:
                    print('%d pixels reassigned.' % (n_changed))
                if n_changed == 0:
                    break

            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.T)

    print('kmeans terminated with', len(set(old_clusters.ravel())), \
        'clusters after', itnum - 1, 'iterations.')
    return (old_clusters.reshape(nrows, ncols), centers.T)
예제 #6
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)