/*!
* 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;
}
// Tests each blob in the list to see if it is certain non-text using 2
// conditions:
// 1. blob overlaps a cell with high value in noise_density_ (previously set
// by ComputeNoiseDensity).
// OR 2. The blob overlaps more than max_blob_overlaps in *this grid. This
// condition is disabled with max_blob_overlaps == -1.
// If it does, the blob is declared non-text, and is used to mark up the
// nontext_mask. Such blobs are fully deleted, and non-noise blobs have their
// neighbours reset, as they may now point to deleted data.
// WARNING: The blobs list blobs may be in the *this grid, but they are
// not removed. If any deleted blobs might be in *this, then this must be
// Clear()ed immediately after MarkAndDeleteNonTextBlobs is called.
// If the win is not NULL, deleted blobs are drawn on it in red, and kept
// blobs are drawn on it in ok_color.
void CCNonTextDetect::MarkAndDeleteNonTextBlobs(BLOBNBOX_LIST* blobs,
int max_blob_overlaps,
ScrollView* win,
ScrollView::Color ok_color,
Pix* nontext_mask) {
int imageheight = tright().y() - bleft().x();
BLOBNBOX_IT blob_it(blobs);
BLOBNBOX_LIST dead_blobs;
BLOBNBOX_IT dead_it(&dead_blobs);
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
BLOBNBOX* blob = blob_it.data();
TBOX box = blob->bounding_box();
if (!noise_density_->RectMostlyOverThreshold(box, max_noise_count_) &&
(max_blob_overlaps < 0 ||
!BlobOverlapsTooMuch(blob, max_blob_overlaps))) {
blob->ClearNeighbours();
#ifndef GRAPHICS_DISABLED
if (win != NULL)
blob->plot(win, ok_color, ok_color);
#endif // GRAPHICS_DISABLED
} else {
if (noise_density_->AnyZeroInRect(box)) {
// There is a danger that the bounding box may overlap real text, so
// we need to render the outline.
Pix* blob_pix = blob->cblob()->render_outline();
pixRasterop(nontext_mask, box.left(), imageheight - box.top(),
box.width(), box.height(), PIX_SRC | PIX_DST,
blob_pix, 0, 0);
pixDestroy(&blob_pix);
} else {
if (box.area() < gridsize() * gridsize()) {
// It is a really bad idea to make lots of small components in the
// photo mask, so try to join it to a bigger area by expanding the
// box in a way that does not touch any zero noise density cell.
box = AttemptBoxExpansion(box, *noise_density_, gridsize());
}
// All overlapped cells are non-zero, so just mark the rectangle.
pixRasterop(nontext_mask, box.left(), imageheight - box.top(),
box.width(), box.height(), PIX_SET, NULL, 0, 0);
}
#ifndef GRAPHICS_DISABLED
if (win != NULL)
blob->plot(win, ScrollView::RED, ScrollView::RED);
#endif // GRAPHICS_DISABLED
// It is safe to delete the cblob now, as it isn't used by the grid
// or BlobOverlapsTooMuch, and the BLOBNBOXes will go away with the
// dead_blobs list.
// TODO(rays) delete the delete when the BLOBNBOX destructor deletes
// the cblob.
delete blob->cblob();
dead_it.add_to_end(blob_it.extract());
}
}
}
/*!
* pixaDisplay()
*
* Input: pixa
* w, h (if set to 0, determines the size from the
* b.b. of the components in pixa)
* Return: pix, or null on error
*
* Notes:
* (1) This uses the boxes to place each pix in the rendered composite.
* (2) Set w = h = 0 to use the b.b. of the components to determine
* the size of the returned pix.
* (3) Uses the first pix in pixa to determine the depth.
* (4) The background is written "white". On 1 bpp, each successive
* pix is "painted" (adding foreground), whereas for grayscale
* or color each successive pix is blitted with just the src.
* (5) If the pixa is empty, returns an empty 1 bpp pix.
*/
PIX *
pixaDisplay(PIXA *pixa,
l_int32 w,
l_int32 h)
{
l_int32 i, n, d, xb, yb, wb, hb;
BOXA *boxa;
PIX *pixt, *pixd;
PROCNAME("pixaDisplay");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
n = pixaGetCount(pixa);
if (n == 0 && w == 0 && h == 0)
return (PIX *)ERROR_PTR("no components; no size", procName, NULL);
if (n == 0) {
L_WARNING("no components; returning empty 1 bpp pix", procName);
return pixCreate(w, h, 1);
}
/* 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);
}
/* Use the first pix in pixa to determine the depth. */
pixt = pixaGetPix(pixa, 0, L_CLONE);
d = pixGetDepth(pixt);
pixDestroy(&pixt);
if ((pixd = pixCreate(w, h, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
if (d > 1)
pixSetAll(pixd);
for (i = 0; i < n; i++) {
if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) {
L_WARNING("no box found!", procName);
continue;
}
pixt = pixaGetPix(pixa, i, L_CLONE);
if (d == 1)
pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
else
pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt, 0, 0);
pixDestroy(&pixt);
}
return pixd;
}
// Renders the outline to the given pix, with left and top being
// the coords of the upper-left corner of the pix.
void C_OUTLINE::render(int left, int top, Pix* pix) const {
ICOORD pos = start;
for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
ICOORD next_step = step(stepindex);
if (next_step.y() < 0) {
pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1,
PIX_NOT(PIX_DST), NULL, 0, 0);
} else if (next_step.y() > 0) {
pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1,
PIX_NOT(PIX_DST), NULL, 0, 0);
}
pos += next_step;
}
}
/*!
* \brief pixConnCompTransform()
*
* \param[in] pixs 1 bpp
* \param[in] connect connectivity: 4 or 8
* \param[in] depth of pixd: 8 or 16 bpp; use 0 for auto determination
* \return pixd 8, 16 or 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) pixd is 8, 16 or 32 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, 16 if the number of c.c is less than 0xfffe,
* and 32 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), and
* if %depth = 32, no mod is taken.
* </pre>
*/
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 && depth != 32)
return (PIX *)ERROR_PTR("depth must be 0, 8, 16 or 32", procName, NULL);
boxa = pixConnComp(pixs, &pixa, connect);
n = pixaGetCount(pixa);
boxaDestroy(&boxa);
pixGetDimensions(pixs, &w, &h, NULL);
if (depth == 0) {
if (n < 254)
depth = 8;
else if (n < 0xfffe)
depth = 16;
else
depth = 32;
}
pixd = pixCreate(w, h, depth);
pixSetSpp(pixd, 1);
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 if (depth == 16) {
index = 1 + (i % 0xfffe);
pix2 = pixConvert1To16(NULL, pix1, 0, index);
} else { /* depth == 32 */
index = 1 + i;
pix2 = pixConvert1To32(NULL, pix1, 0, index);
}
pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0);
pixDestroy(&pix1);
pixDestroy(&pix2);
}
pixaDestroy(&pixa);
return pixd;
}
/**
* Returns an image of the current object at the given level in greyscale
* if available in the input. To guarantee a binary image use BinaryImage.
* NOTE that in order to give the best possible image, the bounds are
* expanded slightly over the binary connected component, by the supplied
* padding, so the top-left position of the returned image is returned
* in (left,top). These will most likely not match the coordinates
* returned by BoundingBox.
* Use pixDestroy to delete the image after use.
*/
Pix* PageIterator::GetImage(PageIteratorLevel level, int padding,
int* left, int* top) const {
int right, bottom;
if (!BoundingBox(level, left, top, &right, &bottom))
return NULL;
Pix* pix = tesseract_->pix_grey();
if (pix == NULL)
return GetBinaryImage(level);
// Expand the box.
*left = MAX(*left - padding, 0);
*top = MAX(*top - padding, 0);
right = MIN(right + padding, rect_width_);
bottom = MIN(bottom + padding, rect_height_);
Box* box = boxCreate(*left, *top, right - *left, bottom - *top);
Pix* grey_pix = pixClipRectangle(pix, box, NULL);
boxDestroy(&box);
if (level == RIL_BLOCK) {
Pix* mask = it_->block()->block->render_mask();
Pix* expanded_mask = pixCreate(right - *left, bottom - *top, 1);
pixRasterop(expanded_mask, padding, padding,
pixGetWidth(mask), pixGetHeight(mask),
PIX_SRC, mask, 0, 0);
pixDestroy(&mask);
pixDilateBrick(expanded_mask, expanded_mask, 2*padding + 1, 2*padding + 1);
pixInvert(expanded_mask, expanded_mask);
pixSetMasked(grey_pix, expanded_mask, 255);
pixDestroy(&expanded_mask);
}
return grey_pix;
}
/*!
* pixAddWithIndicator()
*
* Input: pixs (1 bpp pix from which components are added; in-place)
* pixa (of connected components, some of which will be put
* into pixs)
* na (numa indicator: add components corresponding to 1s)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This complements pixRemoveWithIndicator(). Here, the selected
* components are added to pixs.
*/
l_int32
pixAddWithIndicator(PIX *pixs,
PIXA *pixa,
NUMA *na)
{
l_int32 i, n, ival, x, y, w, h;
BOX *box;
PIX *pix;
PROCNAME("pixAddWithIndicator");
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
if (!pixa)
return ERROR_INT("pixa not defined", procName, 1);
if (!na)
return ERROR_INT("na not defined", procName, 1);
n = pixaGetCount(pixa);
if (n != numaGetCount(na))
return ERROR_INT("pixa and na sizes not equal", procName, 1);
for (i = 0; i < n; i++) {
numaGetIValue(na, i, &ival);
if (ival == 1) {
pix = pixaGetPix(pixa, i, L_CLONE);
box = pixaGetBox(pixa, i, L_CLONE);
boxGetGeometry(box, &x, &y, &w, &h);
pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
boxDestroy(&box);
pixDestroy(&pix);
}
}
return 0;
}
// Compute the connected components in a line
Boxa * CubeLineSegmenter::ComputeLineConComps(Pix *line_mask_pix,
Box *line_box,
Pixa **con_comps_pixa) {
// clone the line mask
Pix *line_pix = pixClone(line_mask_pix);
if (line_pix == NULL) {
return NULL;
}
// AND with the image to get the actual line
pixRasterop(line_pix, 0, 0, line_pix->w, line_pix->h,
PIX_SRC & PIX_DST, img_, line_box->x, line_box->y);
// compute the connected components of the line to be merged
Boxa *line_con_comps = pixConnComp(line_pix, con_comps_pixa, 8);
pixDestroy(&line_pix);
// offset boxes by the bbox of the line
for (int con = 0; con < line_con_comps->n; con++) {
line_con_comps->box[con]->x += line_box->x;
line_con_comps->box[con]->y += line_box->y;
}
return line_con_comps;
}
main(int argc,
char **argv)
{
l_int32 i, j, equal;
PIX *pixs, *pixt, *pixd;
static char mainName[] = "rasteropip_reg";
pixs = pixRead("test8.jpg");
pixt = pixCopy(NULL, pixs);
/* Copy, in-place and one COLUMN at a time, from the right
side to the left side. */
for (j = 0; j < 200; j++)
pixRasterop(pixs, 20 + j, 20, 1, 250, PIX_SRC, pixs, 250 + j, 20);
pixDisplay(pixs, 50, 50);
/* Copy, in-place and one ROW at a time, from the right
side to the left side. */
for (i = 0; i < 250; i++)
pixRasterop(pixt, 20, 20 + i, 200, 1, PIX_SRC, pixt, 250, 20 + i);
pixDisplay(pixt, 620, 50);
/* Test */
pixEqual(pixs, pixt, &equal);
if (equal)
fprintf(stderr, "OK: images are the same\n");
else
fprintf(stderr, "Error: images are different\n");
pixWrite("/tmp/junkpix.png", pixs, IFF_PNG);
pixDestroy(&pixs);
pixDestroy(&pixt);
/* Show the mirrored border, which uses the general
pixRasterop() on an image in-place. */
pixs = pixRead("test8.jpg");
pixt = pixRemoveBorder(pixs, 40);
pixd = pixAddMirroredBorder(pixt, 25, 25, 25, 25);
pixDisplay(pixd, 50, 550);
pixDestroy(&pixs);
pixDestroy(&pixt);
pixDestroy(&pixd);
return 0;
}
/*!
* 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;
}
/*!
* \brief recogGetWindowedArea()
*
* \param[in] recog
* \param[in] index of template
* \param[in] x pixel position of left hand edge of template
* \param[out] pdely y shift of template relative to pix1
* \param[out] pwsum number of fg pixels in window of pixs
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This is called after the best path has been found through
* the trellis, in order to produce a correlation that can be used
* to evaluate the confidence we have in the identification.
* The correlation is |1 \& 2|^2 / (|1| * |2|).
* |1 \& 2| is given by the count array, |2| is found from
* nasum_u[], and |1| is wsum returned from this function.
* </pre>
*/
static l_int32
recogGetWindowedArea(L_RECOG *recog,
l_int32 index,
l_int32 x,
l_int32 *pdely,
l_int32 *pwsum)
{
l_int32 w1, h1, w2, h2;
PIX *pix1, *pix2, *pixt;
L_RDID *did;
PROCNAME("recogGetWindowedArea");
if (pdely) *pdely = 0;
if (pwsum) *pwsum = 0;
if (!pdely || !pwsum)
return ERROR_INT("&dely and &wsum not both defined", procName, 1);
if (!recog)
return ERROR_INT("recog not defined", procName, 1);
if ((did = recogGetDid(recog)) == NULL)
return ERROR_INT("did not defined", procName, 1);
if (index < 0 || index >= did->narray)
return ERROR_INT("invalid index", procName, 1);
pix1 = did->pixs;
pixGetDimensions(pix1, &w1, &h1, NULL);
if (x >= w1)
return ERROR_INT("invalid x position", procName, 1);
pix2 = pixaGetPix(recog->pixa_u, index, L_CLONE);
pixGetDimensions(pix2, &w2, &h2, NULL);
if (w1 < w2) {
L_INFO("template %d too small\n", procName, index);
pixDestroy(&pix2);
return 0;
}
*pdely = did->delya[index][x];
pixt = pixCreate(w2, h1, 1);
pixRasterop(pixt, 0, *pdely, w2, h2, PIX_SRC, pix2, 0, 0);
pixRasterop(pixt, 0, 0, w2, h1, PIX_SRC & PIX_DST, pix1, x, 0);
pixCountPixels(pixt, pwsum, recog->sumtab);
pixDestroy(&pix2);
pixDestroy(&pixt);
return 0;
}
main(int argc,
char **argv)
{
l_int32 i, j;
PIX *pixs, *pixt, *pixd;
L_REGPARAMS *rp;
if (regTestSetup(argc, argv, &rp))
return 1;
pixs = pixRead("test8.jpg");
pixt = pixCopy(NULL, pixs);
/* Copy, in-place and one COLUMN at a time, from the right
side to the left side. */
for (j = 0; j < 200; j++)
pixRasterop(pixs, 20 + j, 20, 1, 250, PIX_SRC, pixs, 250 + j, 20);
pixDisplayWithTitle(pixs, 50, 50, "in-place copy", rp->display);
/* Copy, in-place and one ROW at a time, from the right
side to the left side. */
for (i = 0; i < 250; i++)
pixRasterop(pixt, 20, 20 + i, 200, 1, PIX_SRC, pixt, 250, 20 + i);
/* Test */
regTestComparePix(rp, pixs, pixt); /* 0 */
pixDestroy(&pixs);
pixDestroy(&pixt);
/* Show the mirrored border, which uses the general
pixRasterop() on an image in-place. */
pixs = pixRead("test8.jpg");
pixt = pixRemoveBorder(pixs, 40);
pixd = pixAddMirroredBorder(pixt, 40, 40, 40, 40);
regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 1 */
pixDisplayWithTitle(pixd, 650, 50, "mirrored border", rp->display);
pixDestroy(&pixs);
pixDestroy(&pixt);
pixDestroy(&pixd);
return regTestCleanup(rp);
}
/*!
* 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;
}
// Returns a binary Pix mask with a 1 pixel for every pixel within the
// block. Rotates the coordinate system by rerotation prior to rendering.
Pix* PDBLK::render_mask(const FCOORD& rerotation, TBOX* mask_box) {
TBOX rotated_box(box);
rotated_box.rotate(rerotation);
Pix* pix = pixCreate(rotated_box.width(), rotated_box.height(), 1);
if (hand_poly != nullptr) {
// We are going to rotate, so get a deep copy of the points and
// make a new POLY_BLOCK with it.
ICOORDELT_LIST polygon;
polygon.deep_copy(hand_poly->points(), ICOORDELT::deep_copy);
POLY_BLOCK image_block(&polygon, hand_poly->isA());
image_block.rotate(rerotation);
// Block outline is a polygon, so use a PB_LINE_IT to get the
// rasterized interior. (Runs of interior pixels on a line.)
auto *lines = new PB_LINE_IT(&image_block);
for (int y = box.bottom(); y < box.top(); ++y) {
const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments(
lines->get_line(y));
if (!segments->empty()) {
ICOORDELT_IT s_it(segments.get());
// Each element of segments is a start x and x size of the
// run of interior pixels.
for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) {
int start = s_it.data()->x();
int xext = s_it.data()->y();
// Set the run of pixels to 1.
pixRasterop(pix, start - rotated_box.left(),
rotated_box.height() - 1 - (y - rotated_box.bottom()),
xext, 1, PIX_SET, nullptr, 0, 0);
}
}
}
delete lines;
} else {
// Just fill the whole block as there is only a bounding box.
pixRasterop(pix, 0, 0, rotated_box.width(), rotated_box.height(),
PIX_SET, nullptr, 0, 0);
}
if (mask_box != nullptr) *mask_box = rotated_box;
return pix;
}
请发表评论