/* * lmap.h -- Datastructure 'lm_t' and operations. * * lm_t is a temporary datastructure, used by function 'processImage()'. * */ #ifndef _LMAP_H_ #define _LMAP_H_ /***************************************************************************** * * lm_t -- "Label Map" data structure * ---- * * This data structure combines the following four logical container * data types: * * 1- Map: label --> comp_t. * Quick access to the comp_t data with counts for the component * given the label of the component. * * 2- Map: label --> label. * Register of equivalence mappings from one component label to * component another label. * * 3- List of free labels. * 4- List of used labels. * These allow iteration over the used labels, * and moving any used label to the free list. * *--- * * Logically, 1 and 2 are MAP (associative array) datastructures: We specify * the label of the four-connected component as the key, and want to retrieve * the data for this label. * * For number 4, the used list, a stack or list isn't sufficient, because * we want to be able to remove any given user label from the used list * (in order then to return it to the free list), which needs random access * into the used list. So number 4 also needs to be a "map". * * In each case, the key into the map is label number of a component. * * *--- * * In our application, the above logical datatypes are used for storing * temporary data while processing the input image line by line. The * maximum possible number of labels needed while computing the new * lineAccumulator contents (for the current line) from the previous * lineAcc is the maximum possible number of different components that * can occur in two successive lines. The latter equals Size_X, the number * of pixels in a horizontal line, as shown in the diagram below. * * +---+---+---+---+---+---+---+ * | X | . | X | . | X | . | X | line i * +---+---+---+---+---+---+---+ * | . | X | . | X | . | X | . | line i+1 * +---+---+---+---+---+---+---+ * * |<-------Size_X pixels----->| . = black pixel (blank) * X = white pixel * * This means that for our application, each of the four logical * datatypes only needs to hold data for at most Size_X different * label values. * * The fastest possible map for integer keys is a simple old-fashioned * array. The very limited range of label values makes the array * implementation suitable. * * Therefore I implemented the MAP datatypes all as a fixed-size array, * where the aray index is the label number. The array size is N = Size_X+1: * label number 0 (which represents the empty label value) is unused, and * therefore the first array element is also unused. * * Since all four logical datatypes use the same label range, and the * data stored by all of them concern the same set of components, it * was possible to combine the four into one single array, as follows: * * index * +-----------+--------+-------+-------+ * 1 | compData | lblEq | prev | next | -- data for label '1' * +-----------+--------+-------+-------+ * 2 | compData | lblEq | prev | next | -- data for label '2' * +-----------+--------+-------+-------+ * 3 | compData | lblEq | prev | next | -- data for label '3' * +-----------+--------+-------+-------+ * ... ... ... ... ... * +-----------+--------+-------+-------+ * N-1 | compData | lblEq | prev | next | -- data for label 'N-1' * +-----------+--------+-------+-------+ * * The array elements are 'structs' (lmElem_t) with the following members: * * comp_t compData -- Current counter values for the component. * This implements logical datatype "1". * * label_t lblEq -- Stores the mapping "label1 --> lblEq", where * label1 is the index of the current array element. * This implements logical datatype "2". * * label_t prev -- Index (pointer) to previous item in free list * or in used list. * * label_t next -- Index (pointer) to previous item in free list * or in used list. * * The fields 'prev' and 'next' implement the free list and the used list, * which are both double-linked lists implemented in a fixed-size array. * The free list being the counterpart of the used list (if a given label * is in one list, it is not in the other, and vice versa), it is possible * and convenient to combine the free-list and used-list implementations, * and in the array implementation used here I have done so. * Both lists are chains of lmElem_t structs, with the array index of the * first list element stored outside the array (in iFreeFirst for the free * list and in iUsedFirst for the used list). * * *--- * * The array elements contain two more members, not yet mentioned above: * * int uf -- Boolean that tells whether the element is in the * used list (uf = 1) or in the free list (uf = 0). * This member is not absolutely necessary and is only * used as an extra check. * * int iLine -- Line number in the image in which the label was * last used. * * The member 'iLine' is important; it is needed for the recycling of * label numbers that are no longer used. As explained in the comments * at the start of "processimage.c", this recycling is necessary to * make it possible to keep the set of label numbers down to a very * limited set (which in turn is necessary to make the array * implementation realistic for the MAP containers). * * The algorithm in "processimage.c" updates the iLine for a label (by a * call to "lm_updateLineNr()") whenever the label is used. The member * iLine therefore always contains the line number in which the label was * last used. Labels not used since line L can therefore be recognized * from the fact that their iLine is less than (or equal to) L. * * After the processing of each complete line, the algorithm in * "processimage.c" calls the function "lm_purgeOldLabels()" to return * old labels that are no longer used to the free list. Parameter to the * function is a threshold line number L (meaning that everything older * than L must be recycled). * * To prevent that "lm_purgeOldLabels()" has to do a SEARCH through the * used list to find all labels with iLine < L, the operations on lm_t * make sure that the used list is always kept SORTED on the line * number iLine, from the newest items (large iLine value) at the * beginning to the oldest items (small iLine value) at the end. * The sort order is maintained by inserting NEW labels (with the * latest iLine values) into the used list at the beginning; and by * implementing the function "lm_updateLineNr()" in such a way that a * label that is updated to a newer (= larger) line number is moved to * the beginning of the used list. * With this sorted used list, the function "lm_purgeOldLabels()" no * longer needs to search; all it has to do is simply to remove items * from the END of the used list (as long as iLine < L). * */ #include "comp.h" /* comp_t */ #include "label.h" /* label_h, LABEL_EMPTY */ /************************************************************************* * Structs ************************************************************************/ /* struct lmElem_t -- * Element of the array lm_t::pArr[]. * lm_t::pArr[k] stores the data for label number 'k'. * * Notes: * - If the label has no equivalence data, then lblEq == LABEL_EMPTY. * - If the label has empty comp_t data, this is represented as * compData.pixels == 0. */ typedef struct lmElem_ { comp_t compData; /* Component data. */ label_t lblEq; /* Mapping to the label 'lblEq' (equivalence). */ int iLine; /* Line number where label used last. */ int uf; /* 0 = free, 1 = used. */ label_t prev; /* Prev label in free/used list. */ label_t next; /* Next label in free/used list. */ } lmElem_t; /* struct lm_t -- * One datastructure for all four logical containers. * * Notes: * - iFreeFirst has value LABEL_EMPTY if the free list is empty. * - iUsedFirst has value LABEL_EMPTY if the used list is empty. */ typedef struct lm_ /*Label Map. */ { label_t N; /* Size of array pArr[]. */ lmElem_t * pArr; label_t iFreeFirst; /* First item in free list. */ label_t iUsedFirst; /* First item in used list. */ label_t iUsedLast; /* Last item in used list. */ } lm_t; /************************************************************************* * Function protoypes ************************************************************************/ /* * lm_alloc() -- * * User instantiates an lm_t instance, then calls lm_alloc to * allocate the internal members of that lm_t instance. * * Parameter: * in_nLabels = the number of different labels that the map must store. * * Returns 0 on failure, 1 on success. */ int lm_alloc( lm_t * pLM, int in_nLabels ); /* * lm_free() -- * Deallocate the internal members of the fl_t instance. */ void lm_free( lm_t * pLM ); /* lm_clearDataOfLabel() -- * Clear compData, lblEq, and iLine of the label 'lbl'. */ void lm_clearDataOfLabel( lm_t * pLM, label_t lbl ); /* lm_init() -- * Initialize lm_t instance for beginning with a new image: * - Each label's comp_t data is made empty * - Each label has empty equivalence (i.e. maps to no other label) * - All labels in the free list; used list empty. */ void lm_init( lm_t * pLM ); /* lm_getFreeLabel -- * Take the first free label, * remove it from the free list and insert it into the used list. * Return this label. */ label_t lm_getFreeLabel( lm_t * pLM ); /* lm_returnLabel() -- * Remove the label 'i1' from the used list, * and insert it into the free list. */ void lm_returnLabel( lm_t * pLM, label_t i1 ); /* lm_updateLineNr() -- * Register that label 'lbl' was last used in line number 'in_iLine'. */ void lm_updateLineNr( lm_t * pLM, label_t lbl, int in_iLine ); /* lm_addEquiv() -- * Register the equivalence that label 'from' maps to label 'to'. */ void lm_addEquiv( lm_t * pLM, label_t from, label_t to ); /* lm_getEquiv() -- * Retrieve equivalence. * Returns LABEL_EMPTY if 'from' does not map to any other component. */ label_t lm_getEquiv( lm_t * pLM, label_t from ); /* lm_addPixelToComp() -- * Add to the comp_t of with label 'lbl' the data for the single * pixel at location in_x, in_y. */ void lm_addPixelToComp( lm_t * pLM, label_t lbl, int in_x, int in_y ); /* lm_mergeCompIntoComp() -- * Merge component lblSrc into lblDst (i.e. add the comp_t data for * component lblSrc into the comp_t of component lblDst), * then clear the entry for label 'lblSrc' in the component list. */ void lm_mergeCompIntoComp( lm_t * pLM, label_t lblDst, label_t lblSrc ); /* * Finalize the center-of-mass computation for the component with * label 'lbl', * and then call the usr-defined function 'usrOutComp()' to write * this final comp_t for label 'lbl' to output. */ void lm_outComp( lm_t * pLM, label_t lbl, void (*usrOutComp)( comp_t const * ) ); /* * lm_purgeOldLabels -- * Purge from lm_t all labels that have a line number smaller than in_iY. * * Write the comp_t for the purged labels to the output, using the * user-defined function 'usrOutComp()'. */ void lm_purgeOldLabels( lm_t * pLM, int in_iY, /*Line Number. */ void (*usrOutComp)( comp_t const * ) ); /*Callback fn. */ #endif /*_LMAP_H_ */