mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-05 04:13:23 +08:00
228 lines
7.8 KiB
Python
228 lines
7.8 KiB
Python
"""
|
|
Compute grey level co-occurrence matrices (GLCM) and associated
|
|
properties to characterize image textures.
|
|
"""
|
|
|
|
import numpy as np
|
|
import skimage.util
|
|
|
|
|
|
def compute_glcm(image, distances, angles, levels=256, symmetric=False,
|
|
normed=False):
|
|
"""Calculate the grey-level co-occurrence matrix.
|
|
|
|
A grey level co-occurence matrix is a histogram of co-occuring
|
|
greyscale values at a given offset over an image.
|
|
|
|
Parameters
|
|
----------
|
|
image : ndarray
|
|
Input image, which is converted to the uint8 data type.
|
|
distances : array_like
|
|
List of histogram distance offsets.
|
|
angles : array_like
|
|
List of histogram angles in radians.
|
|
levels : int, optional
|
|
The input image should contain integers in [0, levels-1],
|
|
where levels indicate the number of grey-levels counted
|
|
(typically 256 for an 8-bit image). The default is 256.
|
|
symmetric : bool, optional
|
|
If True, the output matrix P is symmetric. This is accomplished
|
|
by ignoring the order of value pairs, so both (i, j) and (j, i)
|
|
are accumulated when (i, j) is encountered. The default is False.
|
|
normed : bool, optional
|
|
If True, normalize the result by dividing by the number of
|
|
possible outcomes. The elements of the resulting matrix sum
|
|
to 1. The default is False.
|
|
|
|
Returns
|
|
-------
|
|
out : ndarray
|
|
The grey-level co-occurrence histogram. The value
|
|
`P[i,j,d,theta]` is the number of times that grey-level `j`
|
|
occurs at a distance `d` and at an angle `theta` from
|
|
grey-level `i`.
|
|
|
|
References
|
|
----------
|
|
.. [1] The GLCM Tutorial Home Page,
|
|
http://www.fp.ucalgary.ca/mhallbey/tutorial.htm
|
|
.. [2] Pattern Recognition Engineering, Morton Nadler & Eric P.
|
|
Smith
|
|
|
|
|
|
Examples
|
|
--------
|
|
Compute 2 GLCMs: One for a 1-pixel offset to the right, and one
|
|
for a 1-pixel offset upwards.
|
|
|
|
>>> image = np.array([[0, 0, 1, 1],
|
|
... [0, 0, 1, 1],
|
|
... [0, 2, 2, 2],
|
|
... [2, 2, 3, 3]], dtype=np.uint8)
|
|
>>> result = compute_glcm(image, [1], [0, np.pi/2], 4)
|
|
>>> result[:, :, 0, 0]
|
|
array([[2, 2, 1, 0],
|
|
[0, 2, 0, 0],
|
|
[0, 0, 3, 1],
|
|
[0, 0, 0, 1]], dtype=uint32)
|
|
>>> result[:, :, 0, 1]
|
|
array([[3, 0, 2, 0],
|
|
[0, 2, 2, 0],
|
|
[0, 0, 1, 2],
|
|
[0, 0, 0, 0]], dtype=uint32)
|
|
|
|
"""
|
|
image = skimage.util.img_as_ubyte(image)
|
|
assert image.ndim == 2
|
|
assert image.min() >= 0
|
|
assert image.max() < levels
|
|
distances = np.asarray(distances)
|
|
angles = np.asarray(angles)
|
|
assert distances.ndim == 1
|
|
assert angles.ndim == 1
|
|
|
|
rows, cols = image.shape
|
|
out = np.zeros((levels, levels, len(distances), len(angles)),
|
|
dtype=np.uint32)
|
|
|
|
for a_idx, angle in enumerate(angles):
|
|
for d_idx, distance in enumerate(distances):
|
|
for r in range(rows):
|
|
for c in range(cols):
|
|
i = image[r, c]
|
|
|
|
# compute the location of the offset pixel
|
|
row = r + int(np.round(np.sin(angle) * distance))
|
|
col = c + int(np.round(np.cos(angle) * distance))
|
|
|
|
# make sure the offset is within bounds
|
|
if row >= 0 and row < rows and \
|
|
col >= 0 and col < cols:
|
|
j = image[row, col]
|
|
|
|
if i >= 0 and i < levels and \
|
|
j >= 0 and j < levels:
|
|
out[i, j, d_idx, a_idx] += 1
|
|
|
|
# make each GLMC symmetric
|
|
if symmetric:
|
|
for d in range(len(distances)):
|
|
for a in range(len(angles)):
|
|
out[:, :, d, a] += out[:, :, d, a].transpose()
|
|
|
|
# normalize each GLMC individually
|
|
if normed:
|
|
out = out.astype(np.float64)
|
|
for d in range(len(distances)):
|
|
for a in range(len(angles)):
|
|
if np.any(out[:, :, d, a]):
|
|
out[:, :, d, a] /= out[:, :, d, a].sum()
|
|
|
|
return out
|
|
|
|
|
|
def compute_glcm_prop(P, prop='contrast'):
|
|
"""Calculate texture properties of a GLCM.
|
|
|
|
Compute a feature of a grey level co-occurance matrix to serve as
|
|
a compact summary of the matrix. The properties are computed as
|
|
follows:
|
|
|
|
- 'contrast': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}(i-j)^2`
|
|
- 'dissimilarity': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}\\left|i-j\\right|`
|
|
- 'homogeneity': :math:`\\sum_{i,j=0}^{levels-1}\\frac{P_{i,j}}{1+(i-j)^2}`
|
|
- 'ASM': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}^2`
|
|
- 'energy': :math:`\\sqrt{ASM}`
|
|
- 'correlation': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}\\left[\\frac{(i-\\mu_i)(j-\\mu_j)}{\\sqrt{(\\sigma_i^2)(\\sigma_j^2)}}\\right]`
|
|
|
|
|
|
Parameters
|
|
----------
|
|
P : ndarray
|
|
Input array. `P` is the grey-level co-occurrence histogram
|
|
for which to compute the specified property. The value
|
|
`P[i,j,d,theta]` is the number of times that grey-level j
|
|
occurs at a distance d and at an angle theta from
|
|
grey-level i.
|
|
prop : {'contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation', 'ASM'}, optional
|
|
The property of the GLCM to compute. The default is 'contrast'.
|
|
|
|
Returns
|
|
-------
|
|
results : ndarray
|
|
2-dimensional array. `results[d, a]` is the property 'prop' for
|
|
the d'th distance and the a'th angle.
|
|
|
|
References
|
|
----------
|
|
.. [1] The GLCM Tutorial Home Page,
|
|
http://www.fp.ucalgary.ca/mhallbey/tutorial.htm
|
|
|
|
Examples
|
|
--------
|
|
Compute the contrast for GLCMs with distances [1, 2] and angles
|
|
[0 degrees, 90 degrees]
|
|
|
|
>>> image = np.array([[0, 0, 1, 1],
|
|
... [0, 0, 1, 1],
|
|
... [0, 2, 2, 2],
|
|
... [2, 2, 3, 3]], dtype=np.uint8)
|
|
>>> g = compute_glcm(image, [1, 2], [0, np.pi/2], 4, normed=True,
|
|
... symmetric=True)
|
|
>>> contrast = compute_glcm_prop(g, 'contrast')
|
|
>>> contrast
|
|
array([[ 0.58333333, 1. ],
|
|
[ 1.25 , 2.75 ]])
|
|
|
|
"""
|
|
|
|
assert P.ndim == 4
|
|
(num_level, num_level2, num_dist, num_angle) = P.shape
|
|
assert num_level == num_level2
|
|
assert num_dist > 0
|
|
assert num_angle > 0
|
|
|
|
# create weights for specified property
|
|
r = range(num_level)
|
|
I, J = np.meshgrid(r, r)
|
|
if prop == 'contrast':
|
|
weights = (I - J) ** 2
|
|
elif prop == 'dissimilarity':
|
|
weights = np.abs(I - J)
|
|
elif prop == 'homogeneity':
|
|
weights = 1. / (1. + (I - J) ** 2)
|
|
elif prop in ['ASM', 'energy', 'correlation']:
|
|
pass
|
|
else:
|
|
raise ValueError('%s is an invalid property' % (prop))
|
|
|
|
# compute property for each GLCM
|
|
results = np.zeros((num_dist, num_angle), dtype=np.float64)
|
|
for d in range(num_dist):
|
|
for a in range(num_angle):
|
|
if prop == 'energy':
|
|
asm = (P[:, :, d, a] ** 2).sum()
|
|
results[d, a] = np.sqrt(asm)
|
|
elif prop == 'ASM':
|
|
results[d, a] = (P[:, :, d, a] ** 2).sum()
|
|
elif prop == 'correlation':
|
|
g = P[:, :, d, a]
|
|
mean_i = (I * g).sum()
|
|
mean_j = (J * g).sum()
|
|
diff_i = I - mean_i
|
|
diff_j = J - mean_j
|
|
std_i = np.sqrt((g * (diff_i) ** 2).sum())
|
|
std_j = np.sqrt((g * (diff_j) ** 2).sum())
|
|
cov = (g * (diff_i * diff_j)).sum()
|
|
if std_i < 1e-15 or std_j < 1e-15:
|
|
corr = 1.
|
|
else:
|
|
corr = cov / (std_i * std_j)
|
|
|
|
results[d, a] = corr
|
|
else:
|
|
results[d, a] = (P[:, :, d, a] * weights).sum()
|
|
|
|
return results
|