Morphology & connected components

Binary images, such as the result of a threshold, often contain specks, holes, gaps and thin bridges. Morphological operations clean them up by probing the image with a small shape, the structuring element. Afterwards, connected component labeling turns the pixels into separate objects that can be counted and measured.

Input AA drag to paint, hold Shift to erase
Result added removed
Connected components of the result
Structuring element BB the origin is marked

Sets and structuring elements

A binary image is a set AA: the positions of its foreground pixels. The structuring element (SE) BB is a small binary matrix, i.e. a set of offsets around an origin, for example a 3×33 \times 3 square, a cross or a disk. BzB_z denotes BB moved to position zz, and B^\hat B its reflection through the origin (for the symmetric shapes here, B^=B\hat B = B).

Dilation and erosion

A⊕B={ z∣(B^)z∩A≠∅ },A⊖B={ z∣Bz⊆A }A \oplus B = \{\, z \mid (\hat B)_z \cap A \neq \emptyset \,\}, \qquad A \ominus B = \{\, z \mid B_z \subseteq A \,\}

Dilation keeps every position where BB touches the object: objects grow by the shape of BB, small holes and gaps close, and nearby objects merge. Erosion keeps only the positions where BB fits completely inside: objects shrink, and specks and thin lines disappear. The two are dual: eroding the foreground is the same as dilating the background, (A⊖B)c=Ac⊕B^(A \ominus B)^c = A^c \oplus \hat B.

Both are max and min filters: dilation takes the maximum of the image under BB, erosion the minimum. Like the median, they are non-linear. Here pixels outside the image count as background for dilation and do not constrain erosion.

Opening and closing

A∘B=(A⊖B)⊕B,A∙B=(A⊕B)⊖BA \circ B = (A \ominus B) \oplus B, \qquad A \bullet B = (A \oplus B) \ominus B

Opening erodes and then dilates. Everything that survives the erosion grows back, so large objects keep their shape, but whatever BB cannot fit into (specks, thin lines, narrow bridges) is gone. The opening is the union of all copies of BB that fit inside AA. Closing does the opposite: it fills holes, gaps and bays that are smaller than BB without growing the objects. Applying either one a second time changes nothing.

The boundary A−(A⊖B)A - (A \ominus B) keeps the pixels removed by erosion: an outline as thick as the radius of BB.

Connected components

Two foreground pixels belong to the same component if a path of neighboring foreground pixels connects them. With 4-connectivity, neighbors share an edge; with 8-connectivity, diagonal neighbors count too. A diagonal line is one object with 8-connectivity but falls apart into single pixels with 4-connectivity.

The classic two-pass algorithm scans the image row by row. Each foreground pixel gets the smallest label of its already visited neighbors (left and above), or a new label if there is none. When different labels meet, as at the bottom of a U, they are recorded as equivalent in a union-find structure. A second pass replaces every label by the representative of its set. The labels then give each object's area, bounding box or centroid.

Try this