blob: 963117a08f5d2981a0d6159bd200b4f5da43d3f0 [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.
*====================================================================*/
/*
* pix3.c
*
* This file has these operations:
*
* (1) Mask-directed operations
* (2) Full-image bit-logical operations
* (3) Foreground pixel counting operations on 1 bpp images
* (4) Sum of pixel values
* (5) Mirrored tiling of a smaller image
*
*
* Masked operations
* l_int32 pixSetMasked()
* l_int32 pixSetMaskedGeneral()
* l_int32 pixCombineMasked()
* l_int32 pixCombineMaskedGeneral()
* l_int32 pixPaintThroughMask()
* PIX *pixPaintSelfThroughMask()
*
* One and two-image boolean operations on arbitrary depth images
* PIX *pixInvert()
* PIX *pixOr()
* PIX *pixAnd()
* PIX *pixXor()
* PIX *pixSubtract()
*
* Foreground pixel counting in 1 bpp images
* l_int32 pixZero()
* l_int32 pixCountPixels()
* NUMA *pixaCountPixels()
* l_int32 pixCountPixelsInRow()
* NUMA *pixCountPixelsByRow()
* l_int32 pixThresholdPixels()
* l_int32 *makePixelSumTab8()
* l_int32 *makePixelCentroidTab8()
*
* Sum of pixel values
* l_int32 pixSumPixelValues()
*
* Mirrored tiling
* PIX *pixMirroredTiling()
*
* Static helper function
* static l_int32 findTilePatchCenter()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
static l_int32 findTilePatchCenter(PIX *pixs, BOX *box, l_int32 dir,
l_uint32 targdist, l_uint32 *pdist,
l_int32 *pxc, l_int32 *pyc);
#ifndef NO_CONSOLE_IO
#define EQUAL_SIZE_WARNING 0
#endif /* ~NO_CONSOLE_IO */
/*-------------------------------------------------------------*
* Masked operations *
*-------------------------------------------------------------*/
/*!
* pixSetMasked()
*
* Input: pixd (1, 2, 4, 8, 16 or 32 bpp; or colormapped)
* pixm (<optional> 1 bpp mask; no operation if NULL)
* val (value to set at each masked pixel)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) In-place operation. Calls pixSetMaskedCmap() for colormapped
* images.
* (2) If pixm == NULL, a warning is given.
* (3) It is an implicitly aligned operation, where the UL
* corners of pixd and pixm coincide. A warning is
* issued if the two image sizes differ significantly,
* but the operation proceeds.
* (4) Each pixel in pixd that co-locates with an ON pixel
* in pixm is set to the specified input value.
* Other pixels in pixd are not changed.
* (5) You can visualize this as painting the color through
* the mask, as a stencil.
* (6) If you do not want to have the UL corners aligned,
* use the function pixSetMaskedGeneral(), which requires
* you to input the UL corner of pixm relative to pixd.
* (7) Implementation details: see comments in pixPaintThroughMask()
* for when we use rasterop to do the painting.
*/
l_int32
pixSetMasked(PIX *pixd,
PIX *pixm,
l_uint32 val)
{
l_int32 wd, hd, wm, hm, w, h, d, wpld, wplm;
l_int32 i, j, rval, gval, bval;
l_uint32 *datad, *datam, *lined, *linem;
PROCNAME("pixSetMasked");
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (!pixm) {
L_WARNING("no mask; nothing to do", procName);
return 0;
}
if (pixGetColormap(pixd)) {
extractRGBValues(val, &rval, &gval, &bval);
return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval);
}
if (pixGetDepth(pixm) != 1)
return ERROR_INT("pixm not 1 bpp", procName, 1);
d = pixGetDepth(pixd);
if (d == 1)
val &= 1;
else if (d == 2)
val &= 3;
else if (d == 4)
val &= 0x0f;
else if (d == 8)
val &= 0xff;
else if (d == 16)
val &= 0xffff;
else if (d != 32)
return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
pixGetDimensions(pixm, &wm, &hm, NULL);
/* If d == 1, use rasterop; it's about 25x faster */
if (d == 1) {
if (val == 0) {
PIX *pixmi = pixInvert(NULL, pixm);
pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0);
pixDestroy(&pixmi);
}
else /* val == 1 */
pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0);
return 0;
}
/* For d < 32, use rasterop for val == 0 (black); ~3x faster. */
if (d < 32 && val == 0) {
PIX *pixmd = pixUnpackBinary(pixm, d, 1);
pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0);
pixDestroy(&pixmd);
return 0;
}
/* For d < 32, use rasterop for val == maxval (white); ~3x faster. */
if (d < 32 && val == ((1 << d) - 1)) {
PIX *pixmd = pixUnpackBinary(pixm, d, 0);
pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0);
pixDestroy(&pixmd);
return 0;
}
pixGetDimensions(pixd, &wd, &hd, &d);
w = L_MIN(wd, wm);
h = L_MIN(hd, hm);
if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7) /* allow a small tolerance */
L_WARNING("pixd and pixm sizes differ", procName);
datad = pixGetData(pixd);
datam = pixGetData(pixm);
wpld = pixGetWpl(pixd);
wplm = pixGetWpl(pixm);
for (i = 0; i < h; i++) {
lined = datad + i * wpld;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if (GET_DATA_BIT(linem, j)) {
switch(d)
{
case 2:
SET_DATA_DIBIT(lined, j, val);
break;
case 4:
SET_DATA_QBIT(lined, j, val);
break;
case 8:
SET_DATA_BYTE(lined, j, val);
break;
case 16:
SET_DATA_TWO_BYTES(lined, j, val);
break;
case 32:
*(lined + j) = val;
break;
default:
return ERROR_INT("shouldn't get here", procName, 1);
}
}
}
}
return 0;
}
/*!
* pixSetMaskedGeneral()
*
* Input: pixd (8, 16 or 32 bpp)
* pixm (<optional> 1 bpp mask; no operation if null)
* val (value to set at each masked pixel)
* x, y (location of UL corner of pixm relative to pixd;
* can be negative)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This is an in-place operation.
* (2) Alignment is explicit. If you want the UL corners of
* the two images to be aligned, use pixSetMasked().
* (3) A typical use would be painting through the foreground
* of a small binary mask pixm, located somewhere on a
* larger pixd. Other pixels in pixd are not changed.
* (4) You can visualize this as painting the color through
* the mask, as a stencil.
* (5) This uses rasterop to handle clipping and different depths of pixd.
* (6) If pixd has a colormap, you should call pixPaintThroughMask().
* (7) Why is this function here, if pixPaintThroughMask() does the
* same thing, and does it more generally? I've retained it here
* to show how one can paint through a mask using only full
* image rasterops, rather than pixel peeking in pixm and poking
* in pixd. It's somewhat baroque, but I found it amusing.
*/
l_int32
pixSetMaskedGeneral(PIX *pixd,
PIX *pixm,
l_uint32 val,
l_int32 x,
l_int32 y)
{
l_int32 wm, hm, d;
PIX *pixmu, *pixc;
PROCNAME("pixSetMaskedGeneral");
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (!pixm) /* nothing to do */
return 0;
d = pixGetDepth(pixd);
if (d != 8 && d != 16 && d != 32)
return ERROR_INT("pixd not 8, 16 or 32 bpp", procName, 1);
if (pixGetDepth(pixm) != 1)
return ERROR_INT("pixm not 1 bpp", procName, 1);
/* Unpack binary to depth d, with inversion: 1 --> 0, 0 --> 0xff... */
if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL)
return ERROR_INT("pixmu not made", procName, 1);
/* Clear stenciled pixels in pixd */
pixGetDimensions(pixm, &wm, &hm, NULL);
pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0);
/* Generate image with requisite color */
if ((pixc = pixCreateTemplate(pixmu)) == NULL)
return ERROR_INT("pixc not made", procName, 1);
pixSetAllArbitrary(pixc, val);
/* Invert stencil mask, and paint color color into stencil */
pixInvert(pixmu, pixmu);
pixAnd(pixmu, pixmu, pixc);
/* Finally, repaint stenciled pixels, with val, in pixd */
pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0);
pixDestroy(&pixmu);
pixDestroy(&pixc);
return 0;
}
/*!
* pixCombineMasked()
*
* Input: pixd (1 bpp, 8 bpp gray or 32 bpp rgb; no cmap)
* pixs (1 bpp, 8 bpp gray or 32 bpp rgb; no cmap)
* pixm (<optional> 1 bpp mask; no operation if NULL)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) In-place operation; pixd is changed.
* (2) This sets each pixel in pixd that co-locates with an ON
* pixel in pixm to the corresponding value of pixs.
* (3) pixs and pixd must be the same depth and not colormapped.
* (4) All three input pix are aligned at the UL corner, and the
* operation is clipped to the intersection of all three images.
* (5) If pixm == NULL, it's a no-op.
* (6) Implementation: see notes in pixCombineMaskedGeneral().
* For 8 bpp selective masking, you might guess that it
* would be faster to generate an 8 bpp version of pixm,
* using pixConvert1To8(pixm, 0, 255), and then use a
* general combine operation
* d = (d & ~m) | (s & m)
* on a word-by-word basis. Not always. The word-by-word
* combine takes a time that is independent of the mask data.
* If the mask is relatively sparse, the byte-check method
* is actually faster!
*/
l_int32
pixCombineMasked(PIX *pixd,
PIX *pixs,
PIX *pixm)
{
l_int32 w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin;
l_int32 wpl, wpls, wplm, i, j, val;
l_uint32 *data, *datas, *datam, *line, *lines, *linem;
PIX *pixt;
PROCNAME("pixCombineMasked");
if (!pixm) /* nothing to do */
return 0;
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
pixGetDimensions(pixd, &w, &h, &d);
pixGetDimensions(pixs, &ws, &hs, &ds);
pixGetDimensions(pixm, &wm, &hm, &dm);
if (d != ds)
return ERROR_INT("pixs and pixd depths differ", procName, 1);
if (dm != 1)
return ERROR_INT("pixm not 1 bpp", procName, 1);
if (d != 1 && d != 8 && d != 32)
return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
if (pixGetColormap(pixd) || pixGetColormap(pixs))
return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
/* For d = 1, use rasterop. pixt is the part from pixs, under
* the fg of pixm, that is to be combined with pixd. We also
* use pixt to remove all fg of pixd that is under the fg of pixm.
* Then pixt and pixd are combined by ORing. */
wmin = L_MIN(w, L_MIN(ws, wm));
hmin = L_MIN(h, L_MIN(hs, hm));
if (d == 1) {
pixt = pixAnd(NULL, pixs, pixm);
pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
pixm, 0, 0);
pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
pixDestroy(&pixt);
return 0;
}
data = pixGetData(pixd);
datas = pixGetData(pixs);
datam = pixGetData(pixm);
wpl = pixGetWpl(pixd);
wpls = pixGetWpl(pixs);
wplm = pixGetWpl(pixm);
if (d == 8) {
for (i = 0; i < hmin; i++) {
line = data + i * wpl;
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < wmin; j++) {
if (GET_DATA_BIT(linem, j)) {
val = GET_DATA_BYTE(lines, j);
SET_DATA_BYTE(line, j, val);
}
}
}
}
else { /* d == 32 */
for (i = 0; i < hmin; i++) {
line = data + i * wpl;
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < wmin; j++) {
if (GET_DATA_BIT(linem, j))
line[j] = lines[j];
}
}
}
return 0;
}
/*!
* pixCombineMaskedGeneral()
*
* Input: pixd (1 bpp, 8 bpp gray or 32 bpp rgb)
* pixs (1 bpp, 8 bpp gray or 32 bpp rgb)
* pixm (<optional> 1 bpp mask)
* x, y (origin of pixs and pixm relative to pixd; can be negative)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) In-place operation; pixd is changed.
* (2) This is a generalized version of pixCombinedMasked(), where
* the source and mask can be placed at the same (arbitrary)
* location relative to pixd.
* (3) pixs and pixd must be the same depth and not colormapped.
* (4) The UL corners of both pixs and pixm are aligned with
* the point (x, y) of pixd, and the operation is clipped to
* the intersection of all three images.
* (5) If pixm == NULL, it's a no-op.
* (6) Implementation. There are two ways to do these. In the first,
* we use rasterop, ORing the part of pixs under the mask
* with pixd (which has been appropriately cleared there first).
* In the second, the mask is used one pixel at a time to
* selectively replace pixels of pixd with those of pixs.
* Here, we use rasterop for 1 bpp and pixel-wise replacement
* for 8 and 32 bpp. To use rasterop for 8 bpp, for example,
* we must first generate an 8 bpp version of the mask.
* The code is simple:
*
* Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255);
* Pix *pixt = pixAnd(NULL, pixs, pixm8);
* pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
* pixm8, 0, 0);
* pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST,
* pixt, 0, 0);
* pixDestroy(&pixt);
* pixDestroy(&pixm8);
*/
l_int32
pixCombineMaskedGeneral(PIX *pixd,
PIX *pixs,
PIX *pixm,
l_int32 x,
l_int32 y)
{
l_int32 d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin;
l_int32 wpl, wpls, wplm, i, j, val;
l_uint32 *data, *datas, *datam, *line, *lines, *linem;
PIX *pixt;
PROCNAME("pixCombineMaskedGeneral");
if (!pixm) /* nothing to do */
return 0;
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
pixGetDimensions(pixd, &w, &h, &d);
pixGetDimensions(pixs, &ws, &hs, &ds);
pixGetDimensions(pixm, &wm, &hm, &dm);
if (d != ds)
return ERROR_INT("pixs and pixd depths differ", procName, 1);
if (dm != 1)
return ERROR_INT("pixm not 1 bpp", procName, 1);
if (d != 1 && d != 8 && d != 32)
return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
if (pixGetColormap(pixd) || pixGetColormap(pixs))
return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
/* For d = 1, use rasterop. pixt is the part from pixs, under
* the fg of pixm, that is to be combined with pixd. We also
* use pixt to remove all fg of pixd that is under the fg of pixm.
* Then pixt and pixd are combined by ORing. */
wmin = L_MIN(ws, wm);
hmin = L_MIN(hs, hm);
if (d == 1) {
pixt = pixAnd(NULL, pixs, pixm);
pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
pixm, 0, 0);
pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
pixDestroy(&pixt);
return 0;
}
wpl = pixGetWpl(pixd);
data = pixGetData(pixd);
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
wplm = pixGetWpl(pixm);
datam = pixGetData(pixm);
for (i = 0; i < hmin; i++) {
if (y + i < 0 || y + i >= h) continue;
line = data + (y + i) * wpl;
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < wmin; j++) {
if (x + j < 0 || x + j >= w) continue;
if (GET_DATA_BIT(linem, j)) {
switch (d)
{
case 8:
val = GET_DATA_BYTE(lines, j);
SET_DATA_BYTE(line, x + j, val);
break;
case 32:
*(line + x + j) = *(lines + j);
break;
default:
return ERROR_INT("shouldn't get here", procName, 1);
}
}
}
}
return 0;
}
/*!
* pixPaintThroughMask()
*
* Input: pixd (1, 2, 4, 8, 16 or 32 bpp; or colormapped)
* pixm (<optional> 1 bpp mask)
* x, y (origin of pixm relative to pixd; can be negative)
* val (pixel value to set at each masked pixel)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) In-place operation. Calls pixSetMaskedCmap() for colormapped
* images.
* (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate
* number of least significant bits of val.
* (3) If pixm == NULL, it's a no-op.
* (4) The mask origin is placed at (x,y) on pixd, and the
* operation is clipped to the intersection of rectangles.
* (5) For rgb, the components in val are in the canonical locations,
* with red in location COLOR_RED, etc.
* (6) Implementation detail 1:
* For painting with val == 0 or val == maxval, you can use rasterop.
* If val == 0, invert the mask so that it's 0 over the region
* into which you want to write, and use PIX_SRC & PIX_DST to
* clear those pixels. To write with val = maxval (all 1's),
* use PIX_SRC | PIX_DST to set all bits under the mask.
* (7) Implementation detail 2:
* The rasterop trick can be used for depth > 1 as well.
* For val == 0, generate the mask for depth d from the binary
* mask using
* pixmd = pixUnpackBinary(pixm, d, 1);
* and use pixRasterop() with PIX_MASK. For val == maxval,
* pixmd = pixUnpackBinary(pixm, d, 0);
* and use pixRasterop() with PIX_PAINT.
* But note that if d == 32 bpp, it is about 3x faster to use
* the general implementation (not pixRasterop()).
* (8) Implementation detail 3:
* It might be expected that the switch in the inner loop will
* cause large branching delays and should be avoided.
* This is not the case, because the entrance is always the
* same and the compiler can correctly predict the jump.
*/
l_int32
pixPaintThroughMask(PIX *pixd,
PIX *pixm,
l_int32 x,
l_int32 y,
l_uint32 val)
{
l_int32 d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval;
l_uint32 *data, *datam, *line, *linem;
PROCNAME("pixPaintThroughMask");
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (!pixm) /* nothing to do */
return 0;
if (pixGetColormap(pixd)) {
extractRGBValues(val, &rval, &gval, &bval);
return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval);
}
if (pixGetDepth(pixm) != 1)
return ERROR_INT("pixm not 1 bpp", procName, 1);
d = pixGetDepth(pixd);
if (d == 1)
val &= 1;
else if (d == 2)
val &= 3;
else if (d == 4)
val &= 0x0f;
else if (d == 8)
val &= 0xff;
else if (d == 16)
val &= 0xffff;
else if (d != 32)
return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
pixGetDimensions(pixm, &wm, &hm, NULL);
/* If d == 1, use rasterop; it's about 25x faster. */
if (d == 1) {
if (val == 0) {
PIX *pixmi = pixInvert(NULL, pixm);
pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0);
pixDestroy(&pixmi);
}
else /* val == 1 */
pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0);
return 0;
}
/* For d < 32, use rasterop if val == 0 (black); ~3x faster. */
if (d < 32 && val == 0) {
PIX *pixmd = pixUnpackBinary(pixm, d, 1);
pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0);
pixDestroy(&pixmd);
return 0;
}
/* For d < 32, use rasterop if val == maxval (white); ~3x faster. */
if (d < 32 && val == ((1 << d) - 1)) {
PIX *pixmd = pixUnpackBinary(pixm, d, 0);
pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0);
pixDestroy(&pixmd);
return 0;
}
/* All other cases */
pixGetDimensions(pixd, &w, &h, NULL);
wpl = pixGetWpl(pixd);
data = pixGetData(pixd);
wplm = pixGetWpl(pixm);
datam = pixGetData(pixm);
for (i = 0; i < hm; i++) {
if (y + i < 0 || y + i >= h) continue;
line = data + (y + i) * wpl;
linem = datam + i * wplm;
for (j = 0; j < wm; j++) {
if (x + j < 0 || x + j >= w) continue;
if (GET_DATA_BIT(linem, j)) {
switch (d)
{
case 2:
SET_DATA_DIBIT(line, x + j, val);
break;
case 4:
SET_DATA_QBIT(line, x + j, val);
break;
case 8:
SET_DATA_BYTE(line, x + j, val);
break;
case 16:
SET_DATA_TWO_BYTES(line, x + j, val);
break;
case 32:
*(line + x + j) = val;
break;
default:
return ERROR_INT("shouldn't get here", procName, 1);
}
}
}
}
return 0;
}
/*!
* pixPaintSelfThroughMask()
*
* Input: pixd (8 bpp gray or 32 bpp rgb; not colormapped)
* pixm (1 bpp mask)
* x, y (origin of pixm relative to pixd; must not be negative)
* tilesize (requested size for tiling)
* searchdir (L_HORIZ, L_VERT)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) In-place operation; pixd is changed.
* (2) If pixm == NULL, it's a no-op.
* (3) The mask origin is placed at (x,y) on pixd, and the
* operation is clipped to the intersection of pixd and the
* fg of the mask.
* (4) The tilesize is the the requested size for tiling. The
* actual size for each c.c. will be bounded by the minimum
* dimension of the c.c. and the distance at which the tile
* center is located.
* (5) searchdir is the direction with respect to the b.b. of each
* mask component, from which the square patch is chosen and
* tiled onto the image, clipped by the mask component.
* (6) Specifically, a mirrored tiling, generated from pixd,
* is used to construct the pixels that are painted onto
* pixd through pixm.
*/
l_int32
pixPaintSelfThroughMask(PIX *pixd,
PIX *pixm,
l_int32 x,
l_int32 y,
l_int32 tilesize,
l_int32 searchdir)
{
l_int32 w, h, d, wm, hm, dm, i, n, xc, yc, bx, by, bw, bh;
l_int32 depth, cctilesize;
l_uint32 dist, minside, retval;
BOX *box, *boxt;
BOXA *boxa;
PIX *pix, *pixf, *pixdf, *pixt, *pixc;
PIXA *pixa;
PROCNAME("pixPaintSelfThroughMask");
if (!pixm) /* nothing to do */
return 0;
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (pixGetColormap(pixd) != NULL)
return ERROR_INT("pixd has colormap", procName, 1);
pixGetDimensions(pixd, &w, &h, &d);
if (d != 8 && d != 32)
return ERROR_INT("pixd not 8 or 32 bpp", procName, 1);
pixGetDimensions(pixm, &wm, &hm, &dm);
if (dm != 1)
return ERROR_INT("pixm not 1 bpp", procName, 1);
if (x < 0 || y < 0)
return ERROR_INT("x and y must be non-negative", procName, 1);
if (tilesize < 1)
return ERROR_INT("tilesize must be >= 1", procName, 1);
if (searchdir != L_HORIZ && searchdir != L_VERT)
return ERROR_INT("searchdir not in {L_HORIZ, L_VERT}", procName, 1);
/* Embed mask in full sized mask */
if (wm < w || hm < h) {
pixf = pixCreate(w, h, 1);
pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0);
}
else
pixf = pixClone(pixm);
/* Get connected components of mask */
boxa = pixConnComp(pixf, &pixa, 8);
if ((n = pixaGetCount(pixa)) == 0) {
L_WARNING("no fg in mask", procName);
pixDestroy(&pixf);
pixaDestroy(&pixa);
boxaDestroy(&boxa);
return 1;
}
/* Get distance function for the mask */
pixInvert(pixf, pixf);
depth = (tilesize < 256) ? 8 : 16;
pixdf = pixDistanceFunction(pixf, 4, depth, L_BOUNDARY_BG);
pixDestroy(&pixf);
/* For each c.c., generate a representative tile for texturizing
* and apply it through the mask. The input 'tilesize' is the
* requested value. findTilePatchCenter() returns the distance
* at which this patch can safely be found. */
retval = 0;
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixa, i, L_CLONE);
box = pixaGetBox(pixa, i, L_CLONE);
boxGetGeometry(box, &bx, &by, &bw, &bh);
minside = L_MIN(bw, bh);
findTilePatchCenter(pixdf, box, searchdir, L_MIN(minside, tilesize),
&dist, &xc, &yc);
cctilesize = L_MIN(tilesize, dist); /* for this c.c. */
if (cctilesize < 1) {
L_WARNING("region not found!", procName);
pixDestroy(&pix);
boxDestroy(&box);
retval = 1;
continue;
}
/* Extract the selected square from pixd, and generate
* an image the size of the b.b. of the c.c., which
* is then painted through the c.c. mask. */
boxt = boxCreate(L_MAX(0, xc - dist / 2), L_MAX(0, yc - dist / 2),
cctilesize, cctilesize);
pixt = pixClipRectangle(pixd, boxt, NULL);
pixc = pixMirroredTiling(pixt, bw, bh);
pixCombineMaskedGeneral(pixd, pixc, pix, bx, by);
pixDestroy(&pix);
pixDestroy(&pixt);
pixDestroy(&pixc);
boxDestroy(&box);
boxDestroy(&boxt);
}
pixDestroy(&pixdf);
pixaDestroy(&pixa);
boxaDestroy(&boxa);
return retval;
}
/*-------------------------------------------------------------*
* One and two-image boolean ops on arbitrary depth images *
*-------------------------------------------------------------*/
/*!
* pixInvert()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs
* Return: pixd, or null on error
*
* Notes:
* (1) This inverts pixs, for all pixel depths.
* (2) There are 3 cases:
* (a) pixd == null, ~src --> new pixd
* (b) pixd == pixs, ~src --> src (in-place)
* (c) pixd != pixs, ~src --> input pixd
* (3) For clarity, if the case is known, use these patterns:
* (a) pixd = pixInvert(NULL, pixs);
* (b) pixInvert(pixs, pixs);
* (c) pixInvert(pixd, pixs);
*/
PIX *
pixInvert(PIX *pixd,
PIX *pixs)
{
PROCNAME("pixInvert");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
/* Prepare pixd for in-place operation */
if ((pixd = pixCopy(pixd, pixs)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
PIX_NOT(PIX_DST), NULL, 0, 0); /* invert pixd */
return pixd;
}
/*!
* pixOr()
*
* Input: pixd (<optional>; this can be null, equal to pixs1,
* different from pixs1)
* pixs1 (can be == pixd)
* pixs2 (must be != pixd)
* Return: pixd always
*
* Notes:
* (1) This gives the union of two images with equal depth,
* aligning them to the the UL corner. pixs1 and pixs2
* need not have the same width and height.
* (2) There are 3 cases:
* (a) pixd == null, (src1 | src2) --> new pixd
* (b) pixd == pixs1, (src1 | src2) --> src1 (in-place)
* (c) pixd != pixs1, (src1 | src2) --> input pixd
* (3) For clarity, if the case is known, use these patterns:
* (a) pixd = pixOr(NULL, pixs1, pixs2);
* (b) pixOr(pixs1, pixs1, pixs2);
* (c) pixOr(pixd, pixs1, pixs2);
* (4) The size of the result is determined by pixs1.
* (5) The depths of pixs1 and pixs2 must be equal.
* (6) Note carefully that the order of pixs1 and pixs2 only matters
* for the in-place case. For in-place, you must have
* pixd == pixs1. Setting pixd == pixs2 gives an incorrect
* result: the copy puts pixs1 image data in pixs2, and
* the rasterop is then between pixs2 and pixs2 (a no-op).
*/
PIX *
pixOr(PIX *pixd,
PIX *pixs1,
PIX *pixs2)
{
PROCNAME("pixOr");
if (!pixs1)
return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
if (!pixs2)
return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
if (pixd == pixs2)
return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
#if EQUAL_SIZE_WARNING
if (!pixSizesEqual(pixs1, pixs2))
L_WARNING("pixs1 and pixs2 not equal sizes", procName);
#endif /* EQUAL_SIZE_WARNING */
/* Prepare pixd to be a copy of pixs1 */
if ((pixd = pixCopy(pixd, pixs1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
/* src1 | src2 --> dest */
pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
PIX_SRC | PIX_DST, pixs2, 0, 0);
return pixd;
}
/*!
* pixAnd()
*
* Input: pixd (<optional>; this can be null, equal to pixs1,
* different from pixs1)
* pixs1 (can be == pixd)
* pixs2 (must be != pixd)
* Return: pixd always
*
* Notes:
* (1) This gives the intersection of two images with equal depth,
* aligning them to the the UL corner. pixs1 and pixs2
* need not have the same width and height.
* (2) There are 3 cases:
* (a) pixd == null, (src1 & src2) --> new pixd
* (b) pixd == pixs1, (src1 & src2) --> src1 (in-place)
* (c) pixd != pixs1, (src1 & src2) --> input pixd
* (3) For clarity, if the case is known, use these patterns:
* (a) pixd = pixAnd(NULL, pixs1, pixs2);
* (b) pixAnd(pixs1, pixs1, pixs2);
* (c) pixAnd(pixd, pixs1, pixs2);
* (4) The size of the result is determined by pixs1.
* (5) The depths of pixs1 and pixs2 must be equal.
* (6) Note carefully that the order of pixs1 and pixs2 only matters
* for the in-place case. For in-place, you must have
* pixd == pixs1. Setting pixd == pixs2 gives an incorrect
* result: the copy puts pixs1 image data in pixs2, and
* the rasterop is then between pixs2 and pixs2 (a no-op).
*/
PIX *
pixAnd(PIX *pixd,
PIX *pixs1,
PIX *pixs2)
{
PROCNAME("pixAnd");
if (!pixs1)
return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
if (!pixs2)
return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
if (pixd == pixs2)
return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
#if EQUAL_SIZE_WARNING
if (!pixSizesEqual(pixs1, pixs2))
L_WARNING("pixs1 and pixs2 not equal sizes", procName);
#endif /* EQUAL_SIZE_WARNING */
/* Prepare pixd to be a copy of pixs1 */
if ((pixd = pixCopy(pixd, pixs1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
/* src1 & src2 --> dest */
pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
PIX_SRC & PIX_DST, pixs2, 0, 0);
return pixd;
}
/*!
* pixXor()
*
* Input: pixd (<optional>; this can be null, equal to pixs1,
* different from pixs1)
* pixs1 (can be == pixd)
* pixs2 (must be != pixd)
* Return: pixd always
*
* Notes:
* (1) This gives the XOR of two images with equal depth,
* aligning them to the the UL corner. pixs1 and pixs2
* need not have the same width and height.
* (2) There are 3 cases:
* (a) pixd == null, (src1 ^ src2) --> new pixd
* (b) pixd == pixs1, (src1 ^ src2) --> src1 (in-place)
* (c) pixd != pixs1, (src1 ^ src2) --> input pixd
* (3) For clarity, if the case is known, use these patterns:
* (a) pixd = pixXor(NULL, pixs1, pixs2);
* (b) pixXor(pixs1, pixs1, pixs2);
* (c) pixXor(pixd, pixs1, pixs2);
* (4) The size of the result is determined by pixs1.
* (5) The depths of pixs1 and pixs2 must be equal.
* (6) Note carefully that the order of pixs1 and pixs2 only matters
* for the in-place case. For in-place, you must have
* pixd == pixs1. Setting pixd == pixs2 gives an incorrect
* result: the copy puts pixs1 image data in pixs2, and
* the rasterop is then between pixs2 and pixs2 (a no-op).
*/
PIX *
pixXor(PIX *pixd,
PIX *pixs1,
PIX *pixs2)
{
PROCNAME("pixXor");
if (!pixs1)
return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
if (!pixs2)
return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
if (pixd == pixs2)
return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
#if EQUAL_SIZE_WARNING
if (!pixSizesEqual(pixs1, pixs2))
L_WARNING("pixs1 and pixs2 not equal sizes", procName);
#endif /* EQUAL_SIZE_WARNING */
/* Prepare pixd to be a copy of pixs1 */
if ((pixd = pixCopy(pixd, pixs1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
/* src1 ^ src2 --> dest */
pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
PIX_SRC ^ PIX_DST, pixs2, 0, 0);
return pixd;
}
/*!
* pixSubtract()
*
* Input: pixd (<optional>; this can be null, equal to pixs1,
* equal to pixs2, or different from both pixs1 and pixs2)
* pixs1 (can be == pixd)
* pixs2 (can be == pixd)
* Return: pixd always
*
* Notes:
* (1) This gives the set subtraction of two images with equal depth,
* aligning them to the the UL corner. pixs1 and pixs2
* need not have the same width and height.
* (2) Source pixs2 is always subtracted from source pixs1.
* The result is
* pixs1 \ pixs2 = pixs1 & (~pixs2)
* (3) There are 4 cases:
* (a) pixd == null, (src1 - src2) --> new pixd
* (b) pixd == pixs1, (src1 - src2) --> src1 (in-place)
* (c) pixd == pixs2, (src1 - src2) --> src2 (in-place)
* (d) pixd != pixs1 && pixd != pixs2),
* (src1 - src2) --> input pixd
* (4) For clarity, if the case is known, use these patterns:
* (a) pixd = pixSubtract(NULL, pixs1, pixs2);
* (b) pixSubtract(pixs1, pixs1, pixs2);
* (c) pixSubtract(pixs2, pixs1, pixs2);
* (d) pixSubtract(pixd, pixs1, pixs2);
* (5) The size of the result is determined by pixs1.
* (6) The depths of pixs1 and pixs2 must be equal.
*/
PIX *
pixSubtract(PIX *pixd,
PIX *pixs1,
PIX *pixs2)
{
l_int32 w, h;
PROCNAME("pixSubtract");
if (!pixs1)
return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
if (!pixs2)
return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
#if EQUAL_SIZE_WARNING
if (!pixSizesEqual(pixs1, pixs2))
L_WARNING("pixs1 and pixs2 not equal sizes", procName);
#endif /* EQUAL_SIZE_WARNING */
pixGetDimensions(pixs1, &w, &h, NULL);
if (!pixd) {
pixd = pixCopy(NULL, pixs1);
pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
pixs2, 0, 0); /* src1 & (~src2) */
}
else if (pixd == pixs1) {
pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
pixs2, 0, 0); /* src1 & (~src2) */
}
else if (pixd == pixs2) {
pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC,
pixs1, 0, 0); /* src1 & (~src2) */
}
else { /* pixd != pixs1 && pixd != pixs2 */
pixCopy(pixd, pixs1); /* sizes pixd to pixs1 if unequal */
pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
pixs2, 0, 0); /* src1 & (~src2) */
}
return pixd;
}
/*-------------------------------------------------------------*
* Pixel counting *
*-------------------------------------------------------------*/
/*!
* pixZero()
*
* Input: pix
* &empty (<return> 1 if all bits in image are 0; 0 otherwise)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) For a binary image, if there are no fg (black) pixels, empty = 1.
* (2) For a grayscale image, if all pixels are black (0), empty = 1.
* (3) For an RGB image, if all 4 components in every pixel is 0,
* empty = 1.
*/
l_int32
pixZero(PIX *pix,
l_int32 *pempty)
{
l_int32 w, h, wpl, i, j, fullwords, endbits;
l_uint32 endmask;
l_uint32 *data, *line;
PROCNAME("pixZero");
if (!pempty)
return ERROR_INT("pempty not defined", procName, 1);
*pempty = 1;
if (!pix)
return ERROR_INT("pix not defined", procName, 1);
w = pixGetWidth(pix) * pixGetDepth(pix);
h = pixGetHeight(pix);
wpl = pixGetWpl(pix);
data = pixGetData(pix);
fullwords = w / 32;
endbits = w & 31;
endmask = 0xffffffff << (32 - endbits);
for (i = 0; i < h; i++) {
line = data + wpl * i;
for (j = 0; j < fullwords; j++)
if (*line++) {
*pempty = 0;
return 0;
}
if (endbits) {
if (*line & endmask) {
*pempty = 0;
return 0;
}
}
}
return 0;
}
/*!
* pixCountPixels()
*
* Input: binary pix
* &count (<return> count of ON pixels)
* tab8 (<optional> 8-bit pixel lookup table)
* Return: 0 if OK; 1 on error
*/
l_int32
pixCountPixels(PIX *pix,
l_int32 *pcount,
l_int32 *tab8)
{
l_uint32 endmask;
l_int32 w, h, wpl, i, j;
l_int32 fullwords, endbits, sum;
l_int32 *tab;
l_uint32 *data;
PROCNAME("pixCountPixels");
if (!pcount)
return ERROR_INT("pcount not defined", procName, 1);
*pcount = 0;
if (!pix || pixGetDepth(pix) != 1)
return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
if (!tab8)
tab = makePixelSumTab8();
else
tab = tab8;
pixGetDimensions(pix, &w, &h, NULL);
wpl = pixGetWpl(pix);
data = pixGetData(pix);
fullwords = w >> 5;
endbits = w & 31;
endmask = 0xffffffff << (32 - endbits);
sum = 0;
for (i = 0; i < h; i++, data += wpl) {
for (j = 0; j < fullwords; j++) {
l_uint32 word = data[j];
if (word) {
sum += tab[word & 0xff] +
tab[(word >> 8) & 0xff] +
tab[(word >> 16) & 0xff] +
tab[(word >> 24) & 0xff];
}
}
if (endbits) {
l_uint32 word = data[j] & endmask;
if (word) {
sum += tab[word & 0xff] +
tab[(word >> 8) & 0xff] +
tab[(word >> 16) & 0xff] +
tab[(word >> 24) & 0xff];
}
}
}
*pcount = sum;
if (!tab8)
FREE(tab);
return 0;
}
/*!
* pixaCountPixels()
*
* Input: pixa (array of binary pix)
* Return: na of ON pixels in each pix, or null on error
*/
NUMA *
pixaCountPixels(PIXA *pixa)
{
l_int32 d, i, n, count;
l_int32 *tab;
NUMA *na;
PIX *pix;
PROCNAME("pixaCountPixels");
if (!pixa)
return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
if ((n = pixaGetCount(pixa)) == 0)
return numaCreate(1);
pix = pixaGetPix(pixa, 0, L_CLONE);
d = pixGetDepth(pix);
pixDestroy(&pix);
if (d != 1)
return (NUMA *)ERROR_PTR("pixa not 1 bpp", procName, NULL);
tab = makePixelSumTab8();
if ((na = numaCreate(n)) == NULL)
return (NUMA *)ERROR_PTR("na not made", procName, NULL);
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixa, i, L_CLONE);
pixCountPixels(pix, &count, tab);
numaAddNumber(na, count);
pixDestroy(&pix);
}
FREE(tab);
return na;
}
/*!
* pixCountPixelsInRow()
*
* Input: binary pix
* row number
* &count (<return> sum of ON pixels in raster line)
* tab8 (<optional> 8-bit pixel lookup table)
* Return: 0 if OK; 1 on error
*/
l_int32
pixCountPixelsInRow(PIX *pix,
l_int32 row,
l_int32 *pcount,
l_int32 *tab8)
{
l_uint32 word, endmask;
l_int32 j, w, h, wpl;
l_int32 fullwords, endbits, sum;
l_int32 *tab;
l_uint32 *line;
PROCNAME("pixCountPixelsInRow");
if (!pcount)
return ERROR_INT("pcount not defined", procName, 1);
*pcount = 0;
if (!pix || pixGetDepth(pix) != 1)
return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
pixGetDimensions(pix, &w, &h, NULL);
if (row < 0 || row >= h)
return ERROR_INT("row out of bounds", procName, 1);
wpl = pixGetWpl(pix);
line = pixGetData(pix) + row * wpl;
fullwords = w >> 5;
endbits = w & 31;
endmask = 0xffffffff << (32 - endbits);
if (!tab8)
tab = makePixelSumTab8();
else
tab = tab8;
sum = 0;
for (j = 0; j < fullwords; j++) {
word = line[j];
if (word) {
sum += tab[word & 0xff] +
tab[(word >> 8) & 0xff] +
tab[(word >> 16) & 0xff] +
tab[(word >> 24) & 0xff];
}
}
if (endbits) {
word = line[j] & endmask;
if (word) {
sum += tab[word & 0xff] +
tab[(word >> 8) & 0xff] +
tab[(word >> 16) & 0xff] +
tab[(word >> 24) & 0xff];
}
}
*pcount = sum;
if (!tab8)
FREE(tab);
return 0;
}
/*!
* pixCountPixelsByRow()
*
* Input: binary pix
* tab8 (<optional> 8-bit pixel lookup table)
* Return: na of counts, or null on error
*/
NUMA *
pixCountPixelsByRow(PIX *pix,
l_int32 *tab8)
{
l_int32 h, i, count;
l_int32 *tab;
NUMA *na;
PROCNAME("pixCountPixelsByRow");
if (!pix || pixGetDepth(pix) != 1)
return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
if (!tab8)
tab = makePixelSumTab8();
else
tab = tab8;
if ((na = numaCreate(h)) == NULL)
return (NUMA *)ERROR_PTR("na not made", procName, NULL);
h = pixGetHeight(pix);
for (i = 0; i < h; i++) {
pixCountPixelsInRow(pix, i, &count, tab);
numaAddNumber(na, count);
}
if (!tab8)
FREE(tab);
return na;
}
/*!
* pixThresholdPixels()
*
* Input: binary pix
* threshold
* &above (<return> 1 if above threshold;
* 0 if equal to or less than threshold)
* tab8 (<optional> 8-bit pixel lookup table)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This sums the ON pixels and returns immediately if the count
* goes above threshold. It is therefore more efficient
* for matching images (by running this function on the xor of
* the 2 images) than using pixCountPixels(), which counts all
* pixels before returning.
*/
l_int32
pixThresholdPixels(PIX *pix,
l_int32 thresh,
l_int32 *pabove,
l_int32 *tab8)
{
l_uint32 word, endmask;
l_int32 *tab;
l_int32 w, h, wpl, i, j;
l_int32 fullwords, endbits, sum;
l_uint32 *line, *data;
PROCNAME("pixThresholdPixels");
if (!pabove)
return ERROR_INT("pabove not defined", procName, 1);
*pabove = 0;
if (!pix || pixGetDepth(pix) != 1)
return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
if (!tab8)
tab = makePixelSumTab8();
else
tab = tab8;
pixGetDimensions(pix, &w, &h, NULL);
wpl = pixGetWpl(pix);
data = pixGetData(pix);
fullwords = w >> 5;
endbits = w & 31;
endmask = 0xffffffff << (32 - endbits);
sum = 0;
for (i = 0; i < h; i++) {
line = data + wpl * i;
for (j = 0; j < fullwords; j++) {
word = line[j];
if (word) {
sum += tab[word & 0xff] +
tab[(word >> 8) & 0xff] +
tab[(word >> 16) & 0xff] +
tab[(word >> 24) & 0xff];
}
}
if (endbits) {
word = line[j] & endmask;
if (word) {
sum += tab[word & 0xff] +
tab[(word >> 8) & 0xff] +
tab[(word >> 16) & 0xff] +
tab[(word >> 24) & 0xff];
}
}
if (sum > thresh) {
*pabove = 1;
if (!tab8)
FREE(tab);
return 0;
}
}
if (!tab8)
FREE(tab);
return 0;
}
/*!
* makePixelSumTab8()
*
* Input: void
* Return: table of 256 l_int32, or null on error
*
* Notes:
* (1) This table of integers gives the number of 1 bits
* in the 8 bit index.
*/
l_int32 *
makePixelSumTab8(void)
{
l_uint8 byte;
l_int32 i;
l_int32 *tab;
PROCNAME("makePixelSumTab8");
if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
for (i = 0; i < 256; i++) {
byte = (l_uint8)i;
tab[i] = (byte & 0x1) +
((byte >> 1) & 0x1) +
((byte >> 2) & 0x1) +
((byte >> 3) & 0x1) +
((byte >> 4) & 0x1) +
((byte >> 5) & 0x1) +
((byte >> 6) & 0x1) +
((byte >> 7) & 0x1);
}
return tab;
}
/*!
* makePixelCentroidTab8()
*
* Input: void
* Return: table of 256 l_int32, or null on error
*
* Notes:
* (1) This table of integers gives the centroid weight of the 1 bits
* in the 8 bit index. In other words, if sumtab is obtained by
* makePixelSumTab8, and centroidtab is obtained by
* makePixelCentroidTab8, then, for 1 <= i <= 255,
* centroidtab[i] / (float)sumtab[i]
* is the centroid of the 1 bits in the 8-bit index i, where the
* MSB is considered to have position 0 and the LSB is considered
* to have position 7.
*/
l_int32 *
makePixelCentroidTab8(void)
{
l_int32 i;
l_int32 *tab;
PROCNAME("makePixelCentroidTab8");
if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
tab[0] = 0;
tab[1] = 7;
for (i = 2; i < 4; i++) {
tab[i] = tab[i - 2] + 6;
}
for (i = 4; i < 8; i++) {
tab[i] = tab[i - 4] + 5;
}
for (i = 8; i < 16; i++) {
tab[i] = tab[i - 8] + 4;
}
for (i = 16; i < 32; i++) {
tab[i] = tab[i - 16] + 3;
}
for (i = 32; i < 64; i++) {
tab[i] = tab[i - 32] + 2;
}
for (i = 64; i < 128; i++) {
tab[i] = tab[i - 64] + 1;
}
for (i = 128; i < 256; i++) {
tab[i] = tab[i - 128];
}
return tab;
}
/*-------------------------------------------------------------*
* Sum of pixel values *
*-------------------------------------------------------------*/
/*!
* pixSumPixelValues()
*
* Input: pix (1, 2, 4, 8, 16, 32 bpp; not cmapped)
* box (<optional> if null, use entire image)
* &sum (<return> sum of pixel values in region)
* Return: 0 if OK; 1 on error
*/
l_int32
pixSumPixelValues(PIX *pix,
BOX *box,
l_float64 *psum)
{
l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh;
l_uint32 *data, *line;
l_float64 sum;
BOX *boxc;
PROCNAME("pixSumPixelValues");
if (!psum)
return ERROR_INT("psum not defined", procName, 1);
*psum = 0;
if (!pix)
return ERROR_INT("pix not defined", procName, 1);
if (pixGetColormap(pix) != NULL)
return ERROR_INT("pix is colormapped", procName, 1);
pixGetDimensions(pix, &w, &h, &d);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
return ERROR_INT("pix not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
wpl = pixGetWpl(pix);
data = pixGetData(pix);
boxc = NULL;
if (box) {
boxc = boxClipToRectangle(box, w, h);
if (!boxc)
return ERROR_INT("box outside image", procName, 1);
}
xstart = ystart = 0;
xend = w;
yend = h;
if (boxc) {
boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh);
xend = xstart + bw; /* 1 past the end */
yend = ystart + bh; /* 1 past the end */
boxDestroy(&boxc);
}
sum = 0;
for (i = ystart; i < yend; i++) {
line = data + i * wpl;
for (j = xstart; j < xend; j++) {
if (d == 1)
sum += GET_DATA_BIT(line, j);
else if (d == 2)
sum += GET_DATA_DIBIT(line, j);
else if (d == 4)
sum += GET_DATA_QBIT(line, j);
else if (d == 8)
sum += GET_DATA_BYTE(line, j);
else if (d == 16)
sum += GET_DATA_TWO_BYTES(line, j);
else if (d == 32)
sum += line[j];
}
}
*psum = sum;
return 0;
}
/*-------------------------------------------------------------*
* Mirrored tiling of a smaller image *
*-------------------------------------------------------------*/
/*!
* pixMirroredTiling()
*
* Input: pixs (8 or 32 bpp, small tile; to be replicated)
* w, h (dimensions of output pix)
* Return: pixd (usually larger pix, mirror-tiled with pixs),
* or null on error
*
* Notes:
* (1) This uses mirrored tiling, where each row alternates
* with LR flips and every column alternates with TB
* flips, such that the result is a tiling with identical
* 2 x 2 tiles, each of which is composed of these transforms:
* -----------------
* | 1 | LR |
* -----------------
* | TB | LR/TB |
* -----------------
*/
PIX *
pixMirroredTiling(PIX *pixs,
l_int32 w,
l_int32 h)
{
l_int32 wt, ht, d, i, j, nx, ny;
PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix;
PROCNAME("pixMirroredTiling");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
pixGetDimensions(pixs, &wt, &ht, &d);
if (wt <= 0 || ht <= 0)
return (PIX *)ERROR_PTR("pixs size illegal", procName, NULL);
if (d != 8 && d != 32)
return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL);
if ((pixd = pixCreate(w, h, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
nx = (w + wt - 1) / wt;
ny = (h + ht - 1) / ht;
pixsfx = pixFlipLR(NULL, pixs);
pixsfy = pixFlipTB(NULL, pixs);
pixsfxy = pixFlipTB(NULL, pixsfx);
for (i = 0; i < ny; i++) {
for (j = 0; j < nx; j++) {
pix = pixs;
if ((i & 1) && !(j & 1))
pix = pixsfy;
else if (!(i & 1) && (j & 1))
pix = pixsfx;
else if ((i & 1) && (j & 1))
pix = pixsfxy;
pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0);
}
}
pixDestroy(&pixsfx);
pixDestroy(&pixsfy);
pixDestroy(&pixsfxy);
return pixd;
}
/*!
* findTilePatchCenter()
*
* Input: pixs (8 or 16 bpp; distance function of a binary mask)
* box (region of pixs to search around)
* searchdir (L_HORIZ or L_VERT; direction to search)
* targdist (desired distance of selected patch center from fg)
* &dist (<return> actual distance of selected location)
* &xc, &yc (<return> location of selected patch center)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This looks for a patch of non-masked image, that is outside
* but near the input box. The input pixs is a distance
* function giving the distance from the fg in a binary mask.
* (2) The target distance implicitly specifies a desired size
* for the patch. The location of the center of the patch,
* and the actual distance from fg are returned.
* (3) If the target distance is larger than 255, a 16-bit distance
* transform is input.
* (4) It is assured that a square centered at (xc, yc) and of
* size 'dist' will not intersect with the fg of the binary
* mask that was used to generate pixs.
*/
static l_int32
findTilePatchCenter(PIX *pixs,
BOX *box,
l_int32 searchdir,
l_uint32 targdist,
l_uint32 *pdist,
l_int32 *pxc,
l_int32 *pyc)
{
l_int32 w, h, bx, by, bw, bh, left, right, top, bot, i, j;
l_uint32 val, maxval;
PROCNAME("findTilePatchCenter");
if (!pdist || !pxc || !pyc)
return ERROR_INT("&pdist, &pxc, &pyc not all defined", procName, 1);
*pdist = *pxc = *pyc = 0;
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
if (!box)
return ERROR_INT("box not defined", procName, 1);
pixGetDimensions(pixs, &w, &h, NULL);
boxGetGeometry(box, &bx, &by, &bw, &bh);
if (searchdir == L_HORIZ) {
left = bx; /* distance to left of box */
right = w - bx - bw + 1; /* distance to right of box */
maxval = 0;
if (left > right) { /* search to left */
for (j = bx - 1; j >= 0; j--) {
for (i = by; i < by + bh; i++) {
pixGetPixel(pixs, j, i, &val);
if (val > maxval) {
maxval = val;
*pxc = j;
*pyc = i;
*pdist = val;
if (val >= targdist)
return 0;
}
}
}
}
else { /* search to right */
for (j = bx + bw; j < w; j++) {
for (i = by; i < by + bh; i++) {
pixGetPixel(pixs, j, i, &val);
if (val > maxval) {
maxval = val;
*pxc = j;
*pyc = i;
*pdist = val;
if (val >= targdist)
return 0;
}
}
}
}
}
else { /* searchdir == L_VERT */
top = by; /* distance above box */
bot = h - by - bh + 1; /* distance below box */
maxval = 0;
if (top > bot) { /* search above */
for (i = by - 1; i >= 0; i--) {
for (j = bx; j < bx + bw; j++) {
pixGetPixel(pixs, j, i, &val);
if (val > maxval) {
maxval = val;
*pxc = j;
*pyc = i;
*pdist = val;
if (val >= targdist)
return 0;
}
}
}
}
else { /* search below */
for (i = by + bh; i < h; i++) {
for (j = bx; j < bx + bw; j++) {
pixGetPixel(pixs, j, i, &val);
if (val > maxval) {
maxval = val;
*pxc = j;
*pyc = i;
*pdist = val;
if (val >= targdist)
return 0;
}
}
}
}
}
pixGetPixel(pixs, *pxc, *pyc, pdist);
return 0;
}