blob: c0452a9edb5885fd97d68377fa6f3c0f8b9d2469 [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.
*====================================================================*/
/*
* sel2.c
*
* Contains definitions of simple structuring elements
*
* SELA *selaAddBasic()
* Linear horizontal and vertical
* Square
* Diagonals
*
* SELA *selaAddHitMiss()
* Isolated foreground pixel
* Horizontal and vertical edges
* Slanted edge
*
* SELA *selaAddDwaLinear()
* SELA *selaAddDwaCombs()
* SELA *selaAddCrossJunctions()
* SELA *selaAddTJunctions()
*/
#include <stdio.h>
#include <math.h>
#include "allheaders.h"
/* MSVC can't handle arrays dimensioned by static const integers */
#define L_BUF_SIZE 512
/* Linear brick sel sizes, including all those that are required
* for decomposable sels up to size 63. */
static const l_int32 num_linear = 25;
static const l_int32 basic_linear[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, 25, 30, 31, 35, 40, 41, 45, 50, 51};
/*!
* selaAddBasic()
*
* Input: sela (<optional>)
* Return: sela with additional sels, or null on error
*
* Notes:
* (1) Adds the following sels:
* - all linear (horiz, vert) brick sels that are
* necessary for decomposable sels up to size 63
* - square brick sels up to size 10
* - 4 diagonal sels
*/
SELA *
selaAddBasic(SELA *sela)
{
char name[L_BUF_SIZE];
l_int32 i, size;
SEL *sel;
PROCNAME("selaAddBasic");
if (!sela) {
if ((sela = selaCreate(0)) == NULL)
return (SELA *)ERROR_PTR("sela not made", procName, NULL);
}
/*--------------------------------------------------------------*
* Linear horizontal and vertical sels *
*--------------------------------------------------------------*/
for (i = 0; i < num_linear; i++) {
size = basic_linear[i];
sel = selCreateBrick(1, size, 0, size / 2, 1);
snprintf(name, L_BUF_SIZE, "sel_%dh", size);
selaAddSel(sela, sel, name, 0);
}
for (i = 0; i < num_linear; i++) {
size = basic_linear[i];
sel = selCreateBrick(size, 1, size / 2, 0, 1);
snprintf(name, L_BUF_SIZE, "sel_%dv", size);
selaAddSel(sela, sel, name, 0);
}
/*-----------------------------------------------------------*
* 2-d Bricks *
*-----------------------------------------------------------*/
for (i = 2; i <= 5; i++) {
sel = selCreateBrick(i, i, i / 2, i / 2, 1);
snprintf(name, L_BUF_SIZE, "sel_%d", i);
selaAddSel(sela, sel, name, 0);
}
/*-----------------------------------------------------------*
* Diagonals *
*-----------------------------------------------------------*/
/* 0c 1
1 0 */
sel = selCreateBrick(2, 2, 0, 0, 1);
selSetElement(sel, 0, 0, 0);
selSetElement(sel, 1, 1, 0);
selaAddSel(sela, sel, "sel_2dp", 0);
/* 1c 0
0 1 */
sel = selCreateBrick(2, 2, 0, 0, 1);
selSetElement(sel, 0, 1, 0);
selSetElement(sel, 1, 0, 0);
selaAddSel(sela, sel, "sel_2dm", 0);
/* Diagonal, slope +, size 5 */
sel = selCreate(5, 5, "sel_5dp");
sel->cy = 2;
sel->cx = 2;
selSetElement(sel, 0, 4, 1);
selSetElement(sel, 1, 3, 1);
selSetElement(sel, 2, 2, 1);
selSetElement(sel, 3, 1, 1);
selSetElement(sel, 4, 0, 1);
selaAddSel(sela, sel, "sel_5dp", 0);
/* Diagonal, slope -, size 5 */
sel = selCreate(5, 5, "sel_5dm");
sel->cy = 2;
sel->cx = 2;
selSetElement(sel, 0, 0, 1);
selSetElement(sel, 1, 1, 1);
selSetElement(sel, 2, 2, 1);
selSetElement(sel, 3, 3, 1);
selSetElement(sel, 4, 4, 1);
selaAddSel(sela, sel, "sel_5dm", 0);
return sela;
}
/*!
* selaAddHitMiss()
*
* Input: sela (<optional>)
* Return: sela with additional sels, or null on error
*/
SELA *
selaAddHitMiss(SELA *sela)
{
SEL *sel;
PROCNAME("selaAddHitMiss");
if (!sela) {
if ((sela = selaCreate(0)) == NULL)
return (SELA *)ERROR_PTR("sela not made", procName, NULL);
}
#if 0 /* use just for testing */
sel = selCreateBrick(3, 3, 1, 1, 2);
selaAddSel(sela, sel, "sel_bad", 0);
#endif
/*--------------------------------------------------------------*
* Isolated foreground pixel *
*--------------------------------------------------------------*/
sel = selCreateBrick(3, 3, 1, 1, 2);
selSetElement(sel, 1, 1, 1);
selaAddSel(sela, sel, "sel_3hm", 0);
/*--------------------------------------------------------------*
* Horizontal and vertical edges *
*--------------------------------------------------------------*/
sel = selCreateBrick(2, 3, 0, 1, 1);
selSetElement(sel, 1, 0, 2);
selSetElement(sel, 1, 1, 2);
selSetElement(sel, 1, 2, 2);
selaAddSel(sela, sel, "sel_3de", 0);
sel = selCreateBrick(2, 3, 1, 1, 1);
selSetElement(sel, 0, 0, 2);
selSetElement(sel, 0, 1, 2);
selSetElement(sel, 0, 2, 2);
selaAddSel(sela, sel, "sel_3ue", 0);
sel = selCreateBrick(3, 2, 1, 0, 1);
selSetElement(sel, 0, 1, 2);
selSetElement(sel, 1, 1, 2);
selSetElement(sel, 2, 1, 2);
selaAddSel(sela, sel, "sel_3re", 0);
sel = selCreateBrick(3, 2, 1, 1, 1);
selSetElement(sel, 0, 0, 2);
selSetElement(sel, 1, 0, 2);
selSetElement(sel, 2, 0, 2);
selaAddSel(sela, sel, "sel_3le", 0);
/*--------------------------------------------------------------*
* Slanted edge *
*--------------------------------------------------------------*/
sel = selCreateBrick(13, 6, 6, 2, 0);
selSetElement(sel, 0, 3, 2);
selSetElement(sel, 0, 5, 1);
selSetElement(sel, 4, 2, 2);
selSetElement(sel, 4, 4, 1);
selSetElement(sel, 8, 1, 2);
selSetElement(sel, 8, 3, 1);
selSetElement(sel, 12, 0, 2);
selSetElement(sel, 12, 2, 1);
selaAddSel(sela, sel, "sel_sl1", 0);
return sela;
}
/*!
* selaAddDwaLinear()
*
* Input: sela (<optional>)
* Return: sela with additional sels, or null on error
*
* Notes:
* (1) Adds all linear (horizontal, vertical) sels from
* 2 to 63 pixels in length, which are the sizes over
* which dwa code can be generated.
*/
SELA *
selaAddDwaLinear(SELA *sela)
{
char name[L_BUF_SIZE];
l_int32 i;
SEL *sel;
PROCNAME("selaAddDwaLinear");
if (!sela) {
if ((sela = selaCreate(0)) == NULL)
return (SELA *)ERROR_PTR("sela not made", procName, NULL);
}
for (i = 2; i < 64; i++) {
sel = selCreateBrick(1, i, 0, i / 2, 1);
snprintf(name, L_BUF_SIZE, "sel_%dh", i);
selaAddSel(sela, sel, name, 0);
}
for (i = 2; i < 64; i++) {
sel = selCreateBrick(i, 1, i / 2, 0, 1);
snprintf(name, L_BUF_SIZE, "sel_%dv", i);
selaAddSel(sela, sel, name, 0);
}
return sela;
}
/*!
* selaAddDwaCombs()
*
* Input: sela (<optional>)
* Return: sela with additional sels, or null on error
*
* Notes:
* (1) Adds all comb (horizontal, vertical) Sels that are
* used in composite linear morphological operations
* up to 63 pixels in length, which are the sizes over
* which dwa code can be generated.
*/
SELA *
selaAddDwaCombs(SELA *sela)
{
char name[L_BUF_SIZE];
l_int32 i, f1, f2, prevsize, size;
SEL *selh, *selv;
PROCNAME("selaAddDwaCombs");
if (!sela) {
if ((sela = selaCreate(0)) == NULL)
return (SELA *)ERROR_PTR("sela not made", procName, NULL);
}
prevsize = 0;
for (i = 4; i < 64; i++) {
selectComposableSizes(i, &f1, &f2);
size = f1 * f2;
if (size == prevsize)
continue;
selectComposableSels(i, L_HORIZ, NULL, &selh);
selectComposableSels(i, L_VERT, NULL, &selv);
snprintf(name, L_BUF_SIZE, "sel_comb_%dh", size);
selaAddSel(sela, selh, name, 0);
snprintf(name, L_BUF_SIZE, "sel_comb_%dv", size);
selaAddSel(sela, selv, name, 0);
prevsize = size;
}
return sela;
}
/*!
* selaAddCrossJunctions()
*
* 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 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.
*/
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, 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);
/* 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;
pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2);
pixWrite("/tmp/junkxsel1.png", pixt, IFF_PNG);
pixDisplay(pixt, 0, 100);
pixDestroy(&pixt);
pixt = selaDisplayInPix(sela, 15, 2, 20, 1);
pixWrite("/tmp/junkxsel2.png", pixt, IFF_PNG);
pixDisplay(pixt, 500, 100);
pixDestroy(&pixt);
selaWriteStream(stderr, sela);
}
pixaDestroy(&pixa);
return sela;
}
/*!
* 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, 0);
ptaJoin(pta1, pta3, 0, 0);
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);
pixWrite("/tmp/junktsel1.png", pixt, IFF_PNG);
pixDisplay(pixt, 0, 100);
pixDestroy(&pixt);
pixt = selaDisplayInPix(sela, 15, 2, 20, 4);
pixWrite("/tmp/junktsel2.png", pixt, IFF_PNG);
pixDisplay(pixt, 500, 100);
pixDestroy(&pixt);
selaWriteStream(stderr, sela);
}
pixaDestroy(&pixa);
return sela;
}