/* Reconstruction without compaction */ static PIXA * ReconstructPixa1(L_PTRA *papix, L_PTRA *pabox) { l_int32 i, imax, nactual; BOX *box; PIX *pix; PIXA *pixat; ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "Before removal: imax = %4d, actual = %4d\n", imax, nactual); pixat = pixaCreate(imax + 1); for (i = 0; i <= imax; i++) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); if (pix) pixaAddPix(pixat, pix, L_INSERT); if (box) pixaAddBox(pixat, box, L_INSERT); } ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "After removal: imax = %4d, actual = %4d\n\n", imax, nactual); return pixat; }
/* Reconstruction with compaction */ static PIXA * ReconstructPixa2(L_PTRA *papix, L_PTRA *pabox) { l_int32 i, imax, nactual; BOX *box; PIX *pix; PIXA *pixat; ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "Before removal: imax = %4d, actual = %4d\n", imax, nactual); /* Remove half */ pixat = pixaCreate(imax + 1); for (i = 0; i <= imax; i++) { if (i % 2 == 0) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); if (pix) pixaAddPix(pixat, pix, L_INSERT); if (box) pixaAddBox(pixat, box, L_INSERT); } } /* Compact */ ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "Before compaction: imax = %4d, actual = %4d\n", imax, nactual); ptraCompactArray(papix); ptraCompactArray(pabox); ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "After compaction: imax = %4d, actual = %4d\n", imax, nactual); /* Remove the rest (and test compaction with removal) */ while (1) { ptraGetActualCount(papix, &nactual); if (nactual == 0) break; pix = (PIX *)ptraRemove(papix, 0, L_COMPACTION); box = (BOX *)ptraRemove(pabox, 0, L_COMPACTION); pixaAddPix(pixat, pix, L_INSERT); pixaAddBox(pixat, box, L_INSERT); } ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "After removal: imax = %4d, actual = %4d\n\n", imax, nactual); return pixat; }
/*! * ptraReplace() * * Input: ptra * index (element to be replaced) * item (new generic ptr to a struct; can be null) * freeflag (TRUE to free old item; FALSE to return it) * Return: item (old item, if it exists and is not freed), * or null on error */ void * ptraReplace(L_PTRA *pa, l_int32 index, void *item, l_int32 freeflag) { l_int32 imax; void *olditem; PROCNAME("ptraReplace"); if (!pa) return (void *)ERROR_PTR("pa not defined", procName, NULL); ptraGetMaxIndex(pa, &imax); if (index < 0 || index > imax) return (void *)ERROR_PTR("index not in [0 ... imax]", procName, NULL); olditem = pa->array[index]; pa->array[index] = item; if (!item && olditem) pa->nactual--; else if (item && !olditem) pa->nactual++; if (freeflag == FALSE) return olditem; if (olditem) FREE(olditem); return NULL; }
/*! * ptraReverse() * * Input: ptra * Return: 0 if OK, 1 on error */ l_int32 ptraReverse(L_PTRA *pa) { l_int32 i, imax; PROCNAME("ptraReverse"); if (!pa) return ERROR_INT("pa not defined", procName, 1); ptraGetMaxIndex(pa, &imax); for (i = 0; i < (imax + 1) / 2; i++) ptraSwap(pa, i, imax - i); return 0; }
/*! * ptraRemoveLast() * * Input: ptra * Return: item, or null on error or if the array is empty */ void * ptraRemoveLast(L_PTRA *pa) { l_int32 imax; PROCNAME("ptraRemoveLast"); if (!pa) return (void *) ERROR_PTR("pa not defined", procName, NULL); /* Remove the last item in the array. No compaction is required. */ ptraGetMaxIndex(pa, &imax); if (imax >= 0) return ptraRemove(pa, imax, L_NO_COMPACTION); else /* empty */ return NULL; }
/*! * ptraRemove() * * Input: ptra * index (element to be removed) * flag (L_NO_COMPACTION, L_COMPACTION) * Return: item, or null on error * * Notes: * (1) If flag == L_NO_COMPACTION, this removes the item and * nulls the ptr on the array. If it takes the last item * in the array, pa->n is reduced to the next item. * (2) If flag == L_COMPACTION, this compacts the array for * for all i >= index. It should not be used repeatedly on * large arrays, because compaction is O(n). * (3) The ability to remove without automatic compaction allows * removal with cost O(1). */ void * ptraRemove(L_PTRA *pa, l_int32 index, l_int32 flag) { l_int32 i, imax, fromend, icurrent; void *item; PROCNAME("ptraRemove"); if (!pa) return (void *)ERROR_PTR("pa not defined", procName, NULL); ptraGetMaxIndex(pa, &imax); if (index < 0 || index > imax) return (void *)ERROR_PTR("index not in [0 ... imax]", procName, NULL); item = pa->array[index]; if (item) pa->nactual--; pa->array[index] = NULL; /* If we took the last item, need to reduce pa->n */ fromend = (index == imax); if (fromend) { for (i = index - 1; i >= 0; i--) { if (pa->array[i]) break; } pa->imax = i; imax = i + 1; } /* Compact from index to the end of the array */ if (!fromend && flag == L_COMPACTION) { for (icurrent = index, i = index + 1; i <= imax; i++) { if (pa->array[i]) pa->array[icurrent++] = pa->array[i]; } pa->imax = icurrent - 1; } return item; }
/*! * ptraAdd() * * Input: ptra * item (generic ptr to a struct) * Return: 0 if OK, 1 on error * * Notes: * (1) This adds the element to the next location beyond imax, * which is the largest occupied ptr in the array. This is * what you expect from a stack, where all ptrs up to and * including imax are occupied, but here the occuption of * items in the array is entirely arbitrary. */ l_int32 ptraAdd(L_PTRA *pa, void *item) { l_int32 imax; PROCNAME("ptraAdd"); if (!pa) return ERROR_INT("pa not defined", procName, 1); if (!item) return ERROR_INT("item not defined", procName, 1); ptraGetMaxIndex(pa, &imax); if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) return ERROR_INT("extension failure", procName, 1); pa->array[imax + 1] = (void *) item; pa->imax++; pa->nactual++; return 0; }
/*! * ptraJoin() * * Input: ptra1 (add to this one) * ptra2 (appended to ptra1, and emptied of items; can be null) * Return: 0 if OK, 1 on error */ l_int32 ptraJoin(L_PTRA *pa1, L_PTRA *pa2) { l_int32 i, imax; void *item; PROCNAME("ptraJoin"); if (!pa1) return ERROR_INT("pa1 not defined", procName, 1); if (!pa2) return 0; ptraGetMaxIndex(pa2, &imax); for (i = 0; i <= imax; i++) { item = ptraRemove(pa2, i, L_NO_COMPACTION); ptraAdd(pa1, item); } return 0; }
static void CopyPtras(L_PTRA *papixs, L_PTRA *paboxs, L_PTRA **ppapixd, L_PTRA **ppaboxd) { l_int32 i, imax; BOX *box; PIX *pix; ptraGetMaxIndex(papixs, &imax); *ppapixd = ptraCreate(imax + 1); *ppaboxd = ptraCreate(imax + 1); for (i = 0; i <= imax; i++) { pix = pixCopy(NULL, (PIX *)ptraGetPtrToItem(papixs, i)); box = boxCopy((BOX *)ptraGetPtrToItem(paboxs, i)); ptraAdd(*ppapixd, pix); ptraAdd(*ppaboxd, box); } return; }
/*! * ptraSwap() * * Input: ptra * index1 * index2 * Return: 0 if OK, 1 on error */ l_int32 ptraSwap(L_PTRA *pa, l_int32 index1, l_int32 index2) { l_int32 imax; void *item; PROCNAME("ptraSwap"); if (!pa) return ERROR_INT("pa not defined", procName, 1); if (index1 == index2) return 0; ptraGetMaxIndex(pa, &imax); if (index1 < 0 || index1 > imax || index2 < 0 || index2 > imax) return ERROR_INT("invalid index: not in [0 ... imax]", procName, 1); item = ptraRemove(pa, index1, L_NO_COMPACTION); item = ptraReplace(pa, index2, item, FALSE); ptraInsert(pa, index1, item, L_MIN_DOWNSHIFT); return 0; }
/*! * ptraCompactArray() * * Input: ptra * Return: 0 if OK, 1 on error * * Notes: * (1) This compacts the items on the array, filling any empty ptrs. * (2) This does not change the size of the array of ptrs. */ l_int32 ptraCompactArray(L_PTRA *pa) { l_int32 i, imax, nactual, index; PROCNAME("ptraCompactArray"); if (!pa) return ERROR_INT("pa not defined", procName, 1); ptraGetMaxIndex(pa, &imax); ptraGetActualCount(pa, &nactual); if (imax + 1 == nactual) return 0; /* Compact the array */ for (i = 0, index = 0; i <= imax; i++) { if (pa->array[i]) pa->array[index++] = pa->array[i]; } pa->imax = index - 1; if (nactual != index) L_ERROR("index = %d; != nactual\n", procName, index); return 0; }
/*! * ptraInsert() * * Input: ptra * index (location in ptra to insert new value) * item (generic ptr to a struct; can be null) * shiftflag (L_AUTO_DOWNSHIFT, L_MIN_DOWNSHIFT, L_FULL_DOWNSHIFT) * Return: 0 if OK, 1 on error * * Notes: * (1) This checks first to see if the location is valid, and * then if there is presently an item there. If there is not, * it is simply inserted into that location. * (2) If there is an item at the insert location, items must be * moved down to make room for the insert. In the downward * shift there are three options, given by @shiftflag. * - If @shiftflag == L_AUTO_DOWNSHIFT, a decision is made * whether, in a cascade of items, to downshift a minimum * amount or for all items above @index. The decision is * based on the expectation of finding holes (null ptrs) * between @index and the bottom of the array. * Assuming the holes are distributed uniformly, if 2 or more * holes are expected, we do a minimum shift. * - If @shiftflag == L_MIN_DOWNSHIFT, the downward shifting * cascade of items progresses a minimum amount, until * the first empty slot is reached. This mode requires * some computation before the actual shifting is done. * - If @shiftflag == L_FULL_DOWNSHIFT, a shifting cascade is * performed where pa[i] --> pa[i + 1] for all i >= index. * Then, the item is inserted at pa[index]. * (3) If you are not using L_AUTO_DOWNSHIFT, the rule of thumb is * to use L_FULL_DOWNSHIFT if the array is compacted (each * element points to an item), and to use L_MIN_DOWNSHIFT * if there are a significant number of null pointers. * There is no penalty to using L_MIN_DOWNSHIFT for a * compacted array, however, because the full shift is required * and we don't do the O(n) computation to look for holes. * (4) This should not be used repeatedly on large arrays, * because the function is generally O(n). * (5) However, it can be used repeatedly if we start with an empty * ptr array and insert only once at each location. For example, * you can support an array of Numa, where at each ptr location * you store either 0 or 1 Numa, and the Numa can be added * randomly to the ptr array. */ l_int32 ptraInsert(L_PTRA *pa, l_int32 index, void *item, l_int32 shiftflag) { l_int32 i, ihole, imax; l_float32 nexpected; PROCNAME("ptraInsert"); if (!pa) return ERROR_INT("pa not defined", procName, 1); if (index < 0 || index > pa->nalloc) return ERROR_INT("index not in [0 ... nalloc]", procName, 1); if (shiftflag != L_AUTO_DOWNSHIFT && shiftflag != L_MIN_DOWNSHIFT && shiftflag != L_FULL_DOWNSHIFT) return ERROR_INT("invalid shiftflag", procName, 1); if (item) pa->nactual++; if (index == pa->nalloc) { /* can happen when index == n */ if (ptraExtendArray(pa)) return ERROR_INT("extension failure", procName, 1); } /* We are inserting into a hole or adding to the end of the array. * No existing items are moved. */ ptraGetMaxIndex(pa, &imax); if (pa->array[index] == NULL) { pa->array[index] = item; if (item && index > imax) /* new item put beyond max so far */ pa->imax = index; return 0; } /* We are inserting at the location of an existing item, * forcing the existing item and those below to shift down. * First, extend the array automatically if the last element * (nalloc - 1) is occupied (imax). This may not be necessary * in every situation, but only an anomalous sequence of insertions * into the array would cause extra ptr allocation. */ if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) return ERROR_INT("extension failure", procName, 1); /* If there are no holes, do a full downshift. * Otherwise, if L_AUTO_DOWNSHIFT, use the expected number * of holes between index and n to determine the shift mode */ if (imax + 1 == pa->nactual) { shiftflag = L_FULL_DOWNSHIFT; } else if (shiftflag == L_AUTO_DOWNSHIFT) { if (imax < 10) { shiftflag = L_FULL_DOWNSHIFT; /* no big deal */ } else { nexpected = (l_float32)(imax - pa->nactual) * (l_float32)((imax - index) / imax); shiftflag = (nexpected > 2.0) ? L_MIN_DOWNSHIFT : L_FULL_DOWNSHIFT; } } if (shiftflag == L_MIN_DOWNSHIFT) { /* run down looking for a hole */ for (ihole = index + 1; ihole <= imax; ihole++) { if (pa->array[ihole] == NULL) break; } } else { /* L_FULL_DOWNSHIFT */ ihole = imax + 1; } for (i = ihole; i > index; i--) pa->array[i] = pa->array[i - 1]; pa->array[index] = (void *)item; if (ihole == imax + 1) /* the last item was shifted down */ pa->imax++; return 0; }
int main(int argc, char **argv) { l_int32 i, n, w, h, nactual, imax; BOX *box; BOXA *boxa; PIX *pixs, *pixd, *pix; PIXA *pixas, *pixat, *pixac; L_PTRA *papix, *pabox, *papix2, *pabox2; static char mainName[] = "ptra1_reg"; if (argc != 1) return ERROR_INT(" Syntax: ptra1_reg", mainName, 1); setLeptDebugOK(1); pixac = pixaCreate(0); if ((pixs = pixRead("lucasta.1.300.tif")) == NULL) return ERROR_INT("pixs not made", mainName, 1); pixGetDimensions(pixs, &w, &h, NULL); boxa = pixConnComp(pixs, &pixas, 8); pixDestroy(&pixs); boxaDestroy(&boxa); n = pixaGetCount(pixas); /* Fill ptras with clones and reconstruct */ fprintf(stderr, "Fill with clones and reconstruct\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_CLONE); pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 1); /* Remove every other one for the first half; * with compaction at each removal */ fprintf(stderr, "Remove every other in 1st half, with compaction\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_COPY); for (i = 0; i < n / 2; i++) { if (i % 2 == 0) { pix = (PIX *)ptraRemove(papix, i, L_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_COMPACTION); pixDestroy(&pix); boxDestroy(&box); } } pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 0); /* Remove every other one for the entire set, * but without compaction at each removal */ fprintf(stderr, "Remove every other in 1st half, without & then with compaction\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_COPY); for (i = 0; i < n; i++) { if (i % 2 == 0) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); pixDestroy(&pix); boxDestroy(&box); } } ptraCompactArray(papix); /* now do the compaction */ ptraCompactArray(pabox); pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 0); /* Fill ptras using insert at head, and reconstruct */ fprintf(stderr, "Insert at head and reconstruct\n"); papix = ptraCreate(n); pabox = ptraCreate(n); for (i = 0; i < n; i++) { pix = pixaGetPix(pixas, i, L_CLONE); box = pixaGetBox(pixas, i, L_CLONE); ptraInsert(papix, 0, pix, L_MIN_DOWNSHIFT); ptraInsert(pabox, 0, box, L_FULL_DOWNSHIFT); } pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 1); /* Reverse the arrays by swapping */ fprintf(stderr, "Reverse by swapping\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_CLONE); for (i = 0; i < n / 2; i++) { ptraSwap(papix, i, n - i - 1); ptraSwap(pabox, i, n - i - 1); } ptraCompactArray(papix); /* already compact; shouldn't do anything */ ptraCompactArray(pabox); pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 0); /* Remove at the top of the array and push the hole to the end * by neighbor swapping (!). This is O(n^2), so it's not a * recommended way to copy a ptra. [joke] */ fprintf(stderr, "Remove at top, pushing hole to end by swapping -- O(n^2)\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_CLONE); papix2 = ptraCreate(0); pabox2 = ptraCreate(0); while (1) { ptraGetActualCount(papix, &nactual); if (nactual == 0) break; ptraGetMaxIndex(papix, &imax); pix = (PIX *)ptraRemove(papix, 0, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, 0, L_NO_COMPACTION); ptraAdd(papix2, pix); ptraAdd(pabox2, box); for (i = 1; i <= imax; i++) { ptraSwap(papix, i - 1, i); ptraSwap(pabox, i - 1, i); } } ptraCompactArray(papix); /* should be empty */ ptraCompactArray(pabox); /* ditto */ pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 1); /* nothing there */ pixat = ReconstructPixa(papix2, pabox2, CHOOSE_RECON); ptraDestroy(&papix2, 0, 1); ptraDestroy(&pabox2, 0, 1); DisplayResult(pixac, &pixat, w, h, 0); /* Remove and insert one position above, allowing minimum downshift. * If you specify L_AUTO_DOWNSHIFT, because there is only 1 hole, * it will do a full downshift at each insert. This is a * situation where the heuristic (expected number of holes) * fails to do the optimal thing. */ fprintf(stderr, "Remove and insert one position above (min downshift)\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_CLONE); for (i = 1; i < n; i++) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); ptraInsert(papix, i - 1, pix, L_MIN_DOWNSHIFT); ptraInsert(pabox, i - 1, box, L_MIN_DOWNSHIFT); } pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 1); /* Remove and insert one position above, but this time * forcing a full downshift at each step. */ fprintf(stderr, "Remove and insert one position above (full downshift)\n"); MakePtrasFromPixa(pixas, &papix, &pabox, L_CLONE); for (i = 1; i < n; i++) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); ptraInsert(papix, i - 1, pix, L_AUTO_DOWNSHIFT); ptraInsert(pabox, i - 1, box, L_AUTO_DOWNSHIFT); } /* ptraCompactArray(papix); ptraCompactArray(pabox); */ pixat = ReconstructPixa(papix, pabox, CHOOSE_RECON); ptraDestroy(&papix, 0, 1); ptraDestroy(&pabox, 0, 1); DisplayResult(pixac, &pixat, w, h, 0); pixd = pixaDisplay(pixac, 0, 0); pixDisplay(pixd, 100, 100); pixWrite("/tmp/junkptra1.png", pixd, IFF_PNG); pixDestroy(&pixd); pixaDestroy(&pixac); pixaDestroy(&pixas); return 0; }