blob: 3608ba2729e54670bc571baf353a4a02b651cc5d [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.
*====================================================================*/
/*
* boxfunc3.c
*
* Boxa/Boxaa painting into pix
* PIX *pixMaskConnComp()
* PIX *pixMaskBoxa()
* PIX *pixPaintBoxa()
* PIX *pixPaintBoxaRandom()
* PIX *pixBlendBoxaRandom()
* PIX *pixDrawBoxa()
* PIX *pixDrawBoxaRandom()
* PIX *boxaaDisplay()
*
* Split mask components into Boxa
* BOXA *pixSplitIntoBoxa()
* BOXA *pixSplitComponentIntoBoxa()
* static l_int32 pixSearchForRectangle()
*
* See summary in pixPaintBoxa() of various ways to paint and draw
* boxes on images.
*/
#include <stdio.h>
#include <stdlib.h>
#include "allheaders.h"
static l_int32 pixSearchForRectangle(PIX *pixs, BOX *boxs, l_int32 minsum,
l_int32 skipdist, l_int32 delta,
l_int32 maxbg, l_int32 sideflag,
BOXA *boxat, NUMA *nascore);
#ifndef NO_CONSOLE_IO
#define DEBUG_SPLIT 0
#endif /* ~NO_CONSOLE_IO */
/*---------------------------------------------------------------------*
* Boxa/Boxaa painting into Pix *
*---------------------------------------------------------------------*/
/*!
* pixMaskConnComp()
*
* Input: pixs (1 bpp)
* connectivity (4 or 8)
* &boxa (<optional return> bounding boxes of c.c.)
* Return: pixd (1 bpp mask over the c.c.), or null on error
*
* Notes:
* (1) This generates a mask image with ON pixels over the
* b.b. of the c.c. in pixs. If there are no ON pixels in pixs,
* pixd will also have no ON pixels.
*/
PIX *
pixMaskConnComp(PIX *pixs,
l_int32 connectivity,
BOXA **pboxa)
{
BOXA *boxa;
PIX *pixd;
PROCNAME("pixMaskConnComp");
if (!pixs || pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
if (connectivity != 4 && connectivity != 8)
return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
boxa = pixConnComp(pixs, NULL, connectivity);
pixd = pixCreateTemplate(pixs);
if (boxaGetCount(boxa) != 0)
pixMaskBoxa(pixd, pixd, boxa, L_SET_PIXELS);
if (pboxa)
*pboxa = boxa;
else
boxaDestroy(&boxa);
return pixd;
}
/*!
* pixMaskBoxa()
*
* Input: pixd (<optional> may be null)
* pixs (any depth; not cmapped)
* boxa (of boxes, to paint)
* op (L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS)
* Return: pixd (with masking op over the boxes), or null on error
*
* Notes:
* (1) This can be used with:
* pixd = NULL (makes a new pixd)
* pixd = pixs (in-place)
* (2) If pixd == NULL, this first makes a copy of pixs, and then
* bit-twiddles over the boxes. Otherwise, it operates directly
* on pixs.
* (3) This simple function is typically used with 1 bpp images.
* It uses the 1-image rasterop function, rasteropUniLow(),
* to set, clear or flip the pixels in pixd.
* (4) If you want to generate a 1 bpp mask of ON pixels from the boxes
* in a Boxa, in a pix of size (w,h):
* pix = pixCreate(w, h, 1);
* pixMaskBoxa(pix, pix, boxa, L_SET_PIXELS);
*/
PIX *
pixMaskBoxa(PIX *pixd,
PIX *pixs,
BOXA *boxa,
l_int32 op)
{
l_int32 i, n, x, y, w, h;
BOX *box;
PROCNAME("pixMaskBoxa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL);
if (pixd && (pixd != pixs))
return (PIX *)ERROR_PTR("if pixd, must be in-place", procName, NULL);
if (!boxa)
return (PIX *)ERROR_PTR("boxa not defined", procName, NULL);
if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
return (PIX *)ERROR_PTR("invalid op", procName, NULL);
pixd = pixCopy(pixd, pixs);
if ((n = boxaGetCount(boxa)) == 0) {
L_WARNING("no boxes to mask", procName);
return pixd;
}
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
boxGetGeometry(box, &x, &y, &w, &h);
if (op == L_SET_PIXELS)
pixRasterop(pixd, x, y, w, h, PIX_SET, NULL, 0, 0);
else if (op == L_CLEAR_PIXELS)
pixRasterop(pixd, x, y, w, h, PIX_CLR, NULL, 0, 0);
else /* op == L_FLIP_PIXELS */
pixRasterop(pixd, x, y, w, h, PIX_NOT(PIX_DST), NULL, 0, 0);
boxDestroy(&box);
}
return pixd;
}
/*!
* pixPaintBoxa()
*
* Input: pixs (any depth, can be cmapped)
* boxa (of boxes, to paint)
* val (rgba color to paint)
* Return: pixd (with painted boxes), or null on error
*
* Notes:
* (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp
* and the boxa is painted using a colormap; otherwise,
* it is converted to 32 bpp rgb.
* (2) There are several ways to display a box on an image:
* * Paint it as a solid color
* * Draw the outline
* * Blend the outline or region with the existing image
* We provide painting and drawing here; blending is in blend.c.
* When painting or drawing, the result can be either a
* cmapped image or an rgb image. The dest will be cmapped
* if the src is either 1 bpp or has a cmap that is not full.
* To force RGB output, use pixConvertTo8(pixs, FALSE)
* before calling any of these paint and draw functions.
*/
PIX *
pixPaintBoxa(PIX *pixs,
BOXA *boxa,
l_uint32 val)
{
l_int32 i, n, d, rval, gval, bval, newindex;
l_int32 mapvacancy; /* true only if cmap and not full */
BOX *box;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixPaintBoxa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (!boxa)
return (PIX *)ERROR_PTR("boxa not defined", procName, NULL);
if ((n = boxaGetCount(boxa)) == 0) {
L_WARNING("no boxes to paint; returning a copy", procName);
return pixCopy(NULL, pixs);
}
mapvacancy = FALSE;
if ((cmap = pixGetColormap(pixs)) != NULL) {
if (pixcmapGetCount(cmap) < 256)
mapvacancy = TRUE;
}
if (pixGetDepth(pixs) == 1 || mapvacancy)
pixd = pixConvertTo8(pixs, TRUE);
else
pixd = pixConvertTo32(pixs);
if (!pixd)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
d = pixGetDepth(pixd);
if (d == 8) { /* colormapped */
cmap = pixGetColormap(pixd);
extractRGBValues(val, &rval, &gval, &bval);
if (pixcmapAddNewColor(cmap, rval, gval, bval, &newindex))
return (PIX *)ERROR_PTR("cmap full; can't add", procName, NULL);
}
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
if (d == 8)
pixSetInRectArbitrary(pixd, box, newindex);
else
pixSetInRectArbitrary(pixd, box, val);
boxDestroy(&box);
}
return pixd;
}
/*!
* pixPaintBoxaRandom()
*
* Input: pixs (any depth, can be cmapped)
* boxa (of boxes, to paint)
* Return: pixd (with painted boxes), or null on error
*
* Notes:
* (1) If pixs is 1 bpp, we paint the boxa using a colormap;
* otherwise, we convert to 32 bpp.
* (2) We use up to 254 different colors for painting the regions.
* (3) If boxes overlap, the later ones paint over earlier ones.
*/
PIX *
pixPaintBoxaRandom(PIX *pixs,
BOXA *boxa)
{
l_int32 i, n, d, rval, gval, bval, index;
l_uint32 val;
BOX *box;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixPaintBoxaRandom");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (!boxa)
return (PIX *)ERROR_PTR("boxa not defined", procName, NULL);
if ((n = boxaGetCount(boxa)) == 0) {
L_WARNING("no boxes to paint; returning a copy", procName);
return pixCopy(NULL, pixs);
}
if (pixGetDepth(pixs) == 1)
pixd = pixConvert1To8(NULL, pixs, 255, 0);
else
pixd = pixConvertTo32(pixs);
if (!pixd)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreateRandom(8, 1, 1);
d = pixGetDepth(pixd);
if (d == 8) /* colormapped */
pixSetColormap(pixd, cmap);
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
index = 1 + (i % 254);
if (d == 8)
pixSetInRectArbitrary(pixd, box, index);
else { /* d == 32 */
pixcmapGetColor(cmap, index, &rval, &gval, &bval);
composeRGBPixel(rval, gval, bval, &val);
pixSetInRectArbitrary(pixd, box, val);
}
boxDestroy(&box);
}
if (d == 32)
pixcmapDestroy(&cmap);
return pixd;
}
/*!
* pixBlendBoxaRandom()
*
* Input: pixs (any depth; can be cmapped)
* boxa (of boxes, to blend/paint)
* fract (of box color to use)
* Return: pixd (32 bpp, with blend/painted boxes), or null on error
*
* Notes:
* (1) pixs is converted to 32 bpp.
* (2) This differs from pixPaintBoxaRandom(), in that the
* colors here are blended with the color of pixs.
* (3) We use up to 254 different colors for painting the regions.
* (4) If boxes overlap, the final color depends only on the last
* rect that is used.
*/
PIX *
pixBlendBoxaRandom(PIX *pixs,
BOXA *boxa,
l_float32 fract)
{
l_int32 i, n, rval, gval, bval, index;
l_uint32 val;
BOX *box;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixBlendBoxaRandom");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (!boxa)
return (PIX *)ERROR_PTR("boxa not defined", procName, NULL);
if (fract < 0.0 || fract > 1.0) {
L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName);
fract = 0.5;
}
if ((n = boxaGetCount(boxa)) == 0) {
L_WARNING("no boxes to paint; returning a copy", procName);
return pixCopy(NULL, pixs);
}
if ((pixd = pixConvertTo32(pixs)) == NULL)
return (PIX *)ERROR_PTR("pixd not defined", procName, NULL);
cmap = pixcmapCreateRandom(8, 1, 1);
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
index = 1 + (i % 254);
pixcmapGetColor(cmap, index, &rval, &gval, &bval);
composeRGBPixel(rval, gval, bval, &val);
pixBlendInRect(pixd, box, val, fract);
boxDestroy(&box);
}
pixcmapDestroy(&cmap);
return pixd;
}
/*!
* pixDrawBoxa()
*
* Input: pixs (any depth; can be cmapped)
* boxa (of boxes, to draw)
* width (of lines)
* val (rgba color to draw)
* Return: pixd (with outlines of boxes added), or null on error
*
* Notes:
* (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp
* and the boxa is drawn using a colormap; otherwise,
* it is converted to 32 bpp rgb.
*/
PIX *
pixDrawBoxa(PIX *pixs,
BOXA *boxa,
l_int32 width,
l_uint32 val)
{
l_int32 rval, gval, bval, newindex;
l_int32 mapvacancy; /* true only if cmap and not full */
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixDrawBoxa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (!boxa)
return (PIX *)ERROR_PTR("boxa not defined", procName, NULL);
if (width < 1)
return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL);
if (boxaGetCount(boxa) == 0) {
L_WARNING("no boxes to draw; returning a copy", procName);
return pixCopy(NULL, pixs);
}
mapvacancy = FALSE;
if ((cmap = pixGetColormap(pixs)) != NULL) {
if (pixcmapGetCount(cmap) < 256)
mapvacancy = TRUE;
}
if (pixGetDepth(pixs) == 1 || mapvacancy)
pixd = pixConvertTo8(pixs, TRUE);
else
pixd = pixConvertTo32(pixs);
if (!pixd)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
extractRGBValues(val, &rval, &gval, &bval);
if (pixGetDepth(pixd) == 8) { /* colormapped */
cmap = pixGetColormap(pixd);
pixcmapAddNewColor(cmap, rval, gval, bval, &newindex);
}
pixRenderBoxaArb(pixd, boxa, width, rval, gval, bval);
return pixd;
}
/*!
* pixDrawBoxaRandom()
*
* Input: pixs (any depth, can be cmapped)
* boxa (of boxes, to draw)
* width (thickness of line)
* Return: pixd (with box outlines drawn), or null on error
*
* Notes:
* (1) If pixs is 1 bpp, we draw the boxa using a colormap;
* otherwise, we convert to 32 bpp.
* (2) We use up to 254 different colors for drawing the boxes.
* (3) If boxes overlap, the later ones draw over earlier ones.
*/
PIX *
pixDrawBoxaRandom(PIX *pixs,
BOXA *boxa,
l_int32 width)
{
l_int32 i, n, rval, gval, bval, index;
BOX *box;
PIX *pixd;
PIXCMAP *cmap;
PTAA *ptaa;
PROCNAME("pixDrawBoxaRandom");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (!boxa)
return (PIX *)ERROR_PTR("boxa not defined", procName, NULL);
if (width < 1)
return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL);
if ((n = boxaGetCount(boxa)) == 0) {
L_WARNING("no boxes to draw; returning a copy", procName);
return pixCopy(NULL, pixs);
}
/* Input depth = 1 bpp; generate cmapped output */
if (pixGetDepth(pixs) == 1) {
ptaa = generatePtaaBoxa(boxa);
pixd = pixRenderRandomCmapPtaa(pixs, ptaa, 1, width, 1);
ptaaDestroy(&ptaa);
return pixd;
}
/* Generate rgb output */
pixd = pixConvertTo32(pixs);
cmap = pixcmapCreateRandom(8, 1, 1);
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
index = 1 + (i % 254);
pixcmapGetColor(cmap, index, &rval, &gval, &bval);
pixRenderBoxArb(pixd, box, width, rval, gval, bval);
boxDestroy(&box);
}
pixcmapDestroy(&cmap);
return pixd;
}
/*!
* boxaaDisplay()
*
* Input: boxaa
* linewba (line width to display boxa)
* linewb (line width to display box)
* colorba (color to display boxa)
* colorb (color to display box)
* w (of pix; use 0 if determined by boxaa)
* h (of pix; use 0 if determined by boxaa)
* Return: 0 if OK, 1 on error
*/
PIX *
boxaaDisplay(BOXAA *boxaa,
l_int32 linewba,
l_int32 linewb,
l_uint32 colorba,
l_uint32 colorb,
l_int32 w,
l_int32 h)
{
l_int32 i, j, n, m, rbox, gbox, bbox, rboxa, gboxa, bboxa;
BOX *box;
BOXA *boxa;
PIX *pix;
PIXCMAP *cmap;
PROCNAME("boxaaDisplay");
if (!boxaa)
return (PIX *)ERROR_PTR("boxaa not defined", procName, NULL);
if (w == 0 || h == 0)
boxaaGetExtent(boxaa, &w, &h, NULL);
pix = pixCreate(w, h, 8);
cmap = pixcmapCreate(8);
pixSetColormap(pix, cmap);
extractRGBValues(colorb, &rbox, &gbox, &bbox);
extractRGBValues(colorba, &rboxa, &gboxa, &bboxa);
pixcmapAddColor(cmap, 255, 255, 255);
pixcmapAddColor(cmap, rbox, gbox, bbox);
pixcmapAddColor(cmap, rboxa, gboxa, bboxa);
n = boxaaGetCount(boxaa);
for (i = 0; i < n; i++) {
boxa = boxaaGetBoxa(boxaa, i, L_CLONE);
boxaGetExtent(boxa, NULL, NULL, &box);
pixRenderBoxArb(pix, box, linewba, rboxa, gboxa, bboxa);
boxDestroy(&box);
m = boxaGetCount(boxa);
for (j = 0; j < m; j++) {
box = boxaGetBox(boxa, j, L_CLONE);
pixRenderBoxArb(pix, box, linewb, rbox, gbox, bbox);
boxDestroy(&box);
}
boxaDestroy(&boxa);
}
return pix;
}
/*---------------------------------------------------------------------*
* Split mask components into Boxa *
*---------------------------------------------------------------------*/
/*!
* pixSplitIntoBoxa()
*
* Input: pixs (1 bpp)
* minsum (minimum pixels to trigger propagation)
* skipdist (distance before computing sum for propagation)
* delta (difference required to stop propagation)
* maxbg (maximum number of allowed bg pixels in ref scan)
* maxcomps (use 0 for unlimited number of subdivided components)
* remainder (set to 1 to get b.b. of remaining stuff)
* Return: boxa (of rectangles covering the fg of pixs), or null on error
*
* Notes:
* (1) This generates a boxa of rectangles that covers
* the fg of a mask. For each 8-connected component in pixs,
* it does a greedy partitioning, choosing the largest
* rectangle found from each of the four directions at each iter.
* See pixSplitComponentsIntoBoxa() for details.
* (2) The input parameters give some flexibility for boundary
* noise. The resulting set of rectangles may cover some
* bg pixels.
* (3) This should be used when there are a small number of
* mask components, each of which has sides that are close
* to horizontal and vertical. The input parameters @delta
* and @maxbg determine whether or not holes in the mask are covered.
* (4) The parameter @maxcomps gives the maximum number of allowed
* rectangles extracted from any single connected component.
* Use 0 if no limit is to be applied.
* (5) The flag @remainder specifies whether we take a final bounding
* box for anything left after the maximum number of allowed
* rectangle is extracted.
*/
BOXA *
pixSplitIntoBoxa(PIX *pixs,
l_int32 minsum,
l_int32 skipdist,
l_int32 delta,
l_int32 maxbg,
l_int32 maxcomps,
l_int32 remainder)
{
l_int32 i, n;
BOX *box;
BOXA *boxa, *boxas, *boxad;
PIX *pix;
PIXA *pixas;
PROCNAME("pixSplitIntoBoxa");
if (!pixs || pixGetDepth(pixs) != 1)
return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
boxas = pixConnComp(pixs, &pixas, 8);
n = boxaGetCount(boxas);
boxad = boxaCreate(0);
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixas, i, L_CLONE);
box = boxaGetBox(boxas, i, L_CLONE);
boxa = pixSplitComponentIntoBoxa(pix, box, minsum, skipdist,
delta, maxbg, maxcomps, remainder);
boxaJoin(boxad, boxa, 0, 0);
pixDestroy(&pix);
boxDestroy(&box);
boxaDestroy(&boxa);
}
pixaDestroy(&pixas);
boxaDestroy(&boxas);
return boxad;
}
/*!
* pixSplitComponentIntoBoxa()
*
* Input: pixs (1 bpp)
* box (<optional> location of pixs w/rt an origin)
* minsum (minimum pixels to trigger propagation)
* skipdist (distance before computing sum for propagation)
* delta (difference required to stop propagation)
* maxbg (maximum number of allowed bg pixels in ref scan)
* maxcomps (use 0 for unlimited number of subdivided components)
* remainder (set to 1 to get b.b. of remaining stuff)
* Return: boxa (of rectangles covering the fg of pixs), or null on error
*
* Notes:
* (1) This generates a boxa of rectangles that covers
* the fg of a mask. It does so by a greedy partitioning of
* the mask, choosing the largest rectangle found from
* each of the four directions at each step.
* (2) The input parameters give some flexibility for boundary
* noise. The resulting set of rectangles must cover all
* the fg pixels and, in addition, may cover some bg pixels.
* Using small input parameters on a noiseless mask (i.e., one
* that has only large vertical and horizontal edges) will
* result in a proper covering of only the fg pixels of the mask.
* (3) The input is assumed to be a single connected component, that
* may have holes. From each side, sweep inward, counting
* the pixels. If the count becomes greater than @minsum,
* and we have moved forward a further amount @skipdist,
* record that count ('countref'), but don't accept if the scan
* contains more than @maxbg bg pixels. Continue the scan
* until we reach a count that differs from countref by at
* least @delta, at which point the propagation stops. The box
* swept out gets a score, which is the sum of fg pixels
* minus a penalty. The penalty is the number of bg pixels
* in the box. This is done from all four sides, and the
* side with the largest score is saved as a rectangle.
* The process repeats until there is either no rectangle
* left, or there is one that can't be captured from any
* direction. For the latter case, we simply accept the
* last rectangle.
* (4) The input box is only used to specify the location of
* the UL corner of pixs, with respect to an origin that
* typically represents the UL corner of an underlying image,
* of which pixs is one component. If @box is null,
* the UL corner is taken to be (0, 0).
* (5) The parameter @maxcomps gives the maximum number of allowed
* rectangles extracted from any single connected component.
* Use 0 if no limit is to be applied.
* (6) The flag @remainder specifies whether we take a final bounding
* box for anything left after the maximum number of allowed
* rectangle is extracted.
* (7) So if @maxcomps > 0, it specifies that we want no more than
* the first @maxcomps rectangles that satisfy the input
* criteria. After this, we can get a final rectangle that
* bounds everything left over by setting @remainder == 1.
* If @remainder == 0, we only get rectangles that satisfy
* the input criteria.
* (8) It should be noted that the removal of rectangles can
* break the original c.c. into several c.c.
* (9) Summing up:
* * If @maxcomp == 0, the splitting proceeds as far as possible.
* * If @maxcomp > 0, the splitting stops when @maxcomps are
* found, or earlier if no more components can be selected.
* * If @remainder == 1 and components remain that cannot be
* selected, they are returned as a single final rectangle;
* otherwise, they are ignored.
*/
BOXA *
pixSplitComponentIntoBoxa(PIX *pix,
BOX *box,
l_int32 minsum,
l_int32 skipdist,
l_int32 delta,
l_int32 maxbg,
l_int32 maxcomps,
l_int32 remainder)
{
l_int32 i, w, h, boxx, boxy, bx, by, bw, bh, maxdir, maxscore;
l_int32 iter;
BOX *boxs; /* shrinks as rectangular regions are removed */
BOX *boxt1, *boxt2, *boxt3;
BOXA *boxat; /* stores rectangle data for each side in an iteration */
BOXA *boxad;
NUMA *nascore, *nas;
PIX *pixs;
PROCNAME("pixSplitComponentIntoBoxa");
if (!pix || pixGetDepth(pix) != 1)
return (BOXA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
pixs = pixCopy(NULL, pix);
pixGetDimensions(pixs, &w, &h, NULL);
if (box)
boxGetGeometry(box, &boxx, &boxy, NULL, NULL);
else
boxx = boxy = 0;
boxs = boxCreate(0, 0, w, h);
boxad = boxaCreate(0);
iter = 0;
while (boxs != NULL) {
boxGetGeometry(boxs, &bx, &by, &bw, &bh);
boxat = boxaCreate(4); /* potential rectangular regions */
nascore = numaCreate(4);
for (i = 0; i < 4; i++) {
pixSearchForRectangle(pixs, boxs, minsum, skipdist, delta, maxbg,
i, boxat, nascore);
}
nas = numaGetSortIndex(nascore, L_SORT_DECREASING);
numaGetIValue(nas, 0, &maxdir);
numaGetIValue(nascore, maxdir, &maxscore);
#if DEBUG_SPLIT
fprintf(stderr, "Iteration: %d\n", iter);
boxPrintStreamInfo(stderr, boxs);
boxaWriteStream(stderr, boxat);
fprintf(stderr, "\nmaxdir = %d, maxscore = %d\n\n", maxdir, maxscore);
#endif /* DEBUG_SPLIT */
if (maxscore > 0) { /* accept this */
boxt1 = boxaGetBox(boxat, maxdir, L_CLONE);
boxt2 = boxTransform(boxt1, boxx, boxy, 1.0, 1.0);
boxaAddBox(boxad, boxt2, L_INSERT);
pixClearInRect(pixs, boxt1);
boxDestroy(&boxt1);
pixClipBoxToForeground(pixs, boxs, NULL, &boxt3);
boxDestroy(&boxs);
boxs = boxt3;
if (boxs) {
boxGetGeometry(boxs, NULL, NULL, &bw, &bh);
if (bw < 2 || bh < 2)
boxDestroy(&boxs); /* we're done */
}
}
else { /* no more valid rectangles can be found */
if (remainder == 1) { /* save the last box */
boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0);
boxaAddBox(boxad, boxt1, L_INSERT);
}
boxDestroy(&boxs); /* we're done */
}
boxaDestroy(&boxat);
numaDestroy(&nascore);
numaDestroy(&nas);
iter++;
if ((iter == maxcomps) && boxs) {
if (remainder == 1) { /* save the last box */
boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0);
boxaAddBox(boxad, boxt1, L_INSERT);
}
boxDestroy(&boxs); /* we're done */
}
}
pixDestroy(&pixs);
return boxad;
}
/*!
* pixSearchForRectangle()
*
* Input: pixs (1 bpp)
* boxs (current region to investigate)
* minsum (minimum pixels to trigger propagation)
* skipdist (distance before computing sum for propagation)
* delta (difference required to stop propagation)
* maxbg (maximum number of allowed bg pixels in ref scan)
* sideflag (side to search from)
* boxat (add result of rectangular region found here)
* nascore (add score for this rectangle here)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) See pixSplitByRectangles() for an explanation of the algorithm.
* This does the sweep from a single side. For each iteration
* in pixSplitByRectangles(), this will be called 4 times,
* for @sideflag = {0, 1, 2, 3}.
* (2) If a valid rectangle is not found, add a score of 0 and
* input a minimum box.
*/
static l_int32
pixSearchForRectangle(PIX *pixs,
BOX *boxs,
l_int32 minsum,
l_int32 skipdist,
l_int32 delta,
l_int32 maxbg,
l_int32 sideflag,
BOXA *boxat,
NUMA *nascore)
{
l_int32 bx, by, bw, bh, width, height, setref, atref;
l_int32 minincol, maxincol, mininrow, maxinrow, minval, maxval, bgref;
l_int32 x, y, x0, y0, xref, yref, colsum, rowsum, score, countref, diff;
void **lines1;
BOX *boxr;
PROCNAME("pixSearchForRectangle");
if (!pixs || pixGetDepth(pixs) != 1)
return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
if (!boxs)
return ERROR_INT("boxs not defined", procName, 1);
if (!boxat)
return ERROR_INT("boxat not defined", procName, 1);
if (!nascore)
return ERROR_INT("nascore not defined", procName, 1);
lines1 = pixGetLinePtrs(pixs, NULL);
boxGetGeometry(boxs, &bx, &by, &bw, &bh);
boxr = NULL;
setref = 0;
atref = 0;
maxval = 0;
minval = 100000;
score = 0; /* sum of all (fg - bg) pixels seen in the scan */
xref = yref = 100000; /* init to impossibly big number */
if (sideflag == L_FROM_LEFT) {
for (x = bx; x < bx + bw; x++) {
colsum = 0;
maxincol = 0;
minincol = 100000;
for (y = by; y < by + bh; y++) {
if (GET_DATA_BIT(lines1[y], x)) {
colsum++;
if (y > maxincol) maxincol = y;
if (y < minincol) minincol = y;
}
}
score += colsum;
/* Enough fg to sweep out a rectangle? */
if (!setref && colsum >= minsum) {
setref = 1;
xref = x + 10;
if (xref >= bx + bw)
goto failure;
}
/* Reached the reference line; save the count;
* if there is too much bg, the rectangle is invalid. */
if (setref && x == xref) {
atref = 1;
countref = colsum;
bgref = maxincol - minincol + 1 - countref;
if (bgref > maxbg)
goto failure;
}
/* Have we left the rectangle? If so, save it along
* with the score. */
if (atref) {
diff = L_ABS(colsum - countref);
if (diff >= delta || x == bx + bw - 1) {
height = maxval - minval + 1;
width = x - bx;
if (x == bx + bw - 1) width = x - bx + 1;
boxr = boxCreate(bx, minval, width, height);
score = 2 * score - width * height;
goto success;
}
}
maxval = L_MAX(maxval, maxincol);
minval = L_MIN(minval, minincol);
}
goto failure;
}
else if (sideflag == L_FROM_RIGHT) {
for (x = bx + bw - 1; x >= bx; x--) {
colsum = 0;
maxincol = 0;
minincol = 100000;
for (y = by; y < by + bh; y++) {
if (GET_DATA_BIT(lines1[y], x)) {
colsum++;
if (y > maxincol) maxincol = y;
if (y < minincol) minincol = y;
}
}
score += colsum;
if (!setref && colsum >= minsum) {
setref = 1;
xref = x - 10;
if (xref < bx)
goto failure;
}
if (setref && x == xref) {
atref = 1;
countref = colsum;
bgref = maxincol - minincol + 1 - countref;
if (bgref > maxbg)
goto failure;
}
if (atref) {
diff = L_ABS(colsum - countref);
if (diff >= delta || x == bx) {
height = maxval - minval + 1;
x0 = x + 1;
if (x == bx) x0 = x;
width = bx + bw - x0;
boxr = boxCreate(x0, minval, width, height);
score = 2 * score - width * height;
goto success;
}
}
maxval = L_MAX(maxval, maxincol);
minval = L_MIN(minval, minincol);
}
goto failure;
}
else if (sideflag == L_FROM_TOP) {
for (y = by; y < by + bh; y++) {
rowsum = 0;
maxinrow = 0;
mininrow = 100000;
for (x = bx; x < bx + bw; x++) {
if (GET_DATA_BIT(lines1[y], x)) {
rowsum++;
if (x > maxinrow) maxinrow = x;
if (x < mininrow) mininrow = x;
}
}
score += rowsum;
if (!setref && rowsum >= minsum) {
setref = 1;
yref = y + 10;
if (yref >= by + bh)
goto failure;
}
if (setref && y == yref) {
atref = 1;
countref = rowsum;
bgref = maxinrow - mininrow + 1 - countref;
if (bgref > maxbg)
goto failure;
}
if (atref) {
diff = L_ABS(rowsum - countref);
if (diff >= delta || y == by + bh - 1) {
width = maxval - minval + 1;
height = y - by;
if (y == by + bh - 1) height = y - by + 1;
boxr = boxCreate(minval, by, width, height);
score = 2 * score - width * height;
goto success;
}
}
maxval = L_MAX(maxval, maxinrow);
minval = L_MIN(minval, mininrow);
}
goto failure;
} else if (sideflag == L_FROM_BOTTOM) {
for (y = by + bh - 1; y >= by; y--) {
rowsum = 0;
maxinrow = 0;
mininrow = 100000;
for (x = bx; x < bx + bw; x++) {
if (GET_DATA_BIT(lines1[y], x)) {
rowsum++;
if (x > maxinrow) maxinrow = x;
if (x < mininrow) mininrow = x;
}
}
score += rowsum;
if (!setref && rowsum >= minsum) {
setref = 1;
yref = y - 10;
if (yref < by)
goto failure;
}
if (setref && y == yref) {
atref = 1;
countref = rowsum;
bgref = maxinrow - mininrow + 1 - countref;
if (bgref > maxbg)
goto failure;
}
if (atref) {
diff = L_ABS(rowsum - countref);
if (diff >= delta || y == by) {
width = maxval - minval + 1;
y0 = y + 1;
if (y == by) y0 = y;
height = by + bh - y0;
boxr = boxCreate(minval, y0, width, height);
score = 2 * score - width * height;
goto success;
}
}
maxval = L_MAX(maxval, maxinrow);
minval = L_MIN(minval, mininrow);
}
goto failure;
}
failure:
numaAddNumber(nascore, 0);
boxaAddBox(boxat, boxCreate(0, 0, 1, 1), L_INSERT); /* min box */
FREE(lines1);
return 0;
success:
numaAddNumber(nascore, score);
boxaAddBox(boxat, boxr, L_INSERT);
FREE(lines1);
return 0;
}