This page is adapted for Python and differs from the MATLAB page.

download as script · edit page

Stretch a rubber band around a grain and let it snap tight: what you get is the convex hull. A convex grain is its own hull, however large or elongated it is. A bay or inlet leaves a gap between the grain and its hull, so comparing the two isolates lobateness: departure from a convex outline.

This page assumes the direct measurements introduced in Shape Parameters. The shapes below are artificial, chosen so that each measure has something to react to.

import numpy as np
from mtex import *
# import the artificial grain shapes
plottingConvention.default('y↑→x')
grains = mtexdata('testgrains')

# select and smooth a few interesting grains
grains = smoothBoundary(grains['id', [2, 3, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15]], 3)

Boundary smoothing is not optional here. On an unsmoothed square grid every boundary segment is horizontal or vertical. The perimeter then measures the cityblock distance and comes out too long, while the convex hull cuts the corners. The difference between the two would be dominated by the grid. For very small grains even smoothing does not save it, and the numbers below should not be trusted.

hull returns the hulls as grains, so everything that works on grains works on them.

# compute convex hull grains
chGrains = grains.hull()

# plot the original grains
plot(grains, micronbar=False, legend=False)

# and on top of them the convex hull
hold(True)
plot(chGrains.boundary, lineWidth=2, lineColor='r')
text(grains, grains.id)
hold(False)

Where a grain is convex the red line follows its outline; where it is indented the line cuts across. The labels are grain ids used in the table below. Hulls may overlap even when the grains do not. A hull outline no longer separates two grains. The second phase of every hull boundary segment is therefore set to notIndexed.

Four ways to measure an indentation

The relative difference between the perimeter of the grain and that of its hull reacts most strongly to thin, narrow indentations - a crack that does not quite cut the grain in two adds a great deal of perimeter and almost no area. The result, deltaP, is a dimensionless percentage.

deltaP = 100 * (grains.perimeter - chGrains.perimeter) / grains.perimeter

paris, the Percentile Average Relative Indented Surface, compares the same two perimeters relative to the hull and doubles that ratio. It is the conventional name in the literature, and grains.paris() computes it directly.

paris = 200 * (grains.perimeter - chGrains.perimeter) / chGrains.perimeter

Comparing the areas instead of the perimeters reacts to broad, shallow lobes - a big bite out of a grain, which changes the area a lot and the perimeter hardly at all. The result, deltaA, is 100 times one minus the quantity often called solidity, the grain area divided by its hull area.

deltaA = 100 * (chGrains.area - grains.area) / chGrains.area

Since the two react to different kinds of indentation, combining them gives radiusD, a measure that responds to both.

radiusD = np.sqrt(deltaP ** 2 + deltaA ** 2)

Compare the measures

The table makes the values behind the colour maps explicit. A zero in a perimeter-based column means that the outer outline is convex. deltaA is zero only when the grain fills its hull, with no hole. deltaP and deltaA are below 100, whereas paris has no upper bound despite being reported as a percentage.

print(f"{'grainId':>8}{'hasHole':>9}{'deltaP':>9}{'paris':>9}{'deltaA':>9}{'radiusD':>9}")
for k in range(grains.size):
  print(f"{grains.id[k]:>8}{str(bool(grains.hasHole[k])):>9}{deltaP[k]:>9.3f}{grains.paris()[k]:>9.3f}{deltaA[k]:>9.3f}{radiusD[k]:>9.3f}")
 grainId  hasHole   deltaP    paris   deltaA  radiusD
       2     True    0.350    0.703    8.840    8.847
       3     True    0.350    0.703   33.238   33.240
       5    False    0.637    1.283    1.229    1.385
       6    False    0.566    1.138    2.376    2.442
       8    False   22.548   58.225    3.080   22.757
       9    False    2.206    4.512    5.949    6.345
      10    False    4.326    9.042    6.972    8.205
      11    False    0.000    0.000    0.000    0.000
      12    False    8.307   18.119   22.476   23.962
      13    False    1.353    2.744    4.283    4.491
      14    False    1.355    2.748    3.110    3.392
      15    False    8.202   17.870   17.758   19.561

Drawn side by side on the same shapes, the measures separate narrow from broad indentations:

newMtexFigure(layout=[2, 2])
plot(grains, deltaP, micronbar=False)
mtexTitle('deltaP')

nextAxis()
plot(grains, grains.paris(), micronbar=False)
mtexTitle('paris')

nextAxis()
plot(grains, deltaA, micronbar=False)
mtexTitle('deltaA')

nextAxis()
plot(grains, radiusD, micronbar=False)
mtexTitle('radiusD')
mtexColorbar()

The first two maps rank the grains identically, as they must, being the same ratio written two ways - only the scale differs, paris running well beyond deltaP. The third map ranks them differently: the lobed clover scores high on deltaP, all perimeter and almost no missing area, while the two discs with a hole score low on deltaP - as convex as the plain disc - and high on deltaA. radiusD puts both kinds high, which is what combining them is for.

The discs with holes are worth a second look, because they show what these measures are and are not counting. A hole and an inclusion are the same enclosure viewed from opposite sides. It is not an indentation of the outer outline, so the perimeter-based measures ignore it; paris explicitly removes inclusion loops before it measures. The area-based measure cannot ignore it, since the hull contains the hole and the grain does not. If a hole is not what you mean by lobateness, use deltaP or paris; if it is, use deltaA.

Which measure to report otherwise depends on the process being described. A dissolution front eats broad bays and shows up in deltaA; a partly healed fracture is a thin slot and shows up in deltaP.

These values also depend on pixel spacing and boundary smoothing. Use the same smoothBoundary settings for every map being compared, report them with the segmentation and resolution, and state the formula because other software uses names such as convexity and solidity for several different ratios.

References

Next

A convex hull discards every indentation. The Projection Parameters page instead keeps the width of each grain as a function of direction and develops shape-preferred orientation from those widths.

Technical details

MATLAB’s testgrains is a stored map of forty artificial shapes; this port draws its own map of fifteen (a disc, discs with a small and a large hole, an ellipse, a rectangle and a turned one, a clover, a disc with a slit, an L, a triangle, a hexagon and a disc with a bay, in one background grain), so the ids and the table differ from the MATLAB page while the measures rank the same kinds of shape the same way. MATLAB’s table is a printed table here.

Citing this page. This page is part of the documentation of MTEX, a free and open source MATLAB toolbox for analyzing and modeling crystallographic textures. It was written by The MTEX Developers and is published at https://mtex-toolbox.github.io/HullBasedParameters_py.html. If you use MTEX, or reuse text or figures from this page, in your research, please cite

F. Bachmann, R. Hielscher, H. Schaeben: Texture Analysis with MTEX - Free and Open Source Software Toolbox, Solid State Phenomena 160 (2010), 63-68. 10.4028/www.scientific.net/SSP.160.63

BibTeX
@article{bachmann2010mtex,
  author  = {F. Bachmann and R. Hielscher and H. Schaeben},
  title   = {Texture Analysis with MTEX - Free and Open Source Software Toolbox},
  journal = {Solid State Phenomena},
  volume  = {160},
  pages   = {63-68},
  year    = {2010},
  doi     = {10.4028/www.scientific.net/SSP.160.63},
  url     = {https://doi.org/10.4028/www.scientific.net/SSP.160.63}
}

Other papers describing specific MTEX methods are listed under Publications — please cite the one that best fits your application. The MTEX source code is licensed under the GNU General Public License v2.0; the text and figures of this documentation are licensed under CC BY 4.0, which permits reuse — including by automated systems — provided The MTEX Developers and this page are credited.