mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-28 02:30:57 +08:00
294 lines
10 KiB
Python
294 lines
10 KiB
Python
"""
|
|
Methods to characterize image textures.
|
|
"""
|
|
|
|
import numpy as np
|
|
from .._shared.utils import assert_nD
|
|
from ._texture import _glcm_loop, _local_binary_pattern
|
|
|
|
|
|
def greycomatrix(image, distances, angles, levels=256, symmetric=False,
|
|
normed=False):
|
|
"""Calculate the grey-level co-occurrence matrix.
|
|
|
|
A grey level co-occurrence matrix is a histogram of co-occurring
|
|
greyscale values at a given offset over an image.
|
|
|
|
Parameters
|
|
----------
|
|
image : array_like of uint8
|
|
Integer typed input image. The image will be cast to uint8, so
|
|
the maximum value must be less than 256.
|
|
distances : array_like
|
|
List of pixel pair distance offsets.
|
|
angles : array_like
|
|
List of pixel pair 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 maximum value is
|
|
256.
|
|
symmetric : bool, optional
|
|
If True, the output matrix `P[:, :, d, theta]` 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
|
|
for a given offset. The default is False.
|
|
normed : bool, optional
|
|
If True, normalize each matrix `P[:, :, d, theta]` by dividing
|
|
by the total number of accumulated co-occurrences for the given
|
|
offset. The elements of the resulting matrix sum to 1. The
|
|
default is False.
|
|
|
|
Returns
|
|
-------
|
|
P : 4-D 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`. If `normed` is `False`, the output is of
|
|
type uint32, otherwise it is float64.
|
|
|
|
References
|
|
----------
|
|
.. [1] The GLCM Tutorial Home Page,
|
|
http://www.fp.ucalgary.ca/mhallbey/tutorial.htm
|
|
.. [2] Pattern Recognition Engineering, Morton Nadler & Eric P.
|
|
Smith
|
|
.. [3] Wikipedia, http://en.wikipedia.org/wiki/Co-occurrence_matrix
|
|
|
|
|
|
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 = greycomatrix(image, [1], [0, np.pi/4, np.pi/2, 3*np.pi/4], levels=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([[1, 1, 3, 0],
|
|
[0, 1, 1, 0],
|
|
[0, 0, 0, 2],
|
|
[0, 0, 0, 0]], dtype=uint32)
|
|
>>> result[:, :, 0, 2]
|
|
array([[3, 0, 2, 0],
|
|
[0, 2, 2, 0],
|
|
[0, 0, 1, 2],
|
|
[0, 0, 0, 0]], dtype=uint32)
|
|
>>> result[:, :, 0, 3]
|
|
array([[2, 0, 0, 0],
|
|
[1, 1, 2, 0],
|
|
[0, 0, 2, 1],
|
|
[0, 0, 0, 0]], dtype=uint32)
|
|
|
|
"""
|
|
assert_nD(image, 2)
|
|
assert_nD(distances, 1, 'distances')
|
|
assert_nD(angles, 1, 'angles')
|
|
|
|
assert levels <= 256
|
|
image = np.ascontiguousarray(image)
|
|
assert image.min() >= 0
|
|
assert image.max() < levels
|
|
image = image.astype(np.uint8)
|
|
distances = np.ascontiguousarray(distances, dtype=np.float64)
|
|
angles = np.ascontiguousarray(angles, dtype=np.float64)
|
|
|
|
P = np.zeros((levels, levels, len(distances), len(angles)),
|
|
dtype=np.uint32, order='C')
|
|
|
|
# count co-occurences
|
|
_glcm_loop(image, distances, angles, levels, P)
|
|
|
|
# make each GLMC symmetric
|
|
if symmetric:
|
|
Pt = np.transpose(P, (1, 0, 2, 3))
|
|
P = P + Pt
|
|
|
|
# normalize each GLMC
|
|
if normed:
|
|
P = P.astype(np.float64)
|
|
glcm_sums = np.apply_over_axes(np.sum, P, axes=(0, 1))
|
|
glcm_sums[glcm_sums == 0] = 1
|
|
P /= glcm_sums
|
|
|
|
return P
|
|
|
|
|
|
def greycoprops(P, prop='contrast'):
|
|
"""Calculate texture properties of a GLCM.
|
|
|
|
Compute a feature of a grey level co-occurrence 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}|i-j|`
|
|
- '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 : 2-D 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 = greycomatrix(image, [1, 2], [0, np.pi/2], levels=4,
|
|
... normed=True, symmetric=True)
|
|
>>> contrast = greycoprops(g, 'contrast')
|
|
>>> contrast
|
|
array([[ 0.58333333, 1. ],
|
|
[ 1.25 , 2.75 ]])
|
|
|
|
"""
|
|
assert_nD(P, 4, 'P')
|
|
|
|
(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
|
|
I, J = np.ogrid[0:num_level, 0:num_level]
|
|
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
|
|
if prop == 'energy':
|
|
asm = np.apply_over_axes(np.sum, (P ** 2), axes=(0, 1))[0, 0]
|
|
results = np.sqrt(asm)
|
|
elif prop == 'ASM':
|
|
results = np.apply_over_axes(np.sum, (P ** 2), axes=(0, 1))[0, 0]
|
|
elif prop == 'correlation':
|
|
results = np.zeros((num_dist, num_angle), dtype=np.float64)
|
|
I = np.array(range(num_level)).reshape((num_level, 1, 1, 1))
|
|
J = np.array(range(num_level)).reshape((1, num_level, 1, 1))
|
|
diff_i = I - np.apply_over_axes(np.sum, (I * P), axes=(0, 1))[0, 0]
|
|
diff_j = J - np.apply_over_axes(np.sum, (J * P), axes=(0, 1))[0, 0]
|
|
|
|
std_i = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_i) ** 2),
|
|
axes=(0, 1))[0, 0])
|
|
std_j = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_j) ** 2),
|
|
axes=(0, 1))[0, 0])
|
|
cov = np.apply_over_axes(np.sum, (P * (diff_i * diff_j)),
|
|
axes=(0, 1))[0, 0]
|
|
|
|
# handle the special case of standard deviations near zero
|
|
mask_0 = std_i < 1e-15
|
|
mask_0[std_j < 1e-15] = True
|
|
results[mask_0] = 1
|
|
|
|
# handle the standard case
|
|
mask_1 = mask_0 == False
|
|
results[mask_1] = cov[mask_1] / (std_i[mask_1] * std_j[mask_1])
|
|
elif prop in ['contrast', 'dissimilarity', 'homogeneity']:
|
|
weights = weights.reshape((num_level, num_level, 1, 1))
|
|
results = np.apply_over_axes(np.sum, (P * weights), axes=(0, 1))[0, 0]
|
|
|
|
return results
|
|
|
|
|
|
def local_binary_pattern(image, P, R, method='default'):
|
|
"""Gray scale and rotation invariant LBP (Local Binary Patterns).
|
|
|
|
LBP is an invariant descriptor that can be used for texture classification.
|
|
|
|
Parameters
|
|
----------
|
|
image : (N, M) array
|
|
Graylevel image.
|
|
P : int
|
|
Number of circularly symmetric neighbour set points (quantization of
|
|
the angular space).
|
|
R : float
|
|
Radius of circle (spatial resolution of the operator).
|
|
method : {'default', 'ror', 'uniform', 'var'}
|
|
Method to determine the pattern.
|
|
|
|
* 'default': original local binary pattern which is gray scale but not
|
|
rotation invariant.
|
|
* 'ror': extension of default implementation which is gray scale and
|
|
rotation invariant.
|
|
* 'uniform': improved rotation invariance with uniform patterns and
|
|
finer quantization of the angular space which is gray scale and
|
|
rotation invariant.
|
|
* 'nri_uniform': non rotation-invariant uniform patterns variant
|
|
which is only gray scale invariant [2]_.
|
|
* 'var': rotation invariant variance measures of the contrast of local
|
|
image texture which is rotation but not gray scale invariant.
|
|
|
|
Returns
|
|
-------
|
|
output : (N, M) array
|
|
LBP image.
|
|
|
|
References
|
|
----------
|
|
.. [1] Multiresolution Gray-Scale and Rotation Invariant Texture
|
|
Classification with Local Binary Patterns.
|
|
Timo Ojala, Matti Pietikainen, Topi Maenpaa.
|
|
http://www.rafbis.it/biplab15/images/stories/docenti/Danielriccio/Articoliriferimento/LBP.pdf, 2002.
|
|
.. [2] Face recognition with local binary patterns.
|
|
Timo Ahonen, Abdenour Hadid, Matti Pietikainen,
|
|
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.214.6851,
|
|
2004.
|
|
"""
|
|
assert_nD(image, 2)
|
|
|
|
methods = {
|
|
'default': ord('D'),
|
|
'ror': ord('R'),
|
|
'uniform': ord('U'),
|
|
'nri_uniform': ord('N'),
|
|
'var': ord('V')
|
|
}
|
|
image = np.ascontiguousarray(image, dtype=np.double)
|
|
output = _local_binary_pattern(image, P, R, methods[method.lower()])
|
|
return output
|