/*! * \brief boxaConvertToPta() * * \param[in] boxa * \param[in] ncorners 2 or 4 for the representation of each box * \return pta with %ncorners points for each box in the boxa, * or NULL on error * * <pre> * Notes: * (1) If ncorners == 2, we select the UL and LR corners. * Otherwise we save all 4 corners in this order: UL, UR, LL, LR. * (2) Other boxa --> pta functions are: * * boxaExtractAsPta(): allows extraction of any dimension * and/or side location, with each in a separate pta. * * boxaExtractCorners(): extracts any of the four corners as a pta. * </pre> */ PTA * boxaConvertToPta(BOXA *boxa, l_int32 ncorners) { l_int32 i, n; BOX *box; PTA *pta, *pta1; PROCNAME("boxaConvertToPta"); if (!boxa) return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); if (ncorners != 2 && ncorners != 4) return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); n = boxaGetCount(boxa); if ((pta = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_COPY); pta1 = boxConvertToPta(box, ncorners); ptaJoin(pta, pta1, 0, -1); boxDestroy(&box); ptaDestroy(&pta1); } return pta; }
/*! * recogAppend() * * Input: recog1 * recog2 (gets added to recog1) * Return: 0 if OK, 1 on error * * Notes: * (1) This is used to make a training recognizer from more than * one trained recognizer source. It should only be used * when the bitmaps for corresponding character classes are * very similar. That constraint does not arise when * the character classes are disjoint; e.g., if recog1 is * digits and recog2 is alphabetical. * (2) This is done by appending recog2 to recog1. Averages are * computed for each recognizer, if necessary, before appending. * (3) Non-array fields are combined using the appropriate min and max. */ l_int32 recogAppend(L_RECOG *recog1, L_RECOG *recog2) { PROCNAME("recogAppend"); if (!recog1) return ERROR_INT("recog1 not defined", procName, 1); if (!recog2) return ERROR_INT("recog2 not defined", procName, 1); /* Make sure both are finalized with all arrays computed */ recogAverageSamples(recog1, 0); recogAverageSamples(recog2, 0); /* Combine non-array field values */ recog1->minwidth_u = L_MIN(recog1->minwidth_u, recog2->minwidth_u); recog1->maxwidth_u = L_MAX(recog1->maxwidth_u, recog2->maxwidth_u); recog1->minheight_u = L_MIN(recog1->minheight_u, recog2->minheight_u); recog1->maxheight_u = L_MAX(recog1->maxheight_u, recog2->maxheight_u); recog1->minwidth = L_MIN(recog1->minwidth, recog2->minwidth); recog1->maxwidth = L_MAX(recog1->maxwidth, recog2->maxwidth); recog1->min_splitw = L_MIN(recog1->min_splitw, recog2->min_splitw); recog1->min_splith = L_MIN(recog1->min_splith, recog2->min_splith); recog1->max_splith = L_MAX(recog1->max_splith, recog2->max_splith); /* Combine array field values */ recog1->setsize += recog2->setsize; sarrayAppendRange(recog1->sa_text, recog2->sa_text, 0, -1); l_dnaJoin(recog1->dna_tochar, recog2->dna_tochar, 0, -1); pixaaJoin(recog1->pixaa_u, recog2->pixaa_u, 0, -1); pixaJoin(recog1->pixa_u, recog2->pixa_u, 0, -1); ptaaJoin(recog1->ptaa_u, recog2->ptaa_u, 0, -1); ptaJoin(recog1->pta_u, recog2->pta_u, 0, -1); numaaJoin(recog1->naasum_u, recog2->naasum_u, 0, -1); numaJoin(recog1->nasum_u, recog2->nasum_u, 0, -1); pixaaJoin(recog1->pixaa, recog2->pixaa, 0, -1); pixaJoin(recog1->pixa, recog2->pixa, 0, -1); ptaaJoin(recog1->ptaa, recog2->ptaa, 0, -1); ptaJoin(recog1->pta, recog2->pta, 0, -1); numaaJoin(recog1->naasum, recog2->naasum, 0, -1); numaJoin(recog1->nasum, recog2->nasum, 0, -1); return 0; }
/*! * ptaUnionByHash() * * Input: pta1, pta2 * Return: ptad (with the union of the set of points), or null on error * * Notes: * (1) This is faster than ptaUnionByAset(), because the * bucket lookup is O(n). It should be used if the pts are * integers (e.g., representing pixel positions). */ PTA * ptaUnionByHash(PTA *pta1, PTA *pta2) { PTA *pta3, *ptad; PROCNAME("ptaUnionByHash"); if (!pta1) return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); if (!pta2) return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); /* Join */ pta3 = ptaCopy(pta1); ptaJoin(pta3, pta2, 0, -1); /* Eliminate duplicates */ ptaRemoveDupsByHash(pta3, &ptad, NULL); ptaDestroy(&pta3); return ptad; }
/*! * selaAddTJunctions() * * Input: sela (<optional>) * hlsize (length of each line of hits from origin) * mdist (distance of misses from the origin) * norient (number of orientations; max of 8) * debugflag (1 for debug output) * Return: sela with additional sels, or null on error * * Notes: * (1) Adds hitmiss Sels for the T-junction of two lines. * If the lines are very thin, they must be nearly orthogonal * to register. * (2) The number of Sels generated is 4 * @norient. * (3) It is suggested that @hlsize be chosen at least 1 greater * than @mdist. Try values of (@hlsize, @mdist) such as * (6,5), (7,6), (8,7), (9,7), etc. */ SELA * selaAddTJunctions(SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag) { char name[L_BUF_SIZE]; l_int32 i, j, k, w, xc, yc; l_float64 pi, halfpi, radincr, jang, radang; l_float64 angle[3], dist[3]; PIX *pixc, *pixm, *pixt; PIXA *pixa; PTA *pta1, *pta2, *pta3; SEL *sel; PROCNAME("selaAddTJunctions"); if (hlsize <= 2) return (SELA *)ERROR_PTR("hlsizel not > 1", procName, NULL); if (norient < 1 || norient > 8) return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); if (!sela) { if ((sela = selaCreate(0)) == NULL) return (SELA *)ERROR_PTR("sela not made", procName, NULL); } pi = 3.1415926535; halfpi = 3.1415926535 / 2.0; radincr = halfpi / (l_float32)norient; w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5)); if (w % 2 == 0) w++; xc = w / 2; yc = w / 2; pixa = pixaCreate(4 * norient); for (i = 0; i < norient; i++) { for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */ jang = (l_float32)j * halfpi; /* Set the don't cares */ pixc = pixCreate(w, w, 32); pixSetAll(pixc); /* Add the green lines of hits */ pixm = pixCreate(w, w, 1); radang = (l_float32)i * radincr; pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang); pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang + halfpi); pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang + pi); ptaJoin(pta1, pta2, 0, -1); ptaJoin(pta1, pta3, 0, -1); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); /* Add red misses between the lines */ angle[0] = radang + jang - halfpi; angle[1] = radang + jang + 0.5 * halfpi; angle[2] = radang + jang + 1.5 * halfpi; dist[0] = 0.8 * mdist; dist[1] = dist[2] = mdist; for (k = 0; k < 3; k++) { pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])), yc + (l_int32)(dist[k] * sin(angle[k])), 0xff000000); } /* Add dark green for origin */ pixSetPixel(pixc, xc, yc, 0x00550000); /* Generate the sel */ sel = selCreateFromColorPix(pixc, NULL); sprintf(name, "sel_cross_%d", 4 * i + j); selaAddSel(sela, sel, name, 0); if (debugflag) { pixt = pixScaleBySampling(pixc, 10.0, 10.0); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pixm); pixDestroy(&pixc); } } if (debugflag) { l_int32 w; pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2); pixWriteTempfile("/tmp", "tsel1.png", pixt, IFF_PNG, 0); pixDisplay(pixt, 0, 100); pixDestroy(&pixt); pixt = selaDisplayInPix(sela, 15, 2, 20, 4); pixWriteTempfile("/tmp", "tsel2.png", pixt, IFF_PNG, 0); pixDisplay(pixt, 500, 100); pixDestroy(&pixt); selaWriteStream(stderr, sela); } pixaDestroy(&pixa); return sela; }
main(int argc, char **argv) { l_int32 i; l_float32 pi, scale, angle; PIX *pixc, *pixm, *pix1, *pix2, *pix3; PIXA *pixa; PTA *pta1, *pta2, *pta3, *pta4; static char mainName[] = "smallpix_reg"; /* Make a small test image, the hard way! */ pi = 3.1415926535; pixc = pixCreate(9, 9, 32); pixm = pixCreate(9, 9, 1); pta1 = generatePtaLineFromPt(4, 4, 3.1, 0.0); pta2 = generatePtaLineFromPt(4, 4, 3.1, 0.5 * pi); pta3 = generatePtaLineFromPt(4, 4, 3.1, pi); pta4 = generatePtaLineFromPt(4, 4, 3.1, 1.5 * pi); ptaJoin(pta1, pta2, 0, 0); ptaJoin(pta1, pta3, 0, 0); ptaJoin(pta1, pta4, 0, 0); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); ptaDestroy(&pta4); pixDestroy(&pixm); /* Results differ for scaleSmoothLow() w/ and w/out + 0.5. * Neither is properly symmetric (with symm pattern on odd-sized * pix, because the smoothing is destroying the symmetry. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleSmooth(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 100, NULL); /* Results same for pixScaleAreaMap w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleAreaMap(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 200, NULL); /* Results better for pixScaleBySampling with + 0.5, for small, * odd-dimension pix. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleBySampling(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 300, NULL); /* Results same for pixRotateAM w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAM(pix1, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 400, NULL); /* If the size is odd, we express the center exactly, and the * results are better for pixRotateBySampling() w/out 0.5 * However, if the size is even, the center value is not * exact, and if we choose it 0.5 smaller than the actual * center, we get symmetrical results with +0.5. * So we choose not to include + 0.5. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateBySampling(pix1, 4, 4, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 500, NULL); /* Results same for pixRotateAMCorner w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAMCorner(pix1, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 600, NULL); /* Results better for pixRotateAMColorFast without + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAMColorFast(pix1, angle, 0); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 700, NULL); /* Results slightly better for pixScaleColorLI() w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { scale = 1.0 + 0.2 * (l_float32)i; pix2 = pixScaleColorLI(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 4); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 800, NULL); /* Results slightly better for pixScaleColorLI() w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { scale = 1.0 + 0.2 * (l_float32)i; pix2 = pixScaleLI(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 4); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 940, NULL); pixDestroy(&pixc); return 0; }
/*! * \brief pixConnCompIncrAdd() * * \param[in] pixs 32 bpp, with pixels labeled by c.c. * \param[in] ptaa with each pta of pixel locations indexed by c.c. * \param[out] pncc number of c.c * \param[in] x,y location of added pixel * \param[in] debug 0 for no output; otherwise output whenever * debug <= nvals, up to debug == 3 * \return -1 if nothing happens; 0 if a pixel is added; 1 on error * * <pre> * Notes: * (1) This adds a pixel and updates the labeled connected components. * Before calling this function, initialize the process using * pixConnCompIncrInit(). * (2) As a result of adding a pixel, one of the following can happen, * depending on the number of neighbors with non-zero value: * (a) nothing: the pixel is already a member of a c.c. * (b) no neighbors: a new component is added, increasing the * number of c.c. * (c) one neighbor: the pixel is added to an existing c.c. * (d) more than one neighbor: the added pixel causes joining of * two or more c.c., reducing the number of c.c. A maximum * of 4 c.c. can be joined. * (3) When two c.c. are joined, the pixels in the larger index are * relabeled to those of the smaller in pixs, and their locations * are transferred to the pta with the smaller index in the ptaa. * The pta corresponding to the larger index is then deleted. * (4) This is an efficient implementation of a "union-find" operation, * which supports the generation and merging of disjoint sets * of pixels. This function can be called about 1.3 million times * per second. * </pre> */ l_int32 pixConnCompIncrAdd(PIX *pixs, PTAA *ptaa, l_int32 *pncc, l_float32 x, l_float32 y, l_int32 debug) { l_int32 conn, i, j, w, h, count, nvals, ns, firstindex; l_uint32 val; l_int32 *neigh; PTA *ptas, *ptad; PROCNAME("pixConnCompIncrAdd"); if (!pixs || pixGetDepth(pixs) != 32) return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 1); if (!pncc) return ERROR_INT("&ncc not defined", procName, 1); conn = pixs->special; if (conn != 4 && conn != 8) return ERROR_INT("connectivity must be 4 or 8", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); if (x < 0 || x >= w) return ERROR_INT("invalid x pixel location", procName, 1); if (y < 0 || y >= h) return ERROR_INT("invalid y pixel location", procName, 1); pixGetPixel(pixs, x, y, &val); if (val > 0) /* already belongs to a set */ return -1; /* Find unique neighbor pixel values in increasing order of value. * If %nvals > 0, these are returned in the %neigh array, which * is of size %nvals. Note that the pixel values in each * connected component are used as the index into the pta * array of the ptaa, giving the pixel locations. */ pixGetSortedNeighborValues(pixs, x, y, conn, &neigh, &nvals); /* If there are no neighbors, just add a new component */ if (nvals == 0) { count = ptaaGetCount(ptaa); pixSetPixel(pixs, x, y, count); ptas = ptaCreate(1); ptaAddPt(ptas, x, y); ptaaAddPta(ptaa, ptas, L_INSERT); *pncc += 1; LEPT_FREE(neigh); return 0; } /* Otherwise, there is at least one neighbor. Add the pixel * to the first neighbor c.c. */ firstindex = neigh[0]; pixSetPixel(pixs, x, y, firstindex); ptaaAddPt(ptaa, neigh[0], x, y); if (nvals == 1) { if (debug == 1) fprintf(stderr, "nvals = %d: neigh = (%d)\n", nvals, neigh[0]); LEPT_FREE(neigh); return 0; } /* If nvals > 1, there are at least 2 neighbors, so this pixel * joins at least one pair of existing c.c. Join each component * to the first component in the list, which is the one with * the smallest integer label. This is done in two steps: * (a) re-label the pixels in the component to the label of the * first component, and * (b) save the pixel locations in the pta for the first component. */ if (nvals == 2) { if (debug >= 1 && debug <= 2) { fprintf(stderr, "nvals = %d: neigh = (%d,%d)\n", nvals, neigh[0], neigh[1]); } } else if (nvals == 3) { if (debug >= 1 && debug <= 3) { fprintf(stderr, "nvals = %d: neigh = (%d,%d,%d)\n", nvals, neigh[0], neigh[1], neigh[2]); } } else { /* nvals == 4 */ if (debug >= 1 && debug <= 4) { fprintf(stderr, "nvals = %d: neigh = (%d,%d,%d,%d)\n", nvals, neigh[0], neigh[1], neigh[2], neigh[3]); } } ptad = ptaaGetPta(ptaa, firstindex, L_CLONE); for (i = 1; i < nvals; i++) { ptas = ptaaGetPta(ptaa, neigh[i], L_CLONE); ns = ptaGetCount(ptas); for (j = 0; j < ns; j++) { /* relabel pixels */ ptaGetPt(ptas, j, &x, &y); pixSetPixel(pixs, x, y, firstindex); } ptaJoin(ptad, ptas, 0, -1); /* add relabeled pixel locations */ *pncc -= 1; ptaDestroy(&ptaa->pta[neigh[i]]); ptaDestroy(&ptas); /* the clone */ } ptaDestroy(&ptad); /* the clone */ LEPT_FREE(neigh); return 0; }
/*! * \brief selaAddCrossJunctions() * * \param[in] sela [optional] * \param[in] hlsize length of each line of hits from origin * \param[in] mdist distance of misses from the origin * \param[in] norient number of orientations; max of 8 * \param[in] debugflag 1 for debug output * \return sela with additional sels, or NULL on error * * <pre> * Notes: * (1) Adds hitmiss Sels for the intersection of two lines. * If the lines are very thin, they must be nearly orthogonal * to register. * (2) The number of Sels generated is equal to %norient. * (3) If %norient == 2, this generates 2 Sels of crosses, each with * two perpendicular lines of hits. One Sel has horizontal and * vertical hits; the other has hits along lines at +-45 degrees. * Likewise, if %norient == 3, this generates 3 Sels of crosses * oriented at 30 degrees with each other. * (4) It is suggested that %hlsize be chosen at least 1 greater * than %mdist. Try values of (%hlsize, %mdist) such as * (6,5), (7,6), (8,7), (9,7), etc. * </pre> */ SELA * selaAddCrossJunctions(SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag) { char name[L_BUF_SIZE]; l_int32 i, j, w, xc, yc; l_float64 pi, halfpi, radincr, radang; l_float64 angle; PIX *pixc, *pixm, *pixt; PIXA *pixa; PTA *pta1, *pta2, *pta3, *pta4; SEL *sel; PROCNAME("selaAddCrossJunctions"); if (hlsize <= 0) return (SELA *)ERROR_PTR("hlsize not > 0", procName, NULL); if (norient < 1 || norient > 8) return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); if (!sela) { if ((sela = selaCreate(0)) == NULL) return (SELA *)ERROR_PTR("sela not made", procName, NULL); } pi = 3.1415926535; halfpi = 3.1415926535 / 2.0; radincr = halfpi / (l_float64)norient; w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5)); if (w % 2 == 0) w++; xc = w / 2; yc = w / 2; pixa = pixaCreate(norient); for (i = 0; i < norient; i++) { /* Set the don't cares */ pixc = pixCreate(w, w, 32); pixSetAll(pixc); /* Add the green lines of hits */ pixm = pixCreate(w, w, 1); radang = (l_float32)i * radincr; pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang); pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi); pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi); pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi); ptaJoin(pta1, pta2, 0, -1); ptaJoin(pta1, pta3, 0, -1); ptaJoin(pta1, pta4, 0, -1); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); ptaDestroy(&pta4); /* Add red misses between the lines */ for (j = 0; j < 4; j++) { angle = radang + (j - 0.5) * halfpi; pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)), yc + (l_int32)(mdist * sin(angle)), 0xff000000); } /* Add dark green for origin */ pixSetPixel(pixc, xc, yc, 0x00550000); /* Generate the sel */ sel = selCreateFromColorPix(pixc, NULL); sprintf(name, "sel_cross_%d", i); selaAddSel(sela, sel, name, 0); if (debugflag) { pixt = pixScaleBySampling(pixc, 10.0, 10.0); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pixm); pixDestroy(&pixc); } if (debugflag) { l_int32 w; lept_mkdir("lept/sel"); pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2); pixWrite("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG); pixDisplay(pixt, 0, 100); pixDestroy(&pixt); pixt = selaDisplayInPix(sela, 15, 2, 20, 1); pixWrite("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG); pixDisplay(pixt, 500, 100); pixDestroy(&pixt); selaWriteStream(stderr, sela); } pixaDestroy(&pixa); return sela; }