Linear time algorithm for finding a picture’s connected components P Thanisch,
B V McNally and A Robin*
An algorithm that identifies the connnected components of a thresholded digitized picture is presented. The time complexity of the algorithm is linear in the number of runs of object pixels in the picture. Keywords: tizrity
image processing,
pattern recognition,
connec-
Given a digitized picture to which some thresholding technique has been applied in order to distinguish the object pixels from the background pixels, the next stage in picture processing may be to identify the disjoint elements of the picture; this is achieved by labelling the groups of connected object pixels. Algorithms for connected-component detection and labelling have been published by Lutz’, Ronse and Devivje? and Samet 3. These algorithms have the disadvantage that the time complexity is a function of the total number of pixels, ie object and background pixels. We present here a connected-component detection algorithm with a worst-case time complexity that is a linear function of the number of object pixels, or (more precisely) of the number of ‘runs’ of object pixels”. In the next section we give the notation and definitions of terminology that are used in subsequent sections. The same picture-processin $ terminology as in the work of Rosenfeld and Kak is used wherever possible. In the third section, the format of the algorithm’s input and output is described and an illustrative example is given. In the fourth section we give an informal description of the connectedcomponent detection algorithm. A pseudo-PASCAL representation of the algorithm is given in Appendix 1. The correctness and complexity proofs associated with the algorithm are given in the fifth and sixth sections respectively. In the seventh section we conclude the paper with some remarks about the performance of the algorithm in comparison with other connectedcomponent detection algorithms. To simplify the presentation of the algorithm, the following assumptions about the picture data are made. Royal Observatory, Blackford Hill, Edinburgh EH9 3HJ, UK *Observatoire de Besan~on, F25000, Besancon, France
7~12
no 4 novpmbpr1984
0262-8856/84/04191-07$03.00
Thresholding and segmentation processes have been applied to the picture to extract the object pixels. Any grey-level information that may be associated with the object pixels is ignored. The picture is scanned from left to right and from bottom to top. Thus we shall occasionally refer to a pixel position in some scan as being in a ‘column’ of the picture. The object pixel data that forms the input to the algorithm has a run length representation4. Within each horizontal scan, the location and extent of each run in the scan is fully defined by specifying the column numbers of the left- and right-hand pixels of the run. A bound on the number of runs in the picture is known in advance. This assumption is only needed to simplify the initialization of the data structures associated with the algorithm. For an excellent discussion component detection problem, Kak’s work4.
of the connectedsee Rosenfeld and
TERMINOLOGY A pixel located at a point P = (x, y) has four horizontal and vertical points (x-
l,y),
ky-
l),
l),
picture neighbours, the
(x,y+ 11, c.r+ I,y)
and four diagonal neighbours, (x- l,y-
of a digital
(x- 1,y+ I),
namely (x+ l,y-- l),
(x+ 1,y+ 1)
Let A and B be two object pixels. A i,s said to touch B if they are horizontal, vertical or diagonal neighbours. ApathfromAtoBisaseriesA = Pi, Ps, . . . . P, =B (m 3 1) of touching object pixels. A and B are said to be connected if there exists a path from A to B. An (n)path is a path lying completely in scans l-n. An (n)object is a maximal set of touching object pixels lying completely in scans l-n such that there are no other object pixels in scans I-n touching any of the pixels in the (n)object and at least aone of the pixels lies in scan n. An (n)run is a series of one or more touching object pixels lying completely in scan n such that there are
@ 1984 Butterworth
& Co. (Publishers)
Ltd
19 1
no other object pixels on scan n touching any of these. Two (@runs R and S belong to the same (n)object if there exists an (n)path from a pixel in R to a pixel in S. An (n)section is a complete set of (n)runs belonging to the same (n)object. In the algorithm, the data structure used to represent the object pixel runs is a PASCAL-style array of records, Run, described in detail in the section after next. We shall often refer to the name of the pointer to the record representing a run as if it were the run itself. Where R is a pointer to the record, Run[R], representing a particular run, the record includes the following cells Run[R] . object: a pointer to the ‘object label’ (see below) associated with the (n)object touched by R’s pixels Run[R] .lhs: the column number of the leftmost pixel in the (n)run, R Run[R] . rhs: the column number of the rightmost pixel in the (n)run, R We extend the definition of ‘touching’ to runs, sections and objects as follows. An (n)run Rt touches an (n- 1)run Rz if Run[R,] .rhs 2 Run[Rz] .lhs - 1 and Run[RJ.lhs G Run[Rz].rhs + 1. An (n)run touches an (n- 1)section if it touches one of the (n- 1)runs in that (n- 1)section. An (n)run touches an (n- 1)object if it touches the (n- 1)section associated with that (n - 1)object. Let Rt and Rz be two (n)runs such that Run[Rt] . lhs < Run[Rz] . lhs. RI and R2 are said to be adjacent in scan n if there is no other (n)run Rs such that Run[R,] . lhs < Run[Rs] . lhs < Run[Rz] . lhs. An (n)merger is an (n)run that touches two or more (n- 1)runs at least two of which are not in the same (n- 1)section. At the start of scan n, each (n- 1)object is associated with a data structure, called an object label, described in detail in the section after next. Object labels are implemented as a PASCAL-style array of records, Obj. As with runs, we shall often refer to the name of a pointer to the record representing an object as if it were the object itself. At the start of scan n, the object label record pointed at by L will include the following information about the (n - 1)object Obj[L] . left: at the start of scan n, for each object label L, Obj[L] . left is the column number of the left-hand side of the leftmost (n- 1)run in object L’s (n - 1)section Obj[L] . newlabel: at the start of scan n, for each object label L, Obj[L] .newlabel = L; if, during is found to touch an scan 12, an (n)merger (n- 1)run associated with L and for one of the other (n- l)objects, say M, that is touched by the (n)merger, Obj[M] . left is to the left of Obj[L] . left then Obj[L] . newlabel : = Obj[M] . newlabel connected component detected on scan n+ 1 is an (n)object that is not touched by any (n+ 1)run. Note that runs of object pixels are well nested. Thus when processing a scan from left to right, if we meet a run belonging to a different object from the previous run, then we shall not encounter another run belonging to the first object until we have processed all the (n)runs belonging to the second object. More formally, the well nested property is defined as follows.
192
For any two object labels, Mt and Ms, let Lt = Obj[Mt] .left and Lz = Obj[Mz] .left. Without loss of generality, let Lt < Ls. Let Rt and Rz be the column numbers of the right-hand sides of the rightmost (n)runs belonging to Ml and Mz respectively. Then either Rt < Lz or Rz < Rt. On scan n, let B = {B,, BP, . . . , B,} be the set ;f = rN- lgbjects which touch (n)runs. Let Nb} be the partition of B generated by the e&ivalenLe relation: ‘Bi and Bj are in the same equivalence class if and only if there is an (n)path from B, to F$.’ For 1 < k < b, let Lh be the leftmost (n- l)object m Nk; we refer to La as the (n)label of the (n- 1)objects in Nk. The (n- 1)objects in Nk are well labelled if for each Bi E Nk, Obj[Bi] . newlabel =Lk.
INPUT AND OUTPUT THE ALGORITHM
DATA
FOR
Input The total number of columns and the total number of runs in the picture are passed to procedure CONNECT as arguments. The input to the algorithm is assumed to be a string of integers in the following format. If a scan has at least one run in it, then it is represented by a string of integers n, k, II, rl, 12, r2, . . ., lk, rk where n is the scan number, k is the number of runs in scan n and, for 1 G i d k, li and r, are the left- and right-hand column numbers respectively of the ith run in the scan. If a group of one or more consecutive scans are ‘empty’, ie they contain no runs, then only the first run in the consecutive group of scans appears in the input. This scan will be represented by the pair of integers {n, 0) where n is the scan number and 0 denotes that there are no runs in the scan. The last scan in the input is always an ‘empty’ scan. 1 The digitized picture in Figure 1 is 20 columns wide and eight scans high. Note that an empty scan, scan 9, is added to the input. Note also that scan 2 does not appear; it is the second scan in a contiguous group of empty scans. In the diagram, the object pixels are shaded. To make the numerical representation of the runs easier for the reader to follow, we parenthesize each pair of left- and right-hand column numbers that represents a run.
Example
I
2
3
4
5
6
7
8
9
IO II
12 13 14 15 16 17 I8
I9 20
Column number
Figure 1.
Picture associated with example 1
image and vision computing
Our representation
of the picture is as follows:
190 3,2,( 4,2,( 5,2, 6,3, 7,2 8, 1, 9,o
( ( ( (
2, 3),( 11, 11) 2, lO),( 13,181 13, 13 ), ( 18, 18) 13, 13 ), ( 15, 16 ), ( 18, 18) 13,13 ), ( 18918) 13, 18)
output
CONNECTED-COMPONENT ALGORITHM
In this section, we give an informal description of CONNECT, the connected-component detection algorithm. A pseudo-PASCAL representation of the algorithm is given in Appendix 1. Note that a ‘return’ statement5 is used in the function TOUCHING. Much image processing software is still being written in FORTRAN, so we have deliberately formulated the representation of the algorithm to facilitate easy translation into FORTRAN.
Data structures
On termination of procedure CONNECT, the integer variable, count, holds the number of connected components in the picture. CONNECT groups all the runs associated with a particular connected component into a linked list. On termination of procedure CONNECT, the ith cell (1 g i d count) in the array Component contains a pointer to the start of a linked list of runs associated with the ith connected component to be detected in the picture. Each node in the linked lists is a record from the array Run. The cells in each such record, say R, contain
The algorithm uses linked lists to represent the runs in a scan. The runs for a scan are assumed to be ordered according to their position in the scan, with the leftmost (n)run occurring first and the rightmost occurring last. Each run is represented by a record, ie a set of cells, in the array Run. Each record contains the five cells l l l
l l l l
the left-hand column number of the run the right-hand column number of the run the scan number of the run a pointer to the next record in the linked list of records representing the runs that make up the object
Having processed the picture in example 1, count will have been set to 3 and the linked lists headed by cells in the array (:omponent will be as in Figure 2. The procedure PRINTLIST prints out the relevant information in these lists. As this operation is straightforward, we do not give a PASCAL-like representation of PRINTLIST here.
Figure 2.
DETECTION
Output from example I
l
l
lhs: the left-hand column number of the (n)run represented by this record rhs: the right-hand column number of the (n)run represented by this record scan: the scan number of the run rnext: a pointer to the record associated with the adjacent (n)run that lies to the right of the (n)run represented by the current record (if the current record represents the rightmost (nIlrun of the scan, then the rnext field points to a ‘dummy’ record, described below; when the (7l)object which this run touches is identified, the (n)run is placed in the output linked list associated with that (n)object and the rnext field is used to point to the next record in this list) object: a pointer to the (n)object touched by this run
We simplify the algorithm by using dummy records as the first and last elements in the linked list of input (7z)runs’. The algorithm also uses a linked list to represent the object labels of the current set of c,n)runs. As with the representation of the (n)runs, a dummy record is used as the list header. Each 0bjec.t label is represented by a record, ie a set of cells, in the array Obj. Each record contains the six cells left: a pointer to the leftmost (rr)run associated with this (7z)object first: a pointer to the end of the linked list of runs f‘rom previous scans which have been found to be connected to this object last: a pointer to the start of the linked list of‘runs in previous scans that are connected to this object newlabel: where M is an object label, at the start of scan n Obj[M] . newlabel = M (if, during scan n, an (n)path is found between M and an object L for which Obj[L] .left is to the left of the Obj[M].left then Obj[M] . newlabel : = Obj[L] . newlabel) obnext: a pointer to the next object label in the linked list (if the present object is the last object, this field contains zero) touch: set to true in scan n if an (n)run is found to touch this (n - 1)object
the abbreviations and We use LAB(p) LEFTMOST(p) as follows. Where p is a pointer to an object label, LAB(p) = Obj[p] .newlabel; where p is a pointer to a run, LAB(p) = Obj[Run[p] .object] . newlabel. Where p is a pointer to a run, LEFTMOST(p) = Obj[Run[p] .object] .left. Where p is a pointer to an object label, LEFTMOST(p) = Obj[p] .left. If two (n- l)runs, Rt and Rs = Run[RJ. rnext, are touched by the same (n)run, then Obj[R,] . object and Obj[Rp]. object are pushed onto a stack. Later, these object label pointers are all popped from the stack as part of a procedure to establish the final (n)labels for the (n- 1)runs and (n)runs. The number of such pointers cannot be greater than the number of columns in the picture, so the maximum size for the stack is known in advance. The stack is thus implemented using an array called Stack in the procedure FINDCOMPONENTS.
from the list and the (n- 1)object is removed returned to the storage pool. Procedure RELABEL finds the (n)labels for the (n- 1)objects. It then works through the list of (n)runs creating new object label records for any (n)runs that were found not to touch (n- 1)objects and putting the final (n)labels into the (n)runs that do touch (n- l)objects. For each object label, the field ‘left’ is assigned the column number of the left-hand side of the leftmost (n)run that touches the (n)object. Procedure MERGESTORE works through the list of object label records looking for (n- 1)objects that have to be merged with other (n- 1)objects. It combines the linked list of runs pointed at by the merged object label record with the linked list of runs pointed at by the new object label. Finally, it removes the record representing the merged object label from the linked list and returns the record to the storage pool.
Informal description CONNECT
CORRECTNESS
of the procedure
On each scan represented in the input, procedure GETSCAN forms a linked list of records representing the (n)runs. The rnext field contains a pointer to the next record in the list. In each such record, the left- and right-hand column numbers for the run and the scan number are inserted. The (n)runs in the input are ordered according to their position in the scan, with the leftmost appearing first and the rightmost appearing last. A dummy record, newrhead, points at the leftmost (n)run and the rightmost (n)run points at another dummy record, rear. The (n- l)runs are held in a similar list. The (n- 1)objects are held in a separate linked list, comprising records from the array Obj. The obnext field of each record contains a pointer to the next record in the list. There is no significance in the position in which the (n- 1)objects apper in this list. A dummy record, objhead, points to the first (n- l)object in the list. The last object in the list contains a zero in its obnext field. Procedure FINDTOUCHES looks for touching (n)runs and (n- 1)runs. When a touch is found, the (n- 1)run’s (n- 1)object is marked as being ‘current’ and the (n)run is marked as being connected to the (n- 1)run’s (n- 1)object. When an (n)run is found to touch two adjacent (n- l)runs, the (n- 1)objects have to be merged. Their object labels are put into array Stack and the newlabel fields are updated by procedure UPDATELABELS so that they point at the leftmost (n- 1)object known to be connected to these (n- 1)objects via an (n)path. Once an (n- 1)run has been processed in this way, it is placed in the output linked list of its (n- 1)object. Procedure FINDCOMPONENTS works through the linked list of (n- l)objects, looking for any (n- 1)objects that were not found to be touched by any (n)runs. If any such (n- 1)object is found, the linked list of runs to which it points constitutes a connected component. The count of connected components is incremented and the list is pointed at by a cell in the array Component. Finally, the object label for
194
PROOF
A full correctness proof would be long and just as difficult for the reader to check as the actual algorithm. Thus, in this section, we restrict the discussion to that part of the algorithm where the correctness is not immediately obvious, namely that on scan n the procedure CONNECT merges the (n- 1)objects correctly. We let B = {B,, BP, . .., B,} be the set of (n- l)objects that make up an (n)object along with the that (n)runs touch these (n- 1)objects. Let be this set of (n- 1)objects B’ = (B1, BP, . . . . B,) arranged ’ sequence such that Obj[BJ . left <‘tbj[Ba+ 1] . left, 1 dada1. Lemma 1 If an (n-
1)object is not nested in any other (n- l)object, then it will be assigned its final (n)label before procedure RELABEL is executed in scan n. Suppose, as a hypothesis to be proven contradictory, that some nonnested (n- 1)object Bj is not relabelled as Bi before RELABEL is executed. Procedure UPDATELABELS will only change the newlabel field of Obj[BJ when an (n)merger touches BJ and an (n- 1)object B, such that LEFTMOST < LEFTMOST( By definition, Br will not be relabelled, so assume thatj 2 2. Without loss of generality, assume that B is the first nonnested (n- 1)object in the sequence t h at has not received the label Bt before procedure RELABEL is executed. Let B,, be the next nonnested (n- 1)object to the left of BJ in the sequence B’. (Such an (n- 1)object must exist, since B1 must be nonnested.) There must be an (n)merger that touches both B,, and Bj, so Obj[BJ.newlabel is assigned Obj[BJ . newlabel in procedure FINDTOUCHES. Since Bj is nonnested, the rightmost (n- 1)run in Bis (n- l)section is to the left of Obj[BJ . left, so Obj[Bh] . newlabel will not be changed again in procedure FINDTOUCHES. But h
image and vision computing
Theorem I Procedure objects well labelled.
RELABEL
leaves the (n-l)-
Proof Lemma 1 showed that nonnested (n- 1)objects are well labelled. It remains to be proven that a nested (n- 1)object will be well labelled. Let Bj be a nested (n- 1)object. Let the nonnested (n- 1)object that ‘contains’ B1 be B,,. By Lemma 1, Bh is assigned the label B1 before RELABEL is executed. There must be an (n)path from BJ to Bh comprising alternating subpaths of (a) pixels in an (n- 1)path through an (n- 1)object and (b) pixels in an (n)merger. Let this path be BJ = Cl, MI, CP, Mz, . . . . Cd-,, M&l, Cd = Bh, where each Ci is an (n - 1)object (1 < i s d) and each There will be M, is an (n)merger (1 s ;
PROOF
In this section, we let t represent the total number of runs of object pixels in the picture and s the number of scans represented in the input. Lemma 2 For any given value oft, s < (2t + 1). Proof In the worst case, there will be at most one run in any scan, and these nonempty scans will be surrounded by empty scans. In our input representation, however, only the first of a contiguous group of empty scans is actually represented in the input. Thus, in the worst case, each alternate scan in the input is empty. The final scan must always be an empty one. Thus the total number of scans cannot exceed 2t + 1. cl Theorem 2 The time CONNECT is O(t).
complexity
of
procedure
Proof The for loop in procedure INITIALIZE is executed t + 3 times. The repeat loop in procedure CONNECT is executed s times; by Lemma 2, s < 2t + 1. For every scan represented in the input, procedures GETSCAN, FINDTOUCHES, FINDCOMPONENTS, RELABEL and MERGESTORE are executed once. To prove the theorem we show that, on each scan n in the input, the running time of each of these procedures is 0( N + M ) where N is the number of (n)runs and M is the number of runs in the previous scan represented in the input. In each scan n represented in the input, the number of iterations of the for loop in GETSCAN is the same as the number of (n)runs. In procedure FINDTOUCHES, the while loop on lines 3-28 is entered once for each (n- 1)run. The while loop in lines 5-l 1 is entered once for each Thus, on scan n, no statement in (n)run. FINDTOUCHES is executed more than 0( N + M ) times.
~012 no 4 nouember I984
In procedure FINDCOMPONENTS, on any scan n the number of (n- 1)objects cannot exceed the number of (n- 1)runs; this puts an upper bound on the number of iterations of the loop in lines 2-14. Next consider procedure RELABEL. The variable mergecount is incremented in FINDTOUCHES every time an (n- 1)run and its right-hand neighbour are found to touch the same (n)run. Thus mergecount is less than the number of (n- 1)runs. This bounds the number of iterations of the loop on lines l-3 in RELABEL. The loop on lines 5-19 is entered once for each (n)run. Finally, consider procedure MElRGESTORE. In scan n, the loop on lines 3-14 is entered once for each (n - 1)object and (n)object. On any scan n, the number of (n)objects cannot exceed the number of (n)runs. Thus, on each scan, the repeat loop in procedure CONNECT executes in timeO(N+M). cl CONCLUDING
REMARKS
Other connected-component1 labelling algorithm; have been yesented by Lutz , Ronse and Devivjer and Samet . In astronomical image processing applications associated with the Cosmos measuring machine project at the Royal Observatory, Edinburgh6, CONNECT executes approximately four times faster than an implementation of Lutz’s algorithm. Where W is the number of background pixels and B is the number of object pixels, the worst-case time complexity of Ronse and Devivjer’s algorithm2 is O(W + B).The average time complexity of Samet’s algorithm3 is O(W + B log B). The worst case for CONNECT occurs when each run comprises a single object pixel, in which case the time complexity is O(B). Note, however, that in some applications the format of the output data from Samet’s and Ronse and Devivjer’s algorithms may be more suitable for subsequent processing. Image thresholding and segmentation will involve the inspection of all the pixels in a picture, so the time required to carry out these processes is dependent on the total number of object and background pixels. In many applications, however, special-purpose image processing equipment is used for these earlier stages and the thresholded segmented data is passed to a conventional computer for subsequent processing. As the running time of our algorithm is independent of the number of background pixels, it will be particularly useful in such applications.
REFERENCES Lutz, R ‘An algorithm for the real time analysis of digitised images’ Comput. J. Vol 23 No 3 (August 1980) pp 262-269 Ronse, C H and Devivjer, P A ‘Real-time detection of connected components in binary pictures’ Philips Res. Rep. R.469 (February 1983) Samet, H ‘Connected component labelling using quadtrees’J. Assoc. Comput. Mach. Vol28 No 3 (July 1981) pp 487-501
195
4 Rosenfeld, A and Rak, A C Dig&z1picture processing Academic Press, New York, USA, 2nd edition (1982) 5 Aho, A V, Hopcroft, J E and Ullman, J D Data structures and algorithms Addison-Wesley, Reading, MA, USA (1983) 6 MacGillivray, H T and Stobie, R S ‘New results from the COSMOS machine’ Vistas Astron. in press
pmcahue
GETSCAN;
( (
1) 2)
read( R, k ); { n = the scan number newrun := newrbead;
(
3)
for i := 1 to k da
( (
4) 5)
and k = the number of (n)mns
produce a linked list of runs }
{
GETRUN( temp ); Run[newmn].mext := tap; nemlm := temp; read( Run[newnm] .lhs, Run[newun] Run[newmn].scan:=n;
[ ;I ( 8) ( 9) (10)
* Run[newmn]
}
.rhs );
:= rear ( point the last run at tbe dummy rear run
.mext
1
end GETSCAN;
1: A PSEUDO-PASCAL REPRESENTATION OF THE ALGORITHM
APPENDIX
totalcolumns,
pmcedureCONNECT( vuRun:
totalruns );
pruccdure
array [ l..totalruns+4] ofrecurd lhs, rhs, object, scan, mat: integer
end vuObj:
anay [ l..totalmns+4] ofrecord left,first,last,newlabel,obnext: integer; istouched: Boolean
end varcomponent: array [ 1.. Stack array Il.. totalcolumns, end .INITIALIZE;
totahuns] of integer;
rrpcst GETSCAN; FINDTOUCHES; FINDCOMPONENTS; RELABEL; MERGESTORE; Run[oldrhead] .mext Run[newrhead] .mcxt until end of input file;
1. .2] of integer
read in the data for this scan ) connect the (n)nms to touching (n-1)runs 1 are any (n-1)objects connected components? ) I give (n) runs their new labels } combine output lists of merged objects ) 1 := Run[newrhead] .mexti := rear t {
:=
fori 1 to count da print “Runs in connected PRINTLIST(
component”,
Component[i]
i
)
al end CONNECT;
newnun := Run[ncwrhead] mergecount := 0;
( 3)
wbik
CO”“t
: = 0;
oldmn
( 5)
wbik
set the connected component
objavail := 1; mnavail 1; for i := totalruns+ do-to 1 do { initialize Obj[i] .obnext := i + 1; Run[i].mext := i+ 1 od GETOBJ( objhead ); GETRUN( oldrhead ); GETRUN( rear ); Runlrcarl .lhs := totalcolumns + 2: Runjrearj .rhs := totalcolumns + 2; Run[oldrhead] .rnext := rear; GETRUN( newhead ); Run[newrhead] .mext := rear;
:=
tally to zero
)
)
<>
:= Run[oldrhead] Run[newnm]
rear do
.mext
.rhs < (Run[oldrun] nemun,
.rhs
) do
oldrun ) the0
Obj[Run]oldnm] .object] .istouched := true; Run[newun] .object := LAB(oldrun);
I .q
fi newrun := Run[ncwrun]
I,:{ (11)
od
(12)
ifTOUCHING(newun,oldrun)
.mcxt
then
(13) (14)
Obj(Run[oldmn] .object] .istouched := hue; Run[nemun] .object := LAB(oldrun);
05)
ifTOUCHING(
newrun, Run[oldrun]
.mext
) then
mergecount : = mcrgecount + 1; Stack[mergecount, l] := Run(oldrun).object; Stack[mergecount, 21 := Run[run(oldrun] .mext] .object; UPDATELABELS( Run[oldmn] .object, Run[Run[oldrun]
.mext]
.object
fi
I::{
(23)
fi Run[oldrhead] .mext := Run[oldrun] .mext; Run(oldmn] .mext := Obj[Run[oldrun] .object]
(24)
Obj[Run[oldmn]
(25)
ifObj[Run[oldrun]
.object]
.first = 0 then
Obj(Run[oldrun]
.object].
first := oldrun
1::; the linked lists
.rnext;
.mext
ifTOUCHING(
( 6)
(IS) {
Run(ol&head]
( 4)
I:;1 pmcedure INITIALIZE;
FINDTOUCHES;
( 1) ( 2)
.object]
.last;
.last := oldrun
fi (28)
od end FINDTOUCHES;
end INITIALIZE;
procedure 1) 2)
GETRUN(
ptr := runavail; Run(ptr] .object end GETRUN;
procedure
ptr );
:= 0;
runavail := Run[runavail] Run[ptr] .mext := 0
.mext;
( 1)
prevobj
( 2)
wbik
FINDCOMPONENTS;
:= objhead;
Obj [prevobj]
.obnext
<>
0 do
objptr := Obj[prevobj] .obnext; LEFTMOST(objpu) := 0; procedure
GETOBJ(
ptr ); ifObj[objptr]
1) 2) 3)
ptr := objavail; Obj [ptr] first := 0; Obj[ptr] .newlabel := ptr; end GETOBJ;
procedure 1)
FREEOBJ(
Obj[ptr] .obnext end FREEOBJ;
196
objavail := Obj[objavail] Obj[ptr] .last := 0; Obj[ptr] .obnext := 0;
the0
prevobj := objptr; Objjlobjptr] .istouched := false else 1; Component[count] := Obj[objptr] .last; Obj[prwobj] .obnext := Obj[objptr] .obnext; FREEOBJ [objptr]
CO”“t:= count+
ptr );
:= objavail;
.istouched
.obnext;
objavail := ptr end FINDCOMPONENTS;
image and vision computing
)
prwcdun ( I)
RELABEL;
for ptr
: = mergecount downto1 do
{ find
the final (n--l)labels
f
( 5)
ifobjpu
{ ;{
&rbj
= LAB(objptr)
then
:= objptr !
; Z?,’ ~UPDATELABELS(
(
Stack[ptr,
11, Stack[ptr,
Run[Obj[objptr] Obj[~B(abjp~)] Obj[prevobj].obncxt FREEOBJfobjptr)
I :I 4)
( 5)
newrun while
:= Run[ncwrhead] newrun
<>
.mext;
{ relabel
the (n)runs
]
(10)
rear do
if Run(newunj.object
1::i = 0 then
{ it’s a new
object
(13) (14)
}
GETOBJ(objptr); Run[newrun] .object := objptr; Ohj[nbjptr] .left := Run[newrun] .lbs; Ohj[ohjptr] .obnext := Obj[objhead] .obnext; Obj[objhead] .obnext := objptr else Run[ncwrun]
.objcct
if LEFTMOST(ne~n) { this is the first I.E~OST(newrlln) fi fi newnin
fi objptr
the lists
.first] .Iast
.rncxt := Obj[LAB(~bjptr)~ := Obj[objptr] .Iast; := Obj[objptrf.ohnext;
1 pt
:= Obj[prevobjj.abnrxt
thr
next
L!PDATELABELS(
objl,
obj2
);
(
1)
if LEFTMOST(obj1)
( ( ( ( (
2) 3) 4) 5) 6)
Obj[objZ] .newlabel := LABjobjl), 0bj[LAB(objZ)].newlabet := I.AB(objl) etse Obj[objl].~ewl=b=l := LAB(nbj2); ~~bjlLAB(~bjI)l .nculabcl := LABfobjP)
< LEFTMOST(nbj2)
then
:= LAB(newnm);
(n)nm
= 0 &en to be added := Run[newnm]
to the object .Ihr;
1
.mext
(
get the next
(n)nm
1
fi end UPDATELABELS;
al cud RELABEI.;
Boohw~function procedwt ( 1) ( 2)
objptr := Obj[objhcad] prevobj objhead;
( 3)
whik
:=
objptr
<>
0 do
1)
{ combine
Obj[objptr]
istouched
:= f&e;
( Run[ptrl].
if and(
.obnext;
merged
objects’
stored
lists
1
Run(ptrl].rhs>
( 2) rehun(tnar) ( 3) fi ( 4)
t 4)
TOUCHING(ptr1,
ptr2
)
MERGESTORE; (
rehlm(false) end TOUCHING;
Ihs < Run[ptrPJ. Run[ptrZJ.lhs
rhs + I ) -
.last;
gnhject in the list
ad endMERGESTORE;
prwxdure
( 7) := Run[newunl
merge
Zt)
I ) thm
I