mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-29 06:12:18 -06:00
1829 lines
59 KiB
C
1829 lines
59 KiB
C
/*====================================================================*
|
|
- 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;
|
|
}
|
|
|