/*!
* pixEmbedForRotation()
*
* Input: pixs (1, 2, 4, 8, 32 bpp rgb)
* angle (radians; clockwise is positive)
* incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
* width (original width; use 0 to avoid embedding)
* height (original height; use 0 to avoid embedding)
* Return: pixd, or null on error
*
* Notes:
* (1) For very small rotations, just return a clone.
* (2) Generate larger image to embed pixs if necessary, and
* place the center of the input image in the center.
* (3) Rotation brings either white or black pixels in
* from outside the image. For colormapped images where
* there is no white or black, a new color is added if
* possible for these pixels; otherwise, either the
* lightest or darkest color is used. In most cases,
* the colormap will be removed prior to rotation.
* (4) The dest is to be expanded so that no image pixels
* are lost after rotation. Input of the original width
* and height allows the expansion to stop at the maximum
* required size, which is a square with side equal to
* sqrt(w*w + h*h).
* (5) For an arbitrary angle, the expansion can be found by
* considering the UL and UR corners. As the image is
* rotated, these move in an arc centered at the center of
* the image. Normalize to a unit circle by dividing by half
* the image diagonal. After a rotation of T radians, the UL
* and UR corners are at points T radians along the unit
* circle. Compute the x and y coordinates of both these
* points and take the max of absolute values; these represent
* the half width and half height of the containing rectangle.
* The arithmetic is done using formulas for sin(a+b) and cos(a+b),
* where b = T. For the UR corner, sin(a) = h/d and cos(a) = w/d.
* For the UL corner, replace a by (pi - a), and you have
* sin(pi - a) = h/d, cos(pi - a) = -w/d. The equations
* given below follow directly.
*/
PIX *
pixEmbedForRotation(PIX *pixs,
l_float32 angle,
l_int32 incolor,
l_int32 width,
l_int32 height)
{
l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
l_float64 sina, cosa, fw, fh;
PIX *pixd;
PROCNAME("pixEmbedForRotation");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE)
return pixClone(pixs);
/* Test if big enough to hold any rotation of the original image */
pixGetDimensions(pixs, &w, &h, &d);
maxside = (l_int32)(sqrt((l_float64)(width * width) +
(l_float64)(height * height)) + 0.5);
if (w >= maxside && h >= maxside) /* big enough */
return pixClone(pixs);
/* Find the new sizes required to hold the image after rotation.
* Note that the new dimensions must be at least as large as those
* of pixs, because we're rasterop-ing into it before rotation. */
cosa = cos(angle);
sina = sin(angle);
fw = (l_float64)w;
fh = (l_float64)h;
w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5);
w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5);
h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5);
h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5);
wnew = L_MAX(w, L_MAX(w1, w2));
hnew = L_MAX(h, L_MAX(h1, h2));
if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyColormap(pixd, pixs);
pixCopySpp(pixd, pixs);
pixCopyText(pixd, pixs);
xoff = (wnew - w) / 2;
yoff = (hnew - h) / 2;
/* Set background to color to be rotated in */
setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
pixSetBlackOrWhite(pixd, setcolor);
/* Rasterop automatically handles all 4 channels for rgba */
pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
return pixd;
}
/*!
* pixRasteropHip()
*
* Input: pixd (in-place operation)
* by (top of horizontal band)
* bh (height of horizontal band)
* hshift (horizontal shift of band; hshift > 0 is to right)
* incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This rasterop translates a horizontal band of the
* image either left or right, bringing in either white
* or black pixels from outside the image.
* (2) The horizontal band extends the full width of pixd.
* (3) If a colormap exists, the nearest color to white or black
* is brought in.
*/
l_int32
pixRasteropHip(PIX *pixd,
l_int32 by,
l_int32 bh,
l_int32 hshift,
l_int32 incolor)
{
l_int32 w, h, d, index, op;
PIX *pixt;
PIXCMAP *cmap;
PROCNAME("pixRasteropHip");
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
return ERROR_INT("invalid value for incolor", procName, 1);
if (bh <= 0)
return ERROR_INT("bh must be > 0", procName, 1);
if (hshift == 0)
return 0;
pixGetDimensions(pixd, &w, &h, &d);
rasteropHipLow(pixGetData(pixd), h, d, pixGetWpl(pixd), by, bh, hshift);
cmap = pixGetColormap(pixd);
if (!cmap) {
if ((d == 1 && incolor == L_BRING_IN_BLACK) ||
(d > 1 && incolor == L_BRING_IN_WHITE))
op = PIX_SET;
else
op = PIX_CLR;
/* Set the pixels brought in at left or right */
if (hshift > 0)
pixRasterop(pixd, 0, by, hshift, bh, op, NULL, 0, 0);
else /* hshift < 0 */
pixRasterop(pixd, w + hshift, by, -hshift, bh, op, NULL, 0, 0);
return 0;
}
/* Get the nearest index and fill with that */
if (incolor == L_BRING_IN_BLACK)
pixcmapGetRankIntensity(cmap, 0.0, &index);
else /* white */
pixcmapGetRankIntensity(cmap, 1.0, &index);
pixt = pixCreate(L_ABS(hshift), bh, d);
pixSetAllArbitrary(pixt, index);
if (hshift > 0)
pixRasterop(pixd, 0, by, hshift, bh, PIX_SRC, pixt, 0, 0);
else /* hshift < 0 */
pixRasterop(pixd, w + hshift, by, -hshift, bh, PIX_SRC, pixt, 0, 0);
pixDestroy(&pixt);
return 0;
}
/*!
* \brief pixColorSegmentCluster()
*
* \param[in] pixs 32 bpp; 24-bit color
* \param[in] maxdist max euclidean dist to existing cluster
* \param[in] maxcolors max number of colors allowed in first pass
* \param[in] debugflag 1 for debug output; 0 otherwise
* \return pixd 8 bit with colormap, or NULL on error
*
* <pre>
* Notes:
* (1) This is phase 1. See description in pixColorSegment().
* (2) Greedy unsupervised classification. If the limit 'maxcolors'
* is exceeded, the computation is repeated with a larger
* allowed cluster size.
* (3) On each successive iteration, 'maxdist' is increased by a
* constant factor. See comments in pixColorSegment() for
* a guideline on parameter selection.
* Note that the diagonal of the 8-bit rgb color cube is about
* 440, so for 'maxdist' = 440, you are guaranteed to get 1 color!
* </pre>
*/
PIX *
pixColorSegmentCluster(PIX *pixs,
l_int32 maxdist,
l_int32 maxcolors,
l_int32 debugflag)
{
l_int32 w, h, newmaxdist, ret, niters, ncolors, success;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixColorSegmentCluster");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("must be rgb color", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreate(8);
pixSetColormap(pixd, cmap);
pixCopyResolution(pixd, pixs);
newmaxdist = maxdist;
niters = 0;
success = TRUE;
while (1) {
ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist,
maxcolors, debugflag);
niters++;
if (!ret) {
ncolors = pixcmapGetCount(cmap);
if (debugflag)
L_INFO("Success with %d colors after %d iters\n", procName,
ncolors, niters);
break;
}
if (niters == MAX_ALLOWED_ITERATIONS) {
L_WARNING("too many iters; newmaxdist = %d\n",
procName, newmaxdist);
success = FALSE;
break;
}
newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist);
}
if (!success) {
pixDestroy(&pixd);
return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL);
}
return pixd;
}
/*!
* pixaDisplayRandomCmap()
*
* Input: pixa (of 1 bpp components, with boxa)
* w, h (if set to 0, determines the size from the
* b.b. of the components in pixa)
* Return: pix (8 bpp, cmapped, with random colors on the components),
* or null on error
*
* Notes:
* (1) This uses the boxes to place each pix in the rendered composite.
* (2) By default, the background color is: black, cmap index 0.
* This can be changed by pixcmapResetColor()
*/
PIX *
pixaDisplayRandomCmap(PIXA *pixa,
l_int32 w,
l_int32 h)
{
l_int32 i, n, d, index, xb, yb, wb, hb;
BOXA *boxa;
PIX *pixs, *pixt, *pixd;
PIXCMAP *cmap;
PROCNAME("pixaDisplayRandomCmap");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
n = pixaGetCount(pixa);
if (n == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
/* Use the first pix in pixa to verify depth is 1 bpp */
pixs = pixaGetPix(pixa, 0, L_CLONE);
d = pixGetDepth(pixs);
pixDestroy(&pixs);
if (d != 1)
return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL);
/* If w and h not input, determine the minimum size required
* to contain the origin and all c.c. */
if (w == 0 || h == 0) {
boxa = pixaGetBoxa(pixa, L_CLONE);
boxaGetExtent(boxa, &w, &h, NULL);
boxaDestroy(&boxa);
}
/* Set up an 8 bpp dest pix, with a colormap with 254 random colors */
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreateRandom(8, 1, 1);
pixSetColormap(pixd, cmap);
/* Color each component and blit it in */
for (i = 0; i < n; i++) {
index = 1 + (i % 254);
pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
pixs = pixaGetPix(pixa, i, L_CLONE);
pixt = pixConvert1To8(NULL, pixs, 0, index);
pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
pixDestroy(&pixs);
pixDestroy(&pixt);
}
return pixd;
}
/*!
* wshedCreate()
*
* Input: pixs (8 bpp source)
* pixm (1 bpp 'marker' seed)
* mindepth (minimum depth; anything less is not saved)
* debugflag (1 for debug output)
* Return: WShed, or null on error
*
* Notes:
* (1) It is not necessary for the fg pixels in the seed image
* be at minima, or that they be isolated. We extract a
* single pixel from each connected component, and a seed
* anywhere in a watershed will eventually label the watershed
* when the filling level reaches it.
* (2) Set mindepth to some value to ignore noise in pixs that
* can create small local minima. Any watershed shallower
* than mindepth, even if it has a seed, will not be saved;
* It will either be incorporated in another watershed or
* eliminated.
*/
L_WSHED *
wshedCreate(PIX *pixs,
PIX *pixm,
l_int32 mindepth,
l_int32 debugflag)
{
l_int32 w, h;
L_WSHED *wshed;
PROCNAME("wshedCreate");
if (!pixs)
return (L_WSHED *)ERROR_PTR("pixs is not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (L_WSHED *)ERROR_PTR("pixs is not 8 bpp", procName, NULL);
if (!pixm)
return (L_WSHED *)ERROR_PTR("pixm is not defined", procName, NULL);
if (pixGetDepth(pixm) != 1)
return (L_WSHED *)ERROR_PTR("pixm is not 1 bpp", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
if (pixGetWidth(pixm) != w || pixGetHeight(pixm) != h)
return (L_WSHED *)ERROR_PTR("pixs/m sizes are unequal", procName, NULL);
if ((wshed = (L_WSHED *)CALLOC(1, sizeof(L_WSHED))) == NULL)
return (L_WSHED *)ERROR_PTR("wshed not made", procName, NULL);
wshed->pixs = pixClone(pixs);
wshed->pixm = pixClone(pixm);
wshed->mindepth = L_MAX(1, mindepth);
wshed->pixlab = pixCreate(w, h, 32);
pixSetAllArbitrary(wshed->pixlab, MAX_LABEL_VALUE);
wshed->pixt = pixCreate(w, h, 1);
wshed->lines8 = pixGetLinePtrs(pixs, NULL);
wshed->linem1 = pixGetLinePtrs(pixm, NULL);
wshed->linelab32 = pixGetLinePtrs(wshed->pixlab, NULL);
wshed->linet1 = pixGetLinePtrs(wshed->pixt, NULL);
wshed->debug = debugflag;
return wshed;
}
/*!
* pixConnCompTransform()
*
* Input: pixs (1 bpp)
* connect (connectivity: 4 or 8)
* depth (of pixd: 8 or 16 bpp; use 0 for auto determination)
* Return: pixd (8 or 16 bpp), or null on error
*
* Notes:
* (1) pixd is 8 or 16 bpp, and the pixel values label the fg component,
* starting with 1. Pixels in the bg are labelled 0.
* (2) If @depth = 0, the depth of pixd is 8 if the number of c.c.
* is less than 254, and 16 otherwise.
* (3) If @depth = 8, the assigned label for the n-th component is
* 1 + n % 254. We use mod 254 because 0 is uniquely assigned
* to black: e.g., see pixcmapCreateRandom(). Likewise,
* if @depth = 16, the assigned label uses mod(2^16 - 2).
*/
PIX *
pixConnCompTransform(PIX *pixs,
l_int32 connect,
l_int32 depth)
{
l_int32 i, n, index, w, h, xb, yb, wb, hb;
BOXA *boxa;
PIX *pix1, *pix2, *pixd;
PIXA *pixa;
PROCNAME("pixConnCompTransform");
if (!pixs || pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
if (connect != 4 && connect != 8)
return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL);
if (depth != 0 && depth != 8 && depth != 16)
return (PIX *)ERROR_PTR("depth must be 0, 8 or 16", procName, NULL);
boxa = pixConnComp(pixs, &pixa, connect);
n = pixaGetCount(pixa);
boxaDestroy(&boxa);
pixGetDimensions(pixs, &w, &h, NULL);
if (depth == 0) {
depth = (n < 254) ? 8 : 16;
}
pixd = pixCreate(w, h, depth);
if (n == 0) { /* no fg */
pixaDestroy(&pixa);
return pixd;
}
/* Label each component and blit it in */
for (i = 0; i < n; i++) {
pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
pix1 = pixaGetPix(pixa, i, L_CLONE);
if (depth == 8) {
index = 1 + (i % 254);
pix2 = pixConvert1To8(NULL, pix1, 0, index);
} else { /* depth == 16 */
index = 1 + (i % 0xfffe);
pix2 = pixConvert1To16(NULL, pix1, 0, index);
}
pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0);
pixDestroy(&pix1);
pixDestroy(&pix2);
}
pixaDestroy(&pixa);
return pixd;
}
/*!
* boxaGetCoverage()
*
* Input: boxa
* wc, hc (dimensions of overall clipping rectangle with UL
* corner at (0, 0) that is covered by the boxes.
* exactflag (1 for guaranteeing an exact result; 0 for getting
* an exact result only if the boxes do not overlap)
* &fract (<return> sum of box area as fraction of w * h)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) The boxes in boxa are clipped to the input rectangle.
* (2) * When @exactflag == 1, we generate a 1 bpp pix of size
* wc x hc, paint all the boxes black, and count the fg pixels.
* This can take 1 msec on a large page with many boxes.
* * When @exactflag == 0, we clip each box to the wc x hc region
* and sum the resulting areas. This is faster.
* * The results are the same when none of the boxes overlap
* within the wc x hc region.
*/
l_int32
boxaGetCoverage(BOXA *boxa,
l_int32 wc,
l_int32 hc,
l_int32 exactflag,
l_float32 *pfract)
{
l_int32 i, n, x, y, w, h, sum;
BOX *box, *boxc;
PIX *pixt;
PROCNAME("boxaGetCoverage");
if (!pfract)
return ERROR_INT("&fract not defined", procName, 1);
*pfract = 0.0;
if (!boxa)
return ERROR_INT("boxa not defined", procName, 1);
n = boxaGetCount(boxa);
if (n == 0)
return ERROR_INT("no boxes in boxa", procName, 1);
if (exactflag == 0) { /* quick and dirty */
sum = 0;
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) {
boxGetGeometry(boxc, NULL, NULL, &w, &h);
sum += w * h;
boxDestroy(&boxc);
}
boxDestroy(&box);
}
}
else { /* slower and exact */
pixt = pixCreate(wc, hc, 1);
for (i = 0; i < n; i++) {
box = boxaGetBox(boxa, i, L_CLONE);
boxGetGeometry(box, &x, &y, &w, &h);
pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0);
boxDestroy(&box);
}
pixCountPixels(pixt, &sum, NULL);
pixDestroy(&pixt);
}
*pfract = (l_float32)sum / (l_float32)(wc * hc);
return 0;
}
请发表评论