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
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())
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)
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)
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)