blob: 1a2a82f488eea8bf5edebbbcec2cdb1e7320e518 [file] [log] [blame]
/*====================================================================*
- 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;
}