/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - This software is distributed in the hope that it will be - useful, but with NO WARRANTY OF ANY KIND. - No author or distributor accepts responsibility to anyone for the - consequences of using this software, or for whether it serves any - particular purpose or works at all, unless he or she says so in - writing. Everyone is granted permission to copy, modify and - redistribute this source code, for commercial or non-commercial - purposes, with the following restrictions: (1) the origin of this - source code must not be misrepresented; (2) modified versions must - be plainly marked as such; and (3) this notice may not be removed - or altered from any source or modified source distribution. *====================================================================*/ /* * pts.c * * Pta creation, destruction, copy, clone, empty * PTA *ptaCreate() * void ptaDestroy() * PTA *ptaCopy() * PTA *ptaClone() * l_int32 ptaEmpty() * * Pta array extension * l_int32 ptaAddPt() * l_int32 ptaExtendArrays() * * Pta rearrangements * l_int32 ptaJoin() * PTA *ptaReverse() * PTA *ptaCyclicPerm() * PTA *ptaSort() * PTA *ptaRemoveDuplicates() * * Pta Accessors * l_int32 ptaGetRefcount() * l_int32 ptaChangeRefcount() * l_int32 ptaGetCount() * l_int32 ptaGetPt() * l_int32 ptaGetIPt() * l_int32 ptaGetArrays() * * Ptaa creation, destruction * PTAA *ptaaCreate() * void ptaaDestroy() * * Ptaa array extension * l_int32 ptaaAddPta() * l_int32 ptaaExtendArray() * * Ptaa Accessors * l_int32 ptaaGetCount() * l_int32 ptaaGetPta() * * Ptaa serialized I/O * PTAA *ptaaRead() * PTAA *ptaaReadStream() * l_int32 ptaaWrite() * l_int32 ptaaWriteStream() * * Pta serialized I/O * PTA *ptaRead() * PTA *ptaReadStream() * l_int32 ptaWrite() * l_int32 ptaWriteStream() * * In use * BOX *ptaGetExtent() * PTA *ptaGetInsideBox() * PTA *pixFindCornerPixels() * l_int32 pixPlotAlongPta() * l_int32 ptaContainsPt() * l_int32 ptaTestIntersection() * PTA *ptaTransform() * PTA *ptaSubsample() * l_int32 ptaGetLinearLSF() * PTA *ptaGetPixelsFromPix() * PIX *pixGenerateFromPta() * PTA *ptaGetBoundaryPixels() * PTAA *ptaaGetBoundaryPixels() */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "allheaders.h" static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */ /* Default spreading factor for hashing pts in a plane */ static const l_int32 DEFAULT_SPREADING_FACTOR = 7500; /*---------------------------------------------------------------------* * PTA creation, destruction, copy, clone * *---------------------------------------------------------------------*/ /*! * ptaCreate() * * Input: n (initial array sizes) * Return: pta, or null on error. */ PTA * ptaCreate(l_int32 n) { PTA *pta; PROCNAME("ptaCreate"); if (n <= 0) n = INITIAL_PTR_ARRAYSIZE; if ((pta = (PTA *)CALLOC(1, sizeof(PTA))) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); pta->n = 0; pta->nalloc = n; ptaChangeRefcount(pta, 1); /* sets to 1 */ if ((pta->x = (l_float32 *)CALLOC(n, sizeof(l_float32))) == NULL) return (PTA *)ERROR_PTR("x array not made", procName, NULL); if ((pta->y = (l_float32 *)CALLOC(n, sizeof(l_float32))) == NULL) return (PTA *)ERROR_PTR("y array not made", procName, NULL); return pta; } /*! * ptaDestroy() * * Input: &pta (<to be nulled>) * Return: void * * Note: * - Decrements the ref count and, if 0, destroys the pta. * - Always nulls the input ptr. */ void ptaDestroy(PTA **ppta) { PTA *pta; PROCNAME("ptaDestroy"); if (ppta == NULL) { L_WARNING("ptr address is NULL!", procName); return; } if ((pta = *ppta) == NULL) return; ptaChangeRefcount(pta, -1); if (ptaGetRefcount(pta) <= 0) { FREE(pta->x); FREE(pta->y); FREE(pta); } *ppta = NULL; return; } /*! * ptaCopy() * * Input: pta * Return: copy of pta, or null on error */ PTA * ptaCopy(PTA *pta) { l_int32 i; l_float32 x, y; PTA *npta; PROCNAME("ptaCopy"); if (!pta) return (PTA *)ERROR_PTR("pta not defined", procName, NULL); if ((npta = ptaCreate(pta->nalloc)) == NULL) return (PTA *)ERROR_PTR("npta not made", procName, NULL); for (i = 0; i < pta->n; i++) { ptaGetPt(pta, i, &x, &y); ptaAddPt(npta, x, y); } return npta; } /*! * ptaClone() * * Input: pta * Return: ptr to same pta, or null on error */ PTA * ptaClone(PTA *pta) { PROCNAME("ptaClone"); if (!pta) return (PTA *)ERROR_PTR("pta not defined", procName, NULL); ptaChangeRefcount(pta, 1); return pta; } /*! * ptaEmpty() * * Input: pta * Return: 0 if OK, 1 on error * * Note: this only resets the "n" field, for reuse */ l_int32 ptaEmpty(PTA *pta) { PROCNAME("ptaEmpty"); if (!pta) return ERROR_INT("ptad not defined", procName, 1); pta->n = 0; return 0; } /*---------------------------------------------------------------------* * PTA array extension * *---------------------------------------------------------------------*/ /*! * ptaAddPt() * * Input: pta * x, y * Return: 0 if OK, 1 on error */ l_int32 ptaAddPt(PTA *pta, l_float32 x, l_float32 y) { l_int32 n; PROCNAME("ptaAddPt"); if (!pta) return ERROR_INT("pta not defined", procName, 1); n = pta->n; if (n >= pta->nalloc) ptaExtendArrays(pta); pta->x[n] = x; pta->y[n] = y; pta->n++; return 0; } /*! * ptaExtendArrays() * * Input: pta * Return: 0 if OK; 1 on error */ l_int32 ptaExtendArrays(PTA *pta) { PROCNAME("ptaExtendArrays"); if (!pta) return ERROR_INT("pta not defined", procName, 1); if ((pta->x = (l_float32 *)reallocNew((void **)&pta->x, sizeof(l_float32) * pta->nalloc, 2 * sizeof(l_float32) * pta->nalloc)) == NULL) return ERROR_INT("new x array not returned", procName, 1); if ((pta->y = (l_float32 *)reallocNew((void **)&pta->y, sizeof(l_float32) * pta->nalloc, 2 * sizeof(l_float32) * pta->nalloc)) == NULL) return ERROR_INT("new y array not returned", procName, 1); pta->nalloc = 2 * pta->nalloc; return 0; } /*---------------------------------------------------------------------* * Pta rearrangements * *---------------------------------------------------------------------*/ /*! * ptaJoin() * * Input: ptad (dest pta; add to this one) * ptas (source pta; add from this one) * istart (starting index in ptas) * iend (ending index in ptas; use 0 to cat all) * Return: 0 if OK, 1 on error * * Notes: * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) * (2) iend <= 0 means 'read to the end' */ l_int32 ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend) { l_int32 ns, i, x, y; PROCNAME("ptaJoin"); if (!ptad) return ERROR_INT("ptad not defined", procName, 1); if (!ptas) return ERROR_INT("ptas not defined", procName, 1); ns = ptaGetCount(ptas); if (istart < 0) istart = 0; if (istart >= ns) return ERROR_INT("istart out of bounds", procName, 1); if (iend <= 0) iend = ns - 1; if (iend >= ns) return ERROR_INT("iend out of bounds", procName, 1); if (istart > iend) return ERROR_INT("istart > iend; no pts", procName, 1); for (i = istart; i <= iend; i++) { ptaGetIPt(ptas, i, &x, &y); ptaAddPt(ptad, x, y); } return 0; } /*! * ptaReverse() * * Input: ptas * type (0 for float values; 1 for integer values) * Return: ptad (reversed pta), or null on error */ PTA * ptaReverse(PTA *ptas, l_int32 type) { l_int32 n, i, ix, iy; l_float32 x, y; PTA *ptad; PROCNAME("ptaReverse"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); n = ptaGetCount(ptas); if ((ptad = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("ptad not made", procName, NULL); for (i = n - 1; i >= 0; i--) { if (type == 0) { ptaGetPt(ptas, i, &x, &y); ptaAddPt(ptad, x, y); } else { /* type == 1 */ ptaGetIPt(ptas, i, &ix, &iy); ptaAddPt(ptad, ix, iy); } } return ptad; } /*! * ptaCyclicPerm() * * Input: ptas * xs, ys (start point; must be in ptas) * Return: ptad (cyclic permutation, starting and ending at (xs, ys), * or null on error * * Note: we check to insure that ptas is a closed path where * the first and last points are identical, and the * resulting pta also starts and ends on the same point * (which in this case is (xs, ys). */ PTA * ptaCyclicPerm(PTA *ptas, l_int32 xs, l_int32 ys) { l_int32 n, i, x, y, j, index, state; l_int32 x1, y1, x2, y2; PTA *ptad; PROCNAME("ptaCyclicPerm"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); n = ptaGetCount(ptas); /* verify input data */ ptaGetIPt(ptas, 0, &x1, &y1); ptaGetIPt(ptas, n - 1, &x2, &y2); if (x1 != x2 || y1 != y2) return (PTA *)ERROR_PTR("start and end pts not same", procName, NULL); state = L_NOT_FOUND; for (i = 0; i < n; i++) { ptaGetIPt(ptas, i, &x, &y); if (x == xs && y == ys) { state = L_FOUND; break; } } if (state == L_NOT_FOUND) return (PTA *)ERROR_PTR("start pt not in ptas", procName, NULL); if ((ptad = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("ptad not made", procName, NULL); for (j = 0; j < n - 1; j++) { if (i + j < n - 1) index = i + j; else index = (i + j + 1) % n; ptaGetIPt(ptas, index, &x, &y); ptaAddPt(ptad, x, y); } ptaAddPt(ptad, xs, ys); return ptad; } /*! * ptaSort() * * Input: ptas * sorttype (L_SORT_BY_X, L_SORT_BY_Y) * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) * &naindex (<optional return> index of sorted order into * original array) * Return: ptad (sorted version of ptas), or null on error */ PTA * ptaSort(PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex) { l_int32 i, index, n; l_float32 x, y; PTA *ptad; NUMA *na, *naindex; PROCNAME("ptaSort"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) return (PTA *)ERROR_PTR("invalid sort type", procName, NULL); if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) return (PTA *)ERROR_PTR("invalid sort order", procName, NULL); /* Build up numa of specific data */ n = ptaGetCount(ptas); if ((na = numaCreate(n)) == NULL) return (PTA *)ERROR_PTR("na not made", procName, NULL); for (i = 0; i < n; i++) { ptaGetPt(ptas, i, &x, &y); if (sorttype == L_SORT_BY_X) numaAddNumber(na, x); else numaAddNumber(na, y); } /* Get the sort index for data array */ if ((naindex = numaGetSortIndex(na, sortorder)) == NULL) return (PTA *)ERROR_PTR("naindex not made", procName, NULL); /* Build up sorted pta using sort index */ if ((ptad = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("ptad not made", procName, NULL); for (i = 0; i < n; i++) { numaGetIValue(naindex, i, &index); ptaGetPt(ptas, index, &x, &y); ptaAddPt(ptad, x, y); } if (pnaindex) *pnaindex = naindex; else numaDestroy(&naindex); numaDestroy(&na); return ptad; } /*! * ptaRemoveDuplicates() * * Input: ptas (assumed to be integer values) * factor (should be larger than the largest point value; * use 0 for default) * Return: ptad (with duplicates removed), or null on error */ PTA * ptaRemoveDuplicates(PTA *ptas, l_uint32 factor) { l_int32 nsize, i, j, k, index, n, nvals; l_int32 x, y, xk, yk; l_int32 *ia; PTA *ptad; NUMA *na; NUMAHASH *nahash; PROCNAME("ptaRemoveDuplicates"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); if (factor == 0) factor = DEFAULT_SPREADING_FACTOR; /* Build up numaHash of indices, hashed by a key that is * a large linear combination of x and y values designed to * randomize the key. */ nsize = 5507; /* buckets in hash table; prime */ nahash = numaHashCreate(nsize, 2); n = ptaGetCount(ptas); for (i = 0; i < n; i++) { ptaGetIPt(ptas, i, &x, &y); numaHashAdd(nahash, factor * x + y, (l_float32)i); } if ((ptad = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("ptad not made", procName, NULL); for (i = 0; i < nsize; i++) { na = numaHashGetNuma(nahash, i); if (!na) continue; nvals = numaGetCount(na); /* If more than 1 pt, compare exhaustively with double loop; * otherwise, just enter it. */ if (nvals > 1) { if ((ia = (l_int32 *)CALLOC(nvals, sizeof(l_int32))) == NULL) return (PTA *)ERROR_PTR("ia not made", procName, NULL); for (j = 0; j < nvals; j++) { if (ia[j] == 1) continue; numaGetIValue(na, j, &index); ptaGetIPt(ptas, index, &x, &y); ptaAddPt(ptad, x, y); for (k = j + 1; k < nvals; k++) { if (ia[k] == 1) continue; numaGetIValue(na, k, &index); ptaGetIPt(ptas, index, &xk, &yk); if (x == xk && y == yk) /* duplicate */ ia[k] = 1; } } FREE(ia); } else { numaGetIValue(na, 0, &index); ptaGetIPt(ptas, index, &x, &y); ptaAddPt(ptad, x, y); } numaDestroy(&na); /* the clone */ } numaHashDestroy(&nahash); return ptad; } /*---------------------------------------------------------------------* * Pta accessors * *---------------------------------------------------------------------*/ l_int32 ptaGetRefcount(PTA *pta) { PROCNAME("ptaGetRefcount"); if (!pta) return ERROR_INT("pta not defined", procName, 1); return pta->refcount; } l_int32 ptaChangeRefcount(PTA *pta, l_int32 delta) { PROCNAME("ptaChangeRefcount"); if (!pta) return ERROR_INT("pta not defined", procName, 1); pta->refcount += delta; return 0; } /*! * ptaGetCount() * * Input: pta * Return: count, or 0 if no pta */ l_int32 ptaGetCount(PTA *pta) { PROCNAME("ptaGetCount"); if (!pta) return ERROR_INT("pta not defined", procName, 0); return pta->n; } /*! * ptaGetPt() * * Input: pta * index (into arrays) * &x, &y (<return> float values) * Return: 0 if OK; 1 on error */ l_int32 ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py) { PROCNAME("ptaGetPt"); if (!pta) return ERROR_INT("pta not defined", procName, 1); *px = pta->x[index]; *py = pta->y[index]; return 0; } /*! * ptaGetIPt() * * Input: pta * index (into arrays) * &x, &y (<return> integer values) * Return: 0 if OK; 1 on error */ l_int32 ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py) { PROCNAME("ptaGetIPt"); if (!pta) return ERROR_INT("pta not defined", procName, 1); *px = (l_int32)(pta->x[index] + 0.5); *py = (l_int32)(pta->y[index] + 0.5); return 0; } /*! * ptaGetArrays() * * Input: pta * &nax, &nay (<return> numas of x and y arrays) * Return: 0 if OK; 1 on error or if pta is empty * * Notes: * (1) This copies the internal arrays into new Numas, and returns them. * (2) Manipulates internal arrays in pta and numa directly. */ l_int32 ptaGetArrays(PTA *pta, NUMA **pnax, NUMA **pnay) { l_int32 i, n; NUMA *nax, *nay; PROCNAME("ptaGetArrays"); if (!pta) return ERROR_INT("pta not defined", procName, 1); if (!pnax) return ERROR_INT("&nax not defined", procName, 1); if (!pnay) return ERROR_INT("&nay not defined", procName, 1); *pnax = *pnay = NULL; if ((n = ptaGetCount(pta)) == 0) return ERROR_INT("pta is empty", procName, 1); if ((nax = numaCreate(n)) == NULL) return ERROR_INT("nax not made", procName, 1); *pnax = nax; if ((nay = numaCreate(n)) == NULL) return ERROR_INT("nay not made", procName, 1); *pnay = nay; /* use arrays directly for efficiency */ for (i = 0; i < n; i++) { nax->array[i] = pta->x[i]; nay->array[i] = pta->y[i]; } nax->n = nay->n = n; return 0; } /*---------------------------------------------------------------------* * PTAA creation, destruction * *---------------------------------------------------------------------*/ /*! * ptaaCreate() * * Input: n (initial number of ptrs) * Return: ptaa, or null on error */ PTAA * ptaaCreate(l_int32 n) { PTAA *ptaa; PROCNAME("ptaaCreate"); if (n <= 0) n = INITIAL_PTR_ARRAYSIZE; if ((ptaa = (PTAA *)CALLOC(1, sizeof(PTAA))) == NULL) return (PTAA *)ERROR_PTR("ptaa not made", procName, NULL); ptaa->n = 0; ptaa->nalloc = n; if ((ptaa->pta = (PTA **)CALLOC(n, sizeof(PTA *))) == NULL) return (PTAA *)ERROR_PTR("pta ptrs not made", procName, NULL); return ptaa; } /*! * ptaaDestroy() * * Input: &ptaa <to be nulled> * Return: void */ void ptaaDestroy(PTAA **pptaa) { l_int32 i; PTAA *ptaa; PROCNAME("ptaaDestroy"); if (pptaa == NULL) { L_WARNING("ptr address is NULL!", procName); return; } if ((ptaa = *pptaa) == NULL) return; for (i = 0; i < ptaa->n; i++) ptaDestroy(&ptaa->pta[i]); FREE(ptaa->pta); FREE(ptaa); *pptaa = NULL; return; } /*---------------------------------------------------------------------* * PTAA array extension * *---------------------------------------------------------------------*/ /*! * ptaaAddPta() * * Input: ptaa * pta (to be added) * copyflag (L_INSERT, L_COPY, L_CLONE) * Return: 0 if OK, 1 on error */ l_int32 ptaaAddPta(PTAA *ptaa, PTA *pta, l_int32 copyflag) { l_int32 n; PTA *ptac; PROCNAME("ptaaAddPta"); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 1); if (!pta) return ERROR_INT("pta not defined", procName, 1); if (copyflag == L_INSERT) ptac = pta; else if (copyflag == L_COPY) { if ((ptac = ptaCopy(pta)) == NULL) return ERROR_INT("ptac not made", procName, 1); } else if (copyflag == L_CLONE) { if ((ptac = ptaClone(pta)) == NULL) return ERROR_INT("pta clone not made", procName, 1); } else return ERROR_INT("invalid copyflag", procName, 1); n = ptaaGetCount(ptaa); if (n >= ptaa->nalloc) ptaaExtendArray(ptaa); ptaa->pta[n] = ptac; ptaa->n++; return 0; } /*! * ptaaExtendArray() * * Input: ptaa * Return: 0 if OK, 1 on error */ l_int32 ptaaExtendArray(PTAA *ptaa) { PROCNAME("ptaaExtendArray"); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 1); if ((ptaa->pta = (PTA **)reallocNew((void **)&ptaa->pta, sizeof(PTA *) * ptaa->nalloc, 2 * sizeof(PTA *) * ptaa->nalloc)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); ptaa->nalloc = 2 * ptaa->nalloc; return 0; } /*---------------------------------------------------------------------* * Ptaa accessors * *---------------------------------------------------------------------*/ /*! * ptaaGetCount() * * Input: ptaa * Return: count, or 0 if no ptaa */ l_int32 ptaaGetCount(PTAA *ptaa) { PROCNAME("ptaaGetCount"); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 0); return ptaa->n; } /*! * ptaaGetPta() * * Input: ptaa * index (to the i-th pta) * accessflag (L_COPY or L_CLONE) * Return: pta, or null on error */ PTA * ptaaGetPta(PTAA *ptaa, l_int32 index, l_int32 accessflag) { PROCNAME("ptaaGetPta"); if (!ptaa) return (PTA *)ERROR_PTR("ptaa not defined", procName, NULL); if (index < 0 || index >= ptaa->n) return (PTA *)ERROR_PTR("index not valid", procName, NULL); if (accessflag == L_COPY) return ptaCopy(ptaa->pta[index]); else if (accessflag == L_CLONE) return ptaClone(ptaa->pta[index]); else return (PTA *)ERROR_PTR("invalid accessflag", procName, NULL); } /*---------------------------------------------------------------------* * Ptaa serialized I/O * *---------------------------------------------------------------------*/ /*! * ptaaRead() * * Input: filename * Return: ptaa, or null on error */ PTAA * ptaaRead(const char *filename) { FILE *fp; PTAA *ptaa; PROCNAME("ptaaRead"); if (!filename) return (PTAA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (PTAA *)ERROR_PTR("stream not opened", procName, NULL); if ((ptaa = ptaaReadStream(fp)) == NULL) { fclose(fp); return (PTAA *)ERROR_PTR("ptaa not read", procName, NULL); } fclose(fp); return ptaa; } /*! * ptaaReadStream() * * Input: stream * Return: ptaa, or null on error */ PTAA * ptaaReadStream(FILE *fp) { l_int32 i, n, version; PTA *pta; PTAA *ptaa; PROCNAME("ptaaReadStream"); if (!fp) return (PTAA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nPtaa Version %d\n", &version) != 1) return (PTAA *)ERROR_PTR("not a ptaa file", procName, NULL); if (version != PTA_VERSION_NUMBER) return (PTAA *)ERROR_PTR("invalid ptaa version", procName, NULL); if (fscanf(fp, "Number of Pta = %d\n", &n) != 1) return (PTAA *)ERROR_PTR("not a ptaa file", procName, NULL); if ((ptaa = ptaaCreate(n)) == NULL) return (PTAA *)ERROR_PTR("ptaa not made", procName, NULL); for (i = 0; i < n; i++) { if ((pta = ptaReadStream(fp)) == NULL) return (PTAA *)ERROR_PTR("error reading pta", procName, NULL); ptaaAddPta(ptaa, pta, L_INSERT); } return ptaa; } /*! * ptaaWrite() * * Input: filename * ptaa * type (0 for float values; 1 for integer values) * Return: 0 if OK, 1 on error */ l_int32 ptaaWrite(const char *filename, PTAA *ptaa, l_int32 type) { FILE *fp; PROCNAME("ptaaWrite"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 1); if ((fp = fopen(filename, "w")) == NULL) return ERROR_INT("stream not opened", procName, 1); if (ptaaWriteStream(fp, ptaa, type)) return ERROR_INT("ptaa not written to stream", procName, 1); fclose(fp); return 0; } /*! * ptaaWriteStream() * * Input: stream * ptaa * type (0 for float values; 1 for integer values) * Return: 0 if OK; 1 on error */ l_int32 ptaaWriteStream(FILE *fp, PTAA *ptaa, l_int32 type) { l_int32 i, n; PTA *pta; PROCNAME("ptaaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 1); n = ptaaGetCount(ptaa); fprintf(fp, "\nPtaa Version %d\n", PTA_VERSION_NUMBER); fprintf(fp, "Number of Pta = %d\n", n); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa, i, L_CLONE); ptaWriteStream(fp, pta, type); ptaDestroy(&pta); } return 0; } /*---------------------------------------------------------------------* * Pta serialized I/O * *---------------------------------------------------------------------*/ /*! * ptaRead() * * Input: filename * Return: pta, or null on error */ PTA * ptaRead(const char *filename) { FILE *fp; PTA *pta; PROCNAME("ptaRead"); if (!filename) return (PTA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (PTA *)ERROR_PTR("stream not opened", procName, NULL); if ((pta = ptaReadStream(fp)) == NULL) { fclose(fp); return (PTA *)ERROR_PTR("pta not read", procName, NULL); } fclose(fp); return pta; } /*! * ptaReadStream() * * Input: stream * Return: pta, or null on error */ PTA * ptaReadStream(FILE *fp) { char typestr[128]; l_int32 i, n, ix, iy, type, version; l_float32 x, y; PTA *pta; PROCNAME("ptaReadStream"); if (!fp) return (PTA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\n Pta Version %d\n", &version) != 1) return (PTA *)ERROR_PTR("not a pta file", procName, NULL); if (version != PTA_VERSION_NUMBER) return (PTA *)ERROR_PTR("invalid pta version", procName, NULL); if (fscanf(fp, " Number of pts = %d; format = %s\n", &n, typestr) != 2) return (PTA *)ERROR_PTR("not a pta file", procName, NULL); if (!strcmp(typestr, "float")) type = 0; else /* typestr is "integer" */ type = 1; if ((pta = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); for (i = 0; i < n; i++) { if (type == 0) { /* data is float */ fscanf(fp, " (%f, %f)\n", &x, &y); ptaAddPt(pta, x, y); } else { /* data is integer */ fscanf(fp, " (%d, %d)\n", &ix, &iy); ptaAddPt(pta, ix, iy); } } return pta; } /*! * ptaWrite() * * Input: filename * pta * type (0 for float values; 1 for integer values) * Return: 0 if OK, 1 on error */ l_int32 ptaWrite(const char *filename, PTA *pta, l_int32 type) { FILE *fp; PROCNAME("ptaWrite"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!pta) return ERROR_INT("pta not defined", procName, 1); if ((fp = fopen(filename, "w")) == NULL) return ERROR_INT("stream not opened", procName, 1); if (ptaWriteStream(fp, pta, type)) return ERROR_INT("pta not written to stream", procName, 1); fclose(fp); return 0; } /*! * ptaWriteStream() * * Input: stream * pta * type (0 for float values; 1 for integer values) * Return: 0 if OK; 1 on error */ l_int32 ptaWriteStream(FILE *fp, PTA *pta, l_int32 type) { l_int32 i, n, ix, iy; l_float32 x, y; PROCNAME("ptaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!pta) return ERROR_INT("pta not defined", procName, 1); n = ptaGetCount(pta); fprintf(fp, "\n Pta Version %d\n", PTA_VERSION_NUMBER); if (type == 0) fprintf(fp, " Number of pts = %d; format = float\n", n); else /* type == 1 */ fprintf(fp, " Number of pts = %d; format = integer\n", n); for (i = 0; i < n; i++) { if (type == 0) { /* data is float */ ptaGetPt(pta, i, &x, &y); fprintf(fp, " (%f, %f)\n", x, y); } else { /* data is integer */ ptaGetIPt(pta, i, &ix, &iy); fprintf(fp, " (%d, %d)\n", ix, iy); } } return 0; } /*---------------------------------------------------------------------* * In use * *---------------------------------------------------------------------*/ /*! * ptaGetExtent() * * Input: pta * Return: box, or null on error * * Notes: * (1) Returns a box of minimum size containing pts in pta. */ BOX * ptaGetExtent(PTA *pta) { l_int32 n, i, x, y, minx, maxx, miny, maxy; PROCNAME("ptaGetExtent"); if (!pta) return (BOX *)ERROR_PTR("pta not defined", procName, NULL); minx = 10000000; miny = 10000000; maxx = -10000000; maxy = -10000000; n = ptaGetCount(pta); for (i = 0; i < n; i++) { ptaGetIPt(pta, i, &x, &y); if (x < minx) minx = x; if (x > maxx) maxx = x; if (y < miny) miny = y; if (y > maxy) maxy = y; } return boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); } /*! * ptaGetInsideBox() * * Input: ptas (input pts) * box * Return: ptad (of pts in ptas that are inside the box), * or null on error */ PTA * ptaGetInsideBox(PTA *ptas, BOX *box) { PTA *ptad; l_int32 n, i, contains; l_float32 x, y; PROCNAME("ptaGetInsideBox"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); if (!box) return (PTA *)ERROR_PTR("box not defined", procName, NULL); n = ptaGetCount(ptas); ptad = ptaCreate(0); for (i = 0; i < n; i++) { ptaGetPt(ptas, i, &x, &y); boxContainsPt(box, x, y, &contains); if (contains) ptaAddPt(ptad, x, y); } return ptad; } /*! * pixFindCornerPixels() * * Input: pixs (1 bpp) * Return: pta, or null on error * * Notes: * (1) Finds the 4 corner-most pixels, as defined by a search * inward from each corner, using a 45 degree line. */ PTA * pixFindCornerPixels(PIX *pixs) { l_int32 i, j, x, y, w, h, wpl, mindim, found; l_uint32 *data, *line; PTA *pta; PROCNAME("pixFindCornerPixels"); if (!pixs) return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 1) return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); w = pixGetWidth(pixs); h = pixGetHeight(pixs); mindim = L_MIN(w, h); data = pixGetData(pixs); wpl = pixGetWpl(pixs); if ((pta = ptaCreate(4)) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); for (found = FALSE, i = 0; i < mindim; i++) { for (j = 0; j <= i; j++) { y = i - j; line = data + y * wpl; if (GET_DATA_BIT(line, j)) { ptaAddPt(pta, j, y); found = TRUE; break; } } if (found == TRUE) break; } for (found = FALSE, i = 0; i < mindim; i++) { for (j = 0; j <= i; j++) { y = i - j; line = data + y * wpl; x = w - 1 - j; if (GET_DATA_BIT(line, x)) { ptaAddPt(pta, x, y); found = TRUE; break; } } if (found == TRUE) break; } for (found = FALSE, i = 0; i < mindim; i++) { for (j = 0; j <= i; j++) { y = h - 1 - i + j; line = data + y * wpl; if (GET_DATA_BIT(line, j)) { ptaAddPt(pta, j, y); found = TRUE; break; } } if (found == TRUE) break; } for (found = FALSE, i = 0; i < mindim; i++) { for (j = 0; j <= i; j++) { y = h - 1 - i + j; line = data + y * wpl; x = w - 1 - j; if (GET_DATA_BIT(line, x)) { ptaAddPt(pta, x, y); found = TRUE; break; } } if (found == TRUE) break; } return pta; } /*! * pixPlotAlongPta() * * Input: pixs (any depth) * pta (set of points on which to plot) * outformat (GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_X11, * GPLOT_LATEX) * title (<optional> for plot; can be null) * Return: 0 if OK, 1 on error * * Notes: * (1) We remove any existing colormap and clip the pta to the input pixs. * (2) This is a debugging function, and does not remove temporary * plotting files that it generates. * (3) If the image is RGB, three separate plots are generated. */ l_int32 pixPlotAlongPta(PIX *pixs, PTA *pta, l_int32 outformat, const char *title) { char buffer[128]; char *rtitle, *gtitle, *btitle; static l_int32 count = 0; /* require separate temp files for each call */ l_int32 i, x, y, d, w, h, npts, rval, gval, bval; l_uint32 val; NUMA *na, *nar, *nag, *nab; PIX *pixt; PROCNAME("pixPlotAlongLine"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pta) return ERROR_INT("pta not defined", procName, 1); if (outformat != GPLOT_PNG && outformat != GPLOT_PS && outformat != GPLOT_EPS && outformat != GPLOT_X11 && outformat != GPLOT_LATEX) { L_WARNING("outformat invalid; using GPLOT_PNG", procName); outformat = GPLOT_PNG; } pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); d = pixGetDepth(pixt); w = pixGetWidth(pixt); h = pixGetHeight(pixt); npts = ptaGetCount(pta); if (d == 32) { nar = numaCreate(npts); nag = numaCreate(npts); nab = numaCreate(npts); for (i = 0; i < npts; i++) { ptaGetIPt(pta, i, &x, &y); if (x < 0 || x >= w) continue; if (y < 0 || y >= h) continue; pixGetPixel(pixt, x, y, &val); rval = GET_DATA_BYTE(&val, COLOR_RED); gval = GET_DATA_BYTE(&val, COLOR_GREEN); bval = GET_DATA_BYTE(&val, COLOR_BLUE); numaAddNumber(nar, rval); numaAddNumber(nag, gval); numaAddNumber(nab, bval); } sprintf(buffer, "junkplot.%d", count++); rtitle = stringJoin("Red: ", title); gplotSimple1(nar, outformat, buffer, rtitle); sprintf(buffer, "junkplot.%d", count++); gtitle = stringJoin("Green: ", title); gplotSimple1(nag, outformat, buffer, gtitle); sprintf(buffer, "junkplot.%d", count++); btitle = stringJoin("Blue: ", title); gplotSimple1(nab, outformat, buffer, btitle); numaDestroy(&nar); numaDestroy(&nag); numaDestroy(&nab); FREE(rtitle); FREE(gtitle); FREE(btitle); } else { na = numaCreate(npts); for (i = 0; i < npts; i++) { ptaGetIPt(pta, i, &x, &y); if (x < 0 || x >= w) continue; if (y < 0 || y >= h) continue; pixGetPixel(pixt, x, y, &val); numaAddNumber(na, (l_float32)val); } sprintf(buffer, "junkplot.%d", count++); gplotSimple1(na, outformat, buffer, title); numaDestroy(&na); } pixDestroy(&pixt); return 0; } /*! * ptaContainsPt() * * Input: pta * x, y (point) * Return: 1 if contained, 0 otherwise or on error */ l_int32 ptaContainsPt(PTA *pta, l_int32 x, l_int32 y) { l_int32 i, n, ix, iy; PROCNAME("ptaContainsPt"); if (!pta) return ERROR_INT("pta not defined", procName, 0); n = ptaGetCount(pta); for (i = 0; i < n; i++) { ptaGetIPt(pta, i, &ix, &iy); if (x == ix && y == iy) return 1; } return 0; } /*! * ptaTestIntersection() * * Input: pta1, pta2 * Return: bval which is 1 if they have any elements in common; * 0 otherwise or on error. */ l_int32 ptaTestIntersection(PTA *pta1, PTA *pta2) { l_int32 i, j, n1, n2, x1, y1, x2, y2; PROCNAME("ptaTestIntersection"); if (!pta1) return ERROR_INT("pta1 not defined", procName, 0); if (!pta2) return ERROR_INT("pta2 not defined", procName, 0); n1 = ptaGetCount(pta1); n2 = ptaGetCount(pta2); for (i = 0; i < n1; i++) { ptaGetIPt(pta1, i, &x1, &y1); for (j = 0; j < n2; j++) { ptaGetIPt(pta2, i, &x2, &y2); if (x1 == x2 && y1 == y2) return 1; } } return 0; } /*! * ptaTransform() * * Input: pta * shiftx, shifty * scalex, scaley * Return: pta, or null on error * * Notes: * (1) Shift first, then scale. */ PTA * ptaTransform(PTA *ptas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley) { l_int32 n, i, x, y; PTA *ptad; PROCNAME("ptaTransform"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); n = ptaGetCount(ptas); ptad = ptaCreate(n); for (i = 0; i < n; i++) { ptaGetIPt(ptas, i, &x, &y); x = (l_int32)(scalex * (x + shiftx) + 0.5); y = (l_int32)(scaley * (y + shifty) + 0.5); ptaAddPt(ptad, x, y); } return ptad; } /*! * ptaSubsample() * * Input: ptas * subfactor (subsample factor, >= 1) * Return: ptad (evenly sampled pt values from ptas, or null on error */ PTA * ptaSubsample(PTA *ptas, l_int32 subfactor) { l_int32 n, i; l_float32 x, y; PTA *ptad; PROCNAME("pixSubsample"); if (!ptas) return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); if (subfactor < 1) return (PTA *)ERROR_PTR("subfactor < 1", procName, NULL); ptad = ptaCreate(0); n = ptaGetCount(ptas); for (i = 0; i < n; i++) { if (i % subfactor != 0) continue; ptaGetPt(ptas, i, &x, &y); ptaAddPt(ptad, x, y); } return ptad; } /*! * ptaGetLinearLSF() * * Input: pta * &a (<optional return> slope a of least square fit: y = ax + b) * &b (<optional return> intercept b of least square fit) * &nafit (<optional return> numa of least square fit) * Return: 0 if OK, 1 on error * * Notes: * (1) At least one of: &a and &b must not be null. * (2) If both &a and &b are defined, this returns a and b that minimize: * * sum (yi - axi -b)^2 * i * * The method is simple: differentiate this expression w/rt a and b, * and solve the resulting two equations for a and b in terms of * various sums over the input data (xi, yi). * (3) We also allow two special cases, where either a = 0 or b = 0: * (a) If &a is given and &b = null, find the linear LSF that * goes through the origin (b = 0). * (b) If &b is given and &a = null, find the linear LSF with * zero slope (a = 0). * (4) If @nafit is defined, this returns an array of fitted values, * corresponding to the two implicit Numa arrays (nax and nay) in pta. * Thus, just as you can plot the data in pta as nay vs. nax, * you can plot the linear least square fit as nafit vs. nax. */ l_int32 ptaGetLinearLSF(PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit) { l_int32 n, i; l_float32 factor, sx, sy, sxx, sxy, val; l_float32 *xa, *ya; PROCNAME("ptaGetLinearLSF"); if (!pta) return ERROR_INT("pta not defined", procName, 1); if (!pa && !pb) return ERROR_INT("&a and/or &b not defined", procName, 1); if (pa) *pa = 0.0; if (pb) *pb = 0.0; if ((n = ptaGetCount(pta)) < 2) return ERROR_INT("less than 2 pts not found", procName, 1); xa = pta->x; /* not a copy */ ya = pta->y; /* not a copy */ sx = sy = sxx = sxy = 0.; if (pa && pb) { for (i = 0; i < n; i++) { sx += xa[i]; sy += ya[i]; sxx += xa[i] * xa[i]; sxy += xa[i] * ya[i]; } factor = n * sxx - sx * sx; if (factor == 0.0) return ERROR_INT("no solution found", procName, 1); factor = 1. / factor; *pa = factor * ((l_float32)n * sxy - sx * sy); *pb = factor * (sxx * sy - sx * sxy); } else if (pa) { /* line through origin */ for (i = 0; i < n; i++) { sxx += xa[i] * xa[i]; sxy += xa[i] * ya[i]; } if (sxx == 0.0) return ERROR_INT("no solution found", procName, 1); *pa = sxy / sxx; } else { /* a = 0; horizontal line */ for (i = 0; i < n; i++) sy += ya[i]; *pb = sy / (l_float32)n; } if (pnafit) { *pnafit = numaCreate(n); for (i = 0; i < n; i++) { val = (*pa) * xa[i] + *pb; numaAddNumber(*pnafit, val); } } return 0; } /*! * ptaGetPixelsFromPix() * * Input: pixs (1 bpp) * box (<optional> can be null) * Return: pta, or null on error * * Notes: * (1) Generates a pta of fg pixels in the pix, within the box. * If box == NULL, it uses the entire pix. */ PTA * ptaGetPixelsFromPix(PIX *pixs, BOX *box) { l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh; l_uint32 *data, *line; PTA *pta; PROCNAME("ptaGetPixelsFromPix"); if (!pixs || (pixGetDepth(pixs) != 1)) return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); data = pixGetData(pixs); wpl = pixGetWpl(pixs); xstart = ystart = 0; xend = w - 1; yend = h - 1; if (box) { boxGetGeometry(box, &xstart, &ystart, &bw, &bh); xend = xstart + bw - 1; yend = ystart + bh - 1; } if ((pta = ptaCreate(0)) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); for (i = ystart; i <= yend; i++) { line = data + i * wpl; for (j = xstart; j <= xend; j++) { if (GET_DATA_BIT(line, j)) ptaAddPt(pta, j, i); } } return pta; } /*! * pixGenerateFromPta() * * Input: pta * w, h (of pix) * Return: pix (1 bpp), or null on error * * Notes: * (1) Points are rounded to nearest ints. * (2) Any points outside (w,h) are silently discarded. * (3) Output 1 bpp pix has values 1 for each point in the pta. */ PIX * pixGenerateFromPta(PTA *pta, l_int32 w, l_int32 h) { l_int32 n, i, x, y; PIX *pix; PROCNAME("pixGenerateFromPta"); if (!pta) return (PIX *)ERROR_PTR("pta not defined", procName, NULL); if ((pix = pixCreate(w, h, 1)) == NULL) return (PIX *)ERROR_PTR("pix not made", procName, NULL); n = ptaGetCount(pta); for (i = 0; i < n; i++) { ptaGetIPt(pta, i, &x, &y); if (x < 0 || x >= w || y < 0 || y >= h) continue; pixSetPixel(pix, x, y, 1); } return pix; } /*! * ptaGetBoundaryPixels() * * Input: pixs (1 bpp) * type (L_BOUNDARY_FG, L_BOUNDARY_BG) * Return: pta, or null on error * * Notes: * (1) This generates a pta of either fg or bg boundary pixels. */ PTA * ptaGetBoundaryPixels(PIX *pixs, l_int32 type) { PIX *pixt; PTA *pta; PROCNAME("ptaGetBoundaryPixels"); if (!pixs || (pixGetDepth(pixs) != 1)) return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) return (PTA *)ERROR_PTR("invalid type", procName, NULL); if (type == L_BOUNDARY_FG) pixt = pixMorphSequence(pixs, "e3.3", 0); else pixt = pixMorphSequence(pixs, "d3.3", 0); pixXor(pixt, pixt, pixs); pta = ptaGetPixelsFromPix(pixt, NULL); pixDestroy(&pixt); return pta; } /*! * ptaaGetBoundaryPixels() * * Input: pixs (1 bpp) * type (L_BOUNDARY_FG, L_BOUNDARY_BG) * connectivity (4 or 8) * &boxa (<optional return> bounding boxes of the c.c.) * &pixa (<optional return> pixa of the c.c.) * Return: ptaa, or null on error * * Notes: * (1) This generates a ptaa of either fg or bg boundary pixels, * where each pta has the boundary pixels for a connected * component. * (2) We can't simply find all the boundary pixels and then select * those within the bounding box of each component, because * bounding boxes can overlap. It is necessary to extract and * dilate or erode each component separately. Note also that * special handling is required for bg pixels when the * component touches the pix boundary. */ PTAA * ptaaGetBoundaryPixels(PIX *pixs, l_int32 type, l_int32 connectivity, BOXA **pboxa, PIXA **ppixa) { l_int32 i, n, w, h, x, y, bw, bh, left, right, top, bot; BOXA *boxa; PIX *pixt1, *pixt2; PIXA *pixa; PTA *pta1, *pta2; PTAA *ptaa; PROCNAME("ptaaGetBoundaryPixels"); if (pboxa) *pboxa = NULL; if (ppixa) *ppixa = NULL; if (!pixs || (pixGetDepth(pixs) != 1)) return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) return (PTAA *)ERROR_PTR("invalid type", procName, NULL); if (connectivity != 4 && connectivity != 8) return (PTAA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); boxa = pixConnComp(pixs, &pixa, connectivity); n = boxaGetCount(boxa); ptaa = ptaaCreate(0); for (i = 0; i < n; i++) { pixt1 = pixaGetPix(pixa, i, L_CLONE); boxaGetBoxGeometry(boxa, i, &x, &y, &bw, &bh); left = right = top = bot = 0; if (type == L_BOUNDARY_BG) { if (x > 0) left = 1; if (y > 0) top = 1; if (x + bw < w) right = 1; if (y + bh < h) bot = 1; pixt2 = pixAddBorderGeneral(pixt1, left, right, top, bot, 0); } else pixt2 = pixClone(pixt1); pta1 = ptaGetBoundaryPixels(pixt2, type); pta2 = ptaTransform(pta1, x - left, y - top, 1.0, 1.0); ptaaAddPta(ptaa, pta2, L_INSERT); ptaDestroy(&pta1); pixDestroy(&pixt1); pixDestroy(&pixt2); } if (pboxa) *pboxa = boxa; else boxaDestroy(&boxa); if (ppixa) *ppixa = pixa; else pixaDestroy(&pixa); return ptaa; }