Skip to content

Dilation

Status: Stable

documented, exercised by the test suite and/or worked examples, with no known limitations recorded.

Description

Dilation[image, r]

gives the maximum over a (2r+1) x (2r+1) square neighbourhood; Dilation[image, elem] uses the SUPPORT of the matrix elem -- its nonzero positions -- as the neighbourhood. This is flat morphology: the element's values do not enter the maximum, which is what keeps Dilation[img, BoxMatrix[1]] and Dilation[img, 1] the same operation. Padding replicates the border, the same rule the convolutions use, which is what makes Dilation >= image hold at the edges too. A full rectangle is separable for the maximum exactly as for a sum, so it costs kw + kh comparisons rather than kw * kh. A "Bit" image gives a "Bit" image; other types give "Real".

Examples (43)

Every input below was run against the current Mathilda build and its output recorded.

Basic Examples (7)

In[1]:= ImageData[Dilation[Image[{{0., 0., 0.}, {0., 1., 0.}, {0., 0., 0.}}], 1]]
Out[1]= {{1.0, 1.0, 1.0}, {1.0, 1.0, 1.0}, {1.0, 1.0, 1.0}}

In[2]:= disk = Image[Table[N[Boole[(i - 8.5)^2 + (j - 8.5)^2 <= 25]], {i, 1, 16}, {j, 1, 16}], "Real"];

In[3]:= bit = Image[Table[Boole[Mod[i + j, 2] == 0], {i, 1, 8}, {j, 1, 8}]];

In[4]:= Dilation[disk, 1]
Out[4]= -Image-

In[5]:= Dilation[disk, 2]
Out[5]= -Image-

In[6]:= ImageDimensions[Dilation[disk, 2]]
Out[6]= {16, 16}

In[7]:= Dilation[bit, 1]
Out[7]= -Image-

Scope (21)

In[8]:= ImageData[Dilation[Image[{{0, 1, 0, 0}, {0, 0, 0, 0}}], 1], "Bit"]
Out[8]= {{1, 1, 1, 0}, {1, 1, 1, 0}}

In[9]:= ImageType[Dilation[Image[{{0, 1, 0}, {0, 0, 0}}], 1]]
Out[9]= "Bit"

In[10]:= chk = Image[Table[If[Mod[Quotient[i - 1, 2] + Quotient[j - 1, 2], 2] == 0, 0., 1.], {i, 1, 16}, {j, 1, 16}], "Real"];

In[11]:= disk = Image[Table[N[Boole[(i - 8.5)^2 + (j - 8.5)^2 <= 25]], {i, 1, 16}, {j, 1, 16}], "Real"];

In[12]:= ramp = Image[Table[N[(j - 1)/15], {i, 1, 16}, {j, 1, 16}], "Real"];

In[13]:= noise = Image[Table[N[Mod[i*37 + j*17, 101]]/101, {i, 1, 32}, {j, 1, 32}], "Real"];

In[14]:= rgb = Image[Table[{N[i/16], N[j/16], 0.5}, {i, 1, 16}, {j, 1, 16}], "Real"];

In[15]:= byte = Image[Table[Mod[i*13 + j*7, 256], {i, 1, 16}, {j, 1, 16}]];

In[16]:= vol = Image3D[Table[N[Mod[z*7 + y*13 + x*3, 97]]/97, {z, 1, 8}, {y, 1, 10}, {x, 1, 12}], "Real"];

In[17]:= volb = Image3D[Table[N[Boole[x <= 6 && y <= 5]], {z, 1, 8}, {y, 1, 10}, {x, 1, 12}], "Real"];

In[18]:= Dilation[chk, 1]
Out[18]= -Image-

In[19]:= Dilation[ramp, 1]
Out[19]= -Image-

In[20]:= Dilation[noise, 2]
Out[20]= -Image-

In[21]:= Dilation[rgb, 1]
Out[21]= -Image-

In[22]:= Dilation[byte, 1]
Out[22]= -Image-

In[23]:= Dilation[vol, 1]
Out[23]= -Image-

In[24]:= Dilation[volb, 1]
Out[24]= -Image-

In[25]:= Dilation[disk, 3]
Out[25]= -Image-

In[26]:= Dilation[disk, 4]
Out[26]= -Image-

In[27]:= ImageChannels[Dilation[rgb, 1]]
Out[27]= 3

In[28]:= ImageDimensions[Dilation[vol, 2]]
Out[28]= {12, 10, 8}

Applications (4)

In[29]:= disk = Image[Table[N[Boole[(i - 8.5)^2 + (j - 8.5)^2 <= 25]], {i, 1, 16}, {j, 1, 16}], "Real"];

In[30]:= noise = Image[Table[N[Mod[i*37 + j*17, 101]]/101, {i, 1, 32}, {j, 1, 32}], "Real"];

In[31]:= Binarize[Dilation[noise, 1]]
Out[31]= -Image-

In[32]:= EdgeDetect[Dilation[disk, 1]]
Out[32]= -Image-

Properties & Relations (6)

In[33]:= disk = Image[Table[N[Boole[(i - 8.5)^2 + (j - 8.5)^2 <= 25]], {i, 1, 16}, {j, 1, 16}], "Real"];

In[34]:= bit = Image[Table[Boole[Mod[i + j, 2] == 0], {i, 1, 8}, {j, 1, 8}]];

In[35]:= ImageData[Dilation[disk, 0]] === ImageData[disk]
Out[35]= True

In[36]:= ImageDimensions[Dilation[disk, 3]] === ImageDimensions[disk]
Out[36]= True

In[37]:= ImageData[Dilation[Dilation[disk, 1], 1]] === ImageData[Dilation[disk, 2]]
Out[37]= True

In[38]:= Max[Flatten[ImageData[Dilation[bit, 1]]]] <= 1.0
Out[38]= True

Neat Examples (2)

In[39]:= zone = Image[Table[N[(1 + Cos[((i - 16)^2 + (j - 16)^2)/40.])/2], {i, 1, 32}, {j, 1, 32}], "Real"];

In[40]:= Dilation[zone, 2]
Out[40]= -Image-

Applications (3)

In[41]:= ImageData[Dilation[Image[{{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}], 1]]
Out[41]= {{1.0, 1.0, 1.0}, {1.0, 1.0, 1.0}, {1.0, 1.0, 1.0}}

In[42]:= ImageData[Dilation[Image[{{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}, "Bit"], {{0, 1, 0}, {1, 1, 1}, {0, 1, 0}}]]
Out[42]= {{0.0, 1.0, 0.0}, {1.0, 1.0, 1.0}, {0.0, 1.0, 0.0}}

In[43]:= ImageType[Dilation[Image[{{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}, "Bit"], 1]]
Out[43]= "Bit"

Implementation notes

Algorithm. builtin_dilation is morph_builtin(res, MORPH_DILATE, two_pass = false) — the pointwise maximum over a structuring element. Dilation[image, r] uses a (2r+1) × (2r+1) square; Dilation[image, elem] uses the support of the matrix elem — its nonzero positions (morph_support), so the element's values do not enter the maximum. This is flat morphology, which is what keeps Dilation[img, BoxMatrix[1]] and Dilation[img, 1] the same operation. A full rectangle is separable for the maximum exactly as for a sum (max over a rectangle = max over rows of the maxima over columns), so morph_separable does a row pass then a column pass, and each 1-D pass uses the van Herk–Gil-Werman trick: block the line into runs of k, keep a prefix and a suffix running maximum per block, and any width-k window is max(suffix at its start, prefix at its end) — three comparisons per pixel regardless of the radius, which is what makes morphology usable at large r. The fast path is exact (max is associative and idempotent) and a test asserts it agrees bit-for-bit with the naive morph_direct. An arbitrary (non-rectangular) element falls back to morph_direct. Padding replicates the border, the same rule the convolutions use, so Dilation >= image holds at the edges too.

Data structures. Flat double buffers; the element is an unsigned char support mask plus a full flag. The separable path uses padded scratch lines and pre/suf arrays sized to the longer axis plus the kernel. A "Bit" image gives a "Bit" image (image_build_typed, since a max of stored 0/1 values is itself 0/1); every other type gives "Real".

Complexity / limits. Separable full-rectangle: O(width · height · channels) amortised, independent of r. Arbitrary element: O(width · height · channels · |support|). A volume takes the rank-3 path, with an integer radius only (an arbitrary 3-D element is not separable).

Attributes: Protected.

References

See also: Image3D

  • J. Serra, Image Analysis and Mathematical Morphology (Academic Press, 1982).
  • M. van Herk, A fast algorithm for local minimum and maximum filters on rectangular and octagonal kernels, Pattern Recognition Letters 13 (1992) 517-521.
  • J. Gil and M. Werman, Computing 2-D min, median, and max filters, IEEE TPAMI 15 (1993) 504-507.
  • Source: src/imagefilter.c
  • Specification: docs/spec/builtins/image-processing.md
  • Tests: tests/test_image.c

Notes & additional examples

Notes

Dilation[image, r] gives the maximum over a (2r+1) × (2r+1) square; Dilation[image, elem] uses the support (nonzero positions) of the matrix elem. This is flat morphology — the element's values do not enter the maximum — which keeps Dilation[img, BoxMatrix[1]] and Dilation[img, 1] the same operation.

A full rectangle is separable for the maximum, and each 1-D pass runs in three comparisons per pixel by the van Herk–Gil-Werman algorithm, so the cost does not grow with r. Padding replicates the border, so Dilation >= image holds at the edges too. A "Bit" image gives a "Bit" image; other types give "Real". Together with Erosion, Opening and Closing it satisfies Erosion <= Opening <= image <= Closing <= Dilation pointwise everywhere.