/*! * \brief nextOnPixelInRasterLow() * * \param[in] data pix data * \param[in] w, h width and height * \param[in] wpl words per line * \param[in] xstart, ystart starting point for search * \param[out] px, py coord value of next ON pixel * \return 1 if a pixel is found; 0 otherwise or on error */ l_int32 nextOnPixelInRasterLow(l_uint32 *data, l_int32 w, l_int32 h, l_int32 wpl, l_int32 xstart, l_int32 ystart, l_int32 *px, l_int32 *py) { l_int32 i, x, y, xend, startword; l_uint32 *line, *pword; /* Look at the first word */ line = data + ystart * wpl; pword = line + (xstart / 32); if (*pword) { xend = xstart - (xstart % 32) + 31; for (x = xstart; x <= xend && x < w; x++) { if (GET_DATA_BIT(line, x)) { *px = x; *py = ystart; return 1; } } } /* Continue with the rest of the line */ startword = (xstart / 32) + 1; x = 32 * startword; for (pword = line + startword; x < w; pword++, x += 32) { if (*pword) { for (i = 0; i < 32 && x < w; i++, x++) { if (GET_DATA_BIT(line, x)) { *px = x; *py = ystart; return 1; } } } } /* Continue with following lines */ for (y = ystart + 1; y < h; y++) { line = data + y * wpl; for (pword = line, x = 0; x < w; pword++, x += 32) { if (*pword) { for (i = 0; i < 32 && x < w; i++, x++) { if (GET_DATA_BIT(line, x)) { *px = x; *py = y; return 1; } } } } } return 0; }
/*! * dpixMeanSquareAccum() * * Input: pixs (1 bpp or 8 bpp grayscale) * Return: dpix (64 bit array), or null on error * * Notes: * (1) This is an extension to the standard pixMeanSquareAccum() * implementation provided by Leptonica, to handle 1bpp binary pix * transparently. * (1) Similar to pixBlockconvAccum(), this computes the * sum of the squares of the pixel values in such a way * that the value at (i,j) is the sum of all squares in * the rectangle from the origin to (i,j). * (2) The general recursion relation (v are squared pixel values) is * a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1) * For the first line, this reduces to the special case * a(i,j) = v(i,j) + a(i, j-1) * For the first column, the special case is * a(i,j) = v(i,j) + a(i-1, j) */ DPIX * dpixMeanSquareAccum(PIX *pixs) { l_int32 i, j, w, h, d, wpl, wpls, val; l_uint32 *datas, *lines; l_float64 *data, *line, *linep; DPIX *dpix; PROCNAME("dpixMeanSquareAccum"); if (!pixs) return (DPIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1 && d != 8) return (DPIX *)ERROR_PTR("pixs not 1 bpp or 8 bpp", procName, NULL); if ((dpix = dpixCreate(w, h)) == NULL) return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); data = dpixGetData(dpix); wpl = dpixGetWpl(dpix); lines = datas; line = data; for (j = 0; j < w; j++) { /* first line */ val = d == 1 ? GET_DATA_BIT(lines, j) : GET_DATA_BYTE(lines, j); if (j == 0) line[0] = val * val; else line[j] = line[j - 1] + val * val; } /* Do the other lines */ for (i = 1; i < h; i++) { lines = datas + i * wpls; line = data + i * wpl; /* current dest line */ linep = line - wpl;; /* prev dest line */ for (j = 0; j < w; j++) { val = d == 1 ? GET_DATA_BIT(lines, j) : GET_DATA_BYTE(lines, j); if (j == 0) line[0] = linep[0] + val * val; else line[j] = line[j - 1] + linep[j] - linep[j - 1] + val * val; } } return dpix; }
/*! * ptaGetMeanVerticals() * * Input: pixs (1 bpp, single c.c.) * x,y (location of UL corner of pixs with respect to page image * Return: pta (mean y-values in component for each x-value, * both translated by (x,y) */ PTA * pixGetMeanVerticals(PIX *pixs, l_int32 x, l_int32 y) { l_int32 w, h, i, j, wpl, sum, count; l_uint32 *line, *data; PTA *pta; PROCNAME("pixGetMeanVerticals"); if (!pixs || pixGetDepth(pixs) != 1) return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); pta = ptaCreate(w); data = pixGetData(pixs); wpl = pixGetWpl(pixs); for (j = 0; j < w; j++) { line = data; sum = count = 0; for (i = 0; i < h; i++) { if (GET_DATA_BIT(line, j) == 1) { sum += i; count += 1; } line += wpl; } if (count == 0) continue; ptaAddPt(pta, x + j, y + (sum / count)); } return pta; }
/** * creates a raw buffer from the specified location of the pix */ unsigned char *CubeUtils::GetImageData(Pix *pix, int left, int top, int wid, int hgt) { // skip invalid dimensions if (left < 0 || top < 0 || wid < 0 || hgt < 0 || (left + wid) > pix->w || (top + hgt) > pix->h || pix->d != 1) { return NULL; } // copy the char img to a temp buffer unsigned char *temp_buff = new unsigned char[wid * hgt]; if (temp_buff == NULL) { return NULL; } l_int32 w; l_int32 h; l_int32 d; l_int32 wpl; l_uint32 *line; l_uint32 *data; pixGetDimensions(pix, &w, &h, &d); wpl = pixGetWpl(pix); data = pixGetData(pix); line = data + (top * wpl); for (int y = 0, off = 0; y < hgt; y++) { for (int x = 0; x < wid; x++, off++) { temp_buff[off] = GET_DATA_BIT(line, x + left) ? 0 : 255; } line += wpl; } return temp_buff; }
/*! * pixFindVerticalRuns() * * Input: pix (1 bpp) * x (line to traverse) * ystart (returns array of start positions for fg runs) * yend (returns array of end positions for fg runs) * &n (<return> the number of runs found) * Return: 0 if OK; 1 on error * * Notes: * (1) This finds foreground vertical runs on a single scanline. * (2) To find background runs, use pixInvert() before applying * this function. * (3) The ystart and yend arrays are input. They should be * of size h/2 + 1 to insure that they can hold * the maximum number of runs in the raster line. */ l_int32 pixFindVerticalRuns(PIX *pix, l_int32 x, l_int32 *ystart, l_int32 *yend, l_int32 *pn) { l_int32 inrun; /* boolean */ l_int32 index, w, h, d, i, wpl, val; l_uint32 *data, *line; PROCNAME("pixFindVerticalRuns"); if (!pn) return ERROR_INT("&n not defined", procName, 1); *pn = 0; if (!pix) return ERROR_INT("pix not defined", procName, 1); pixGetDimensions(pix, &w, &h, &d); if (d != 1) return ERROR_INT("pix not 1 bpp", procName, 1); if (x < 0 || x >= w) return ERROR_INT("x not in [0 ... w - 1]", procName, 1); if (!ystart) return ERROR_INT("ystart not defined", procName, 1); if (!yend) return ERROR_INT("yend not defined", procName, 1); wpl = pixGetWpl(pix); data = pixGetData(pix); inrun = FALSE; index = 0; for (i = 0; i < h; i++) { line = data + i * wpl; val = GET_DATA_BIT(line, x); if (!inrun) { if (val) { ystart[index] = i; inrun = TRUE; } } else { if (!val) { yend[index++] = i - 1; inrun = FALSE; } } } /* Finish last run if necessary */ if (inrun) yend[index++] = h - 1; *pn = index; return 0; }
/*! * \brief pixFindMaxVerticalRunOnLine() * * \param[in] pix 1 bpp * \param[in] x column to traverse * \param[out] pystart [optional] start position * \param[out] psize the size of the run * \return 0 if OK; 1 on error * * <pre> * Notes: * (1) This finds the longest foreground vertical run on a scanline. * (2) To find background runs, use pixInvert() before applying * this function. * </pre> */ l_int32 pixFindMaxVerticalRunOnLine(PIX *pix, l_int32 x, l_int32 *pystart, l_int32 *psize) { l_int32 inrun; /* boolean */ l_int32 w, h, i, wpl, val, maxstart, maxsize, length, start; l_uint32 *data, *line; PROCNAME("pixFindMaxVerticalRunOnLine"); if (pystart) *pystart = 0; if (!psize) return ERROR_INT("&size not defined", procName, 1); *psize = 0; if (!pix || pixGetDepth(pix) != 1) return ERROR_INT("pix not defined or not 1 bpp", procName, 1); pixGetDimensions(pix, &w, &h, NULL); if (x < 0 || x >= w) return ERROR_INT("x not in [0 ... w - 1]", procName, 1); wpl = pixGetWpl(pix); data = pixGetData(pix); inrun = FALSE; start = 0; maxstart = 0; maxsize = 0; for (i = 0; i < h; i++) { line = data + i * wpl; val = GET_DATA_BIT(line, x); if (!inrun) { if (val) { start = i; inrun = TRUE; } } else if (!val) { /* run just ended */ length = i - start; if (length > maxsize) { maxsize = length; maxstart = start; } inrun = FALSE; } } if (inrun) { /* a run has continued to the end of the column */ length = i - start; if (length > maxsize) { maxsize = length; maxstart = start; } } if (pystart) *pystart = maxstart; *psize = maxsize; return 0; }
/*! * pixFindHorizontalRuns() * * Input: pix (1 bpp) * y (line to traverse) * xstart (returns array of start positions for fg runs) * xend (returns array of end positions for fg runs) * &n (<return> the number of runs found) * Return: 0 if OK; 1 on error * * Notes: * (1) This finds foreground horizontal runs on a single scanline. * (2) To find background runs, use pixInvert() before applying * this function. * (3) The xstart and xend arrays are input. They should be * of size w/2 + 1 to insure that they can hold * the maximum number of runs in the raster line. */ l_int32 pixFindHorizontalRuns(PIX *pix, l_int32 y, l_int32 *xstart, l_int32 *xend, l_int32 *pn) { l_int32 inrun; /* boolean */ l_int32 index, w, h, d, j, wpl, val; l_uint32 *line; PROCNAME("pixFindHorizontalRuns"); if (!pn) return ERROR_INT("&n not defined", procName, 1); *pn = 0; if (!pix) return ERROR_INT("pix not defined", procName, 1); pixGetDimensions(pix, &w, &h, &d); if (d != 1) return ERROR_INT("pix not 1 bpp", procName, 1); if (y < 0 || y >= h) return ERROR_INT("y not in [0 ... h - 1]", procName, 1); if (!xstart) return ERROR_INT("xstart not defined", procName, 1); if (!xend) return ERROR_INT("xend not defined", procName, 1); wpl = pixGetWpl(pix); line = pixGetData(pix) + y * wpl; inrun = FALSE; index = 0; for (j = 0; j < w; j++) { val = GET_DATA_BIT(line, j); if (!inrun) { if (val) { xstart[index] = j; inrun = TRUE; } } else { if (!val) { xend[index++] = j - 1; inrun = FALSE; } } } /* Finish last run if necessary */ if (inrun) xend[index++] = w - 1; *pn = index; return 0; }
/*! * \brief pixFindMaxHorizontalRunOnLine() * * \param[in] pix 1 bpp * \param[in] y line to traverse * \param[out] pxstart [optional] start position * \param[out] psize the size of the run * \return 0 if OK; 1 on error * * <pre> * Notes: * (1) This finds the longest foreground horizontal run on a scanline. * (2) To find background runs, use pixInvert() before applying * this function. * </pre> */ l_int32 pixFindMaxHorizontalRunOnLine(PIX *pix, l_int32 y, l_int32 *pxstart, l_int32 *psize) { l_int32 inrun; /* boolean */ l_int32 w, h, j, wpl, val, maxstart, maxsize, length, start; l_uint32 *line; PROCNAME("pixFindMaxHorizontalRunOnLine"); if (pxstart) *pxstart = 0; if (!psize) return ERROR_INT("&size not defined", procName, 1); *psize = 0; if (!pix || pixGetDepth(pix) != 1) return ERROR_INT("pix not defined or not 1 bpp", procName, 1); pixGetDimensions(pix, &w, &h, NULL); if (y < 0 || y >= h) return ERROR_INT("y not in [0 ... h - 1]", procName, 1); wpl = pixGetWpl(pix); line = pixGetData(pix) + y * wpl; inrun = FALSE; start = 0; maxstart = 0; maxsize = 0; for (j = 0; j < w; j++) { val = GET_DATA_BIT(line, j); if (!inrun) { if (val) { start = j; inrun = TRUE; } } else if (!val) { /* run just ended */ length = j - start; if (length > maxsize) { maxsize = length; maxstart = start; } inrun = FALSE; } } if (inrun) { /* a run has continued to the end of the row */ length = j - start; if (length > maxsize) { maxsize = length; maxstart = start; } } if (pxstart) *pxstart = maxstart; *psize = maxsize; return 0; }
// Scanning columns vertically on y=[y_start, y_end), returns the first x // colum starting at x_start, stepping by x_step to x_end in which there is // any black pixel. static int VScanForBlack(uinT32* data, int wpl, int x_start, int x_end, int y_start, int y_end, int x_step) { for (int x = x_start; x != x_end; x += x_step) { uinT32* line = data + y_start * wpl; for (int y = y_start; y < y_end; ++y, line += wpl) { if (GET_DATA_BIT(line, x)) return x; } } return x_end; }
// Scanning rows horizontally on x=[x_start, x_end), returns the first y row // starting at y_start, stepping by y_step to y_end in which there is // any black pixel. static int HScanForBlack(uinT32* data, int wpl, int x_start, int x_end, int y_start, int y_end, int y_step) { for (int y = y_start; y != y_end; y += y_step) { uinT32* line = data + wpl * y; for (int x = x_start; x < x_end; ++x) { if (GET_DATA_BIT(line, x)) return y; } } return y_end; }
/*! * \brief pixExpandBinaryReplicate() * * \param[in] pixs 1 bpp * \param[in] xfact integer scale factor for horiz. replicative expansion * \param[in] yfact integer scale factor for vertical replicative expansion * \return pixd scaled up, or NULL on error */ PIX * pixExpandBinaryReplicate(PIX *pixs, l_int32 xfact, l_int32 yfact) { l_int32 w, h, d, wd, hd, wpls, wpld, i, j, k, start; l_uint32 *datas, *datad, *lines, *lined; PIX *pixd; PROCNAME("pixExpandBinaryReplicate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); if (xfact <= 0 || yfact <= 0) return (PIX *)ERROR_PTR("invalid scale factor: <= 0", procName, NULL); if (xfact == yfact) { if (xfact == 1) return pixCopy(NULL, pixs); if (xfact == 2 || xfact == 4 || xfact == 8 || xfact == 16) return pixExpandBinaryPower2(pixs, xfact); } wpls = pixGetWpl(pixs); datas = pixGetData(pixs); wd = xfact * w; hd = yfact * h; if ((pixd = pixCreate(wd, hd, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixScaleResolution(pixd, (l_float32)xfact, (l_float32)yfact); wpld = pixGetWpl(pixd); datad = pixGetData(pixd); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + yfact * i * wpld; for (j = 0; j < w; j++) { /* replicate pixels on a single line */ if (GET_DATA_BIT(lines, j)) { start = xfact * j; for (k = 0; k < xfact; k++) SET_DATA_BIT(lined, start + k); } } for (k = 1; k < yfact; k++) /* replicate the line */ memcpy(lined + k * wpld, lined, 4 * wpld); } return pixd; }
/*! * pixExpandBinaryReplicate() * * Input: pixs (1 bpp) * factor (integer scale factor for replicative expansion) * Return: pixd (scaled up), or null on error */ PIX * pixExpandBinaryReplicate(PIX *pixs, l_int32 factor) { l_int32 w, h, d, wd, hd, wpls, wpld, i, j, k, start; l_uint32 *datas, *datad, *lines, *lined; PIX *pixd; PROCNAME("pixExpandBinaryReplicate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); if (factor <= 0) return (PIX *)ERROR_PTR("factor <= 0; invalid", procName, NULL); if (factor == 1) return pixCopy(NULL, pixs); if (factor == 2 || factor == 4 || factor == 8 || factor == 16) return pixExpandBinaryPower2(pixs, factor); wpls = pixGetWpl(pixs); datas = pixGetData(pixs); wd = factor * w; hd = factor * h; if ((pixd = pixCreate(wd, hd, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); wpld = pixGetWpl(pixd); datad = pixGetData(pixd); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + factor * i * wpld; for (j = 0; j < w; j++) { if (GET_DATA_BIT(lines, j)) { start = factor * j; for (k = 0; k < factor; k++) SET_DATA_BIT(lined, start + k); } } for (k = 1; k < factor; k++) memcpy(lined + k * wpld, lined, 4 * wpld); } return pixd; }
// Sends for each pixel either '1' or '0'. void ScrollView::TransferBinaryImage(PIX* image) { char* pixel_data = new char[image->w + 2]; for (int y = 0; y < image->h; y++) { l_uint32* data = pixGetData(image) + y * pixGetWpl(image); for (int x = 0; x < image->w; x++) { if (GET_DATA_BIT(data, x)) pixel_data[x] = '1'; else pixel_data[x] = '0'; } pixel_data[image->w] = '\n'; pixel_data[image->w + 1] = '\0'; SendRawMessage(pixel_data); } delete [] pixel_data; }
// Methods to construct histograms from images. void PixelHistogram::ConstructVerticalCountHist(Pix* pix) { Clear(); int width = pixGetWidth(pix); int height = pixGetHeight(pix); hist_ = new int[width]; length_ = width; int wpl = pixGetWpl(pix); l_uint32 *data = pixGetData(pix); for (int i = 0; i < width; ++i) hist_[i] = 0; for (int i = 0; i < height; ++i) { l_uint32 *line = data + i * wpl; for (int j = 0; j < width; ++j) if (GET_DATA_BIT(line, j)) ++(hist_[j]); } }
/*read*/ kal_uint16 serial_read_data(void) { kal_uint16 data=0; kal_int16 i; kal_uint32 savedMask; kal_uint32 retry=0; //savedMask = SaveAndSetIRQMask(); SET_CLK_LOW(); SET_CLK_HIGH(); while(GET_BUSY_BIT()) { SET_CLK_LOW(); SET_CLK_HIGH(); retry++; if(retry>1000000)/*give up the read. controller may be broken*/ return 0; }; for(i=11;i>=0;i--) { //SET_CLK_LOW(); //serial_delay(); SET_CLK_HIGH(); serial_delay(); if(GET_DATA_BIT()) data |= (1<<i); SET_CLK_LOW(); serial_delay(); } for(i=0;i<ZERO_FIELD_COUNT;i++) { SET_CLK_LOW(); serial_delay(); SET_CLK_HIGH(); SET_CLK_LOW(); } data&=0x3fff; //RestoreIRQMask(savedMask); return data; }
void block_edges(Pix *t_pix, // thresholded image PDBLK *block, // block in image C_OUTLINE_IT* outline_it) { ICOORD bleft; // bounding box ICOORD tright; BLOCK_LINE_IT line_it = block; // line iterator int width = pixGetWidth(t_pix); int height = pixGetHeight(t_pix); int wpl = pixGetWpl(t_pix); // lines in progress CRACKEDGE **ptrline = new CRACKEDGE*[width + 1]; CRACKEDGE *free_cracks = NULL; block->bounding_box(bleft, tright); // block box int block_width = tright.x() - bleft.x(); for (int x = block_width; x >= 0; x--) ptrline[x] = NULL; // no lines in progress uinT8* bwline = new uinT8[width]; uinT8 margin = WHITE_PIX; for (int y = tright.y() - 1; y >= bleft.y() - 1; y--) { if (y >= bleft.y() && y < tright.y()) { // Get the binary pixels from the image. l_uint32* line = pixGetData(t_pix) + wpl * (height - 1 - y); for (int x = 0; x < block_width; ++x) { bwline[x] = GET_DATA_BIT(line, x + bleft.x()) ^ 1; } make_margins(block, &line_it, bwline, margin, bleft.x(), tright.x(), y); } else { memset(bwline, margin, block_width * sizeof(bwline[0])); } line_edges(bleft.x(), y, block_width, margin, bwline, ptrline, &free_cracks, outline_it); } free_crackedges(free_cracks); // really free them delete[] ptrline; delete[] bwline; }
// Scans vertically on y=[y_start,y_end), starting with x=*x_start, // stepping x+=x_step, until x=x_end. *x_start is input/output. // If the number of black pixels in a column, pix_count fits this pattern: // 0 or more cols with pix_count < min_count then // <= mid_width cols with min_count <= pix_count <= max_count then // a column with pix_count > max_count then // true is returned, and *x_start = the first x with pix_count >= min_count. static bool VScanForEdge(uinT32* data, int wpl, int y_start, int y_end, int min_count, int mid_width, int max_count, int x_end, int x_step, int* x_start) { int mid_cols = 0; for (int x = *x_start; x != x_end; x += x_step) { int pix_count = 0; uinT32* line = data + y_start * wpl; for (int y = y_start; y < y_end; ++y, line += wpl) { if (GET_DATA_BIT(line, x)) ++pix_count; } if (mid_cols == 0 && pix_count < min_count) continue; // In the min phase. if (mid_cols == 0) *x_start = x; // Save the place where we came out of the min phase. if (pix_count > max_count) return true; // found the pattern. ++mid_cols; if (mid_cols > mid_width) break; // Middle too big. } return false; // Never found max_count. }
// Scans horizontally on x=[x_start,x_end), starting with y=*y_start, // stepping y+=y_step, until y=y_end. *ystart is input/output. // If the number of black pixels in a row, pix_count fits this pattern: // 0 or more rows with pix_count < min_count then // <= mid_width rows with min_count <= pix_count <= max_count then // a row with pix_count > max_count then // true is returned, and *y_start = the first y with pix_count >= min_count. static bool HScanForEdge(uinT32* data, int wpl, int x_start, int x_end, int min_count, int mid_width, int max_count, int y_end, int y_step, int* y_start) { int mid_rows = 0; for (int y = *y_start; y != y_end; y += y_step) { // Need pixCountPixelsInRow(pix, y, &pix_count, NULL) to count in a subset. int pix_count = 0; uinT32* line = data + wpl * y; for (int x = x_start; x < x_end; ++x) { if (GET_DATA_BIT(line, x)) ++pix_count; } if (mid_rows == 0 && pix_count < min_count) continue; // In the min phase. if (mid_rows == 0) *y_start = y; // Save the y_start where we came out of the min phase. if (pix_count > max_count) return true; // Found the pattern. ++mid_rows; if (mid_rows > mid_width) break; // Middle too big. } return false; // Never found max_count. }
int main(int argc, char **argv) { l_int32 i, j, k, w, h, w2, w4, w8, w16, w32, wpl; l_int32 count1, count2, count3; l_uint32 val32, val1, val2; l_uint32 *data1, *line1, *data2, *line2; void **lines1, **linet1, **linet2; PIX *pixs, *pix1, *pix2; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixs = pixRead("feyn-fract.tif"); pixGetDimensions(pixs, &w, &h, NULL); data1 = pixGetData(pixs); wpl = pixGetWpl(pixs); lines1 = pixGetLinePtrs(pixs, NULL); /* Get timing for the 3 different methods */ startTimer(); for (k = 0; k < 10; k++) { count1 = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { if (GET_DATA_BIT(lines1[i], j)) count1++; } } } fprintf(stderr, "Time with line ptrs = %5.3f sec, count1 = %d\n", stopTimer(), count1); startTimer(); for (k = 0; k < 10; k++) { count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; for (j = 0; j < w; j++) { if (l_getDataBit(line1, j)) count2++; } } } fprintf(stderr, "Time with l_get* = %5.3f sec, count2 = %d\n", stopTimer(), count2); startTimer(); for (k = 0; k < 10; k++) { count3 = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pixGetPixel(pixs, j, i, &val32); count3 += val32; } } } fprintf(stderr, "Time with pixGetPixel() = %5.3f sec, count3 = %d\n", stopTimer(), count3); pix1 = pixCreateTemplate(pixs); linet1 = pixGetLinePtrs(pix1, NULL); pix2 = pixCreateTemplate(pixs); data2 = pixGetData(pix2); linet2 = pixGetLinePtrs(pix2, NULL); /* ------------------------------------------------- */ /* Test different methods for 1 bpp */ /* ------------------------------------------------- */ count1 = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { val1 = GET_DATA_BIT(lines1[i], j); count1 += val1; if (val1) SET_DATA_BIT(linet1[i], j); } } count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w; j++) { val2 = l_getDataBit(line1, j); count2 += val2; if (val2) l_setDataBit(line2, j); } } CompareResults(pixs, pix1, pix2, count1, count2, "1 bpp", rp); /* ------------------------------------------------- */ /* Test different methods for 2 bpp */ /* ------------------------------------------------- */ count1 = 0; w2 = w / 2; for (i = 0; i < h; i++) { for (j = 0; j < w2; j++) { val1 = GET_DATA_DIBIT(lines1[i], j); count1 += val1; val1 += 0xbbbbbbbc; SET_DATA_DIBIT(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w2; j++) { val2 = l_getDataDibit(line1, j); count2 += val2; val2 += 0xbbbbbbbc; l_setDataDibit(line2, j, val2); } } CompareResults(pixs, pix1, pix2, count1, count2, "2 bpp", rp); /* ------------------------------------------------- */ /* Test different methods for 4 bpp */ /* ------------------------------------------------- */ count1 = 0; w4 = w / 4; for (i = 0; i < h; i++) { for (j = 0; j < w4; j++) { val1 = GET_DATA_QBIT(lines1[i], j); count1 += val1; val1 += 0xbbbbbbb0; SET_DATA_QBIT(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w4; j++) { val2 = l_getDataQbit(line1, j); count2 += val2; val2 += 0xbbbbbbb0; l_setDataQbit(line2, j, val2); } } CompareResults(pixs, pix1, pix2, count1, count2, "4 bpp", rp); /* ------------------------------------------------- */ /* Test different methods for 8 bpp */ /* ------------------------------------------------- */ count1 = 0; w8 = w / 8; for (i = 0; i < h; i++) { for (j = 0; j < w8; j++) { val1 = GET_DATA_BYTE(lines1[i], j); count1 += val1; val1 += 0xbbbbbb00; SET_DATA_BYTE(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w8; j++) { val2 = l_getDataByte(line1, j); count2 += val2; val2 += 0xbbbbbb00; l_setDataByte(line2, j, val2); } } CompareResults(pixs, pix1, pix2, count1, count2, "8 bpp", rp); /* ------------------------------------------------- */ /* Test different methods for 16 bpp */ /* ------------------------------------------------- */ count1 = 0; w16 = w / 16; for (i = 0; i < h; i++) { for (j = 0; j < w16; j++) { val1 = GET_DATA_TWO_BYTES(lines1[i], j); count1 += val1; val1 += 0xbbbb0000; SET_DATA_TWO_BYTES(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w16; j++) { val2 = l_getDataTwoBytes(line1, j); count2 += val2; val2 += 0xbbbb0000; l_setDataTwoBytes(line2, j, val2); } } CompareResults(pixs, pix1, pix2, count1, count2, "16 bpp", rp); /* ------------------------------------------------- */ /* Test different methods for 32 bpp */ /* ------------------------------------------------- */ count1 = 0; w32 = w / 32; for (i = 0; i < h; i++) { for (j = 0; j < w32; j++) { val1 = GET_DATA_FOUR_BYTES(lines1[i], j); count1 += val1 & 0xfff; SET_DATA_FOUR_BYTES(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line1 = data1 + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w32; j++) { val2 = l_getDataFourBytes(line1, j); count2 += val2 & 0xfff; l_setDataFourBytes(line2, j, val2); } } CompareResults(pixs, pix1, pix2, count1, count2, "32 bpp", rp); pixDestroy(&pixs); pixDestroy(&pix1); pixDestroy(&pix2); lept_free(lines1); lept_free(linet1); lept_free(linet2); return regTestCleanup(rp); }
main(int argc, char **argv) { l_int32 x, y, i, j, k, w, h, w2, w4, w8, w16, w32, wpl, nerrors; l_int32 count1, count2, count3, ret, val1, val2; l_uint32 val32; l_uint32 *data, *line, *line1, *line2, *data1, *data2; void **lines1, **linet1, **linet2; PIX *pixs, *pixt1, *pixt2; static char mainName[] = "lowaccess_reg"; pixs = pixRead("feyn.tif"); /* width divisible by 16 */ pixGetDimensions(pixs, &w, &h, NULL); data = pixGetData(pixs); wpl = pixGetWpl(pixs); lines1 = pixGetLinePtrs(pixs, NULL); /* Get timing for the 3 different methods */ startTimer(); for (k = 0; k < 10; k++) { count1 = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { if (GET_DATA_BIT(lines1[i], j)) count1++; } } } fprintf(stderr, "Time with line ptrs = %5.3f sec, count1 = %d\n", stopTimer(), count1); startTimer(); for (k = 0; k < 10; k++) { count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < w; j++) { if (l_getDataBit(line, j)) count2++; } } } fprintf(stderr, "Time with l_get* = %5.3f sec, count2 = %d\n", stopTimer(), count2); startTimer(); for (k = 0; k < 10; k++) { count3 = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pixGetPixel(pixs, j, i, &val32); count3 += val32; } } } fprintf(stderr, "Time with pixGetPixel() = %5.3f sec, count3 = %d\n", stopTimer(), count3); pixt1 = pixCreateTemplate(pixs); data1 = pixGetData(pixt1); linet1 = pixGetLinePtrs(pixt1, NULL); pixt2 = pixCreateTemplate(pixs); data2 = pixGetData(pixt2); linet2 = pixGetLinePtrs(pixt2, NULL); nerrors = 0; /* Test different methods for 1 bpp */ count1 = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { val1 = GET_DATA_BIT(lines1[i], j); count1 += val1; if (val1) SET_DATA_BIT(linet1[i], j); } } count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w; j++) { val2 = l_getDataBit(line, j); count2 += val2; if (val2) l_setDataBit(line2, j); } } ret = compareResults(pixs, pixt1, pixt2, count1, count2, "1 bpp"); nerrors += ret; /* Test different methods for 2 bpp */ count1 = 0; w2 = w / 2; for (i = 0; i < h; i++) { for (j = 0; j < w2; j++) { val1 = GET_DATA_DIBIT(lines1[i], j); count1 += val1; val1 += 0xbbbbbbbc; SET_DATA_DIBIT(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w2; j++) { val2 = l_getDataDibit(line, j); count2 += val2; val2 += 0xbbbbbbbc; l_setDataDibit(line2, j, val2); } } ret = compareResults(pixs, pixt1, pixt2, count1, count2, "2 bpp"); nerrors += ret; /* Test different methods for 4 bpp */ count1 = 0; w4 = w / 4; for (i = 0; i < h; i++) { for (j = 0; j < w4; j++) { val1 = GET_DATA_QBIT(lines1[i], j); count1 += val1; val1 += 0xbbbbbbb0; SET_DATA_QBIT(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w4; j++) { val2 = l_getDataQbit(line, j); count2 += val2; val2 += 0xbbbbbbb0; l_setDataQbit(line2, j, val2); } } ret = compareResults(pixs, pixt1, pixt2, count1, count2, "4 bpp"); nerrors += ret; /* Test different methods for 8 bpp */ count1 = 0; w8 = w / 8; for (i = 0; i < h; i++) { for (j = 0; j < w8; j++) { val1 = GET_DATA_BYTE(lines1[i], j); count1 += val1; val1 += 0xbbbbbb00; SET_DATA_BYTE(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w8; j++) { val2 = l_getDataByte(line, j); count2 += val2; val2 += 0xbbbbbb00; l_setDataByte(line2, j, val2); } } ret = compareResults(pixs, pixt1, pixt2, count1, count2, "8 bpp"); nerrors += ret; /* Test different methods for 16 bpp */ count1 = 0; w16 = w / 16; for (i = 0; i < h; i++) { for (j = 0; j < w16; j++) { val1 = GET_DATA_TWO_BYTES(lines1[i], j); count1 += val1; val1 += 0xbbbb0000; SET_DATA_TWO_BYTES(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w16; j++) { val2 = l_getDataTwoBytes(line, j); count2 += val2; val2 += 0xbbbb0000; l_setDataTwoBytes(line2, j, val2); } } ret = compareResults(pixs, pixt1, pixt2, count1, count2, "16 bpp"); nerrors += ret; /* Test different methods for 32 bpp */ count1 = 0; w32 = w / 32; for (i = 0; i < h; i++) { for (j = 0; j < w32; j++) { val1 = GET_DATA_FOUR_BYTES(lines1[i], j); count1 += val1 & 0xfff; SET_DATA_FOUR_BYTES(linet1[i], j, val1); } } count2 = 0; for (i = 0; i < h; i++) { line = data + i * wpl; line2 = data2 + i * wpl; for (j = 0; j < w32; j++) { val2 = l_getDataFourBytes(line, j); count2 += val2 & 0xfff; l_setDataFourBytes(line2, j, val2); } } ret = compareResults(pixs, pixt1, pixt2, count1, count2, "32 bpp"); nerrors += ret; if (!nerrors) fprintf(stderr, "**** No errors ****\n"); else fprintf(stderr, "**** %d errors found! ****\n", nerrors); pixDestroy(&pixs); pixDestroy(&pixt1); pixDestroy(&pixt2); lept_free(lines1); lept_free(linet1); lept_free(linet2); return 0; }
/*! * pixSetSelectCmap() * * Input: pixs (1, 2, 4 or 8 bpp, with colormap) * box (<optional> region to set color; can be NULL) * sindex (colormap index of pixels to be changed) * rval, gval, bval (new color to paint) * Return: 0 if OK, 1 on error * * Note: * (1) This is an in-place operation. * (2) It sets all pixels in region that have the color specified * by the colormap index 'sindex' to the new color. * (3) sindex must be in the existing colormap; otherwise an * error is returned. * (4) If the new color exists in the colormap, it is used; * otherwise, it is added to the colormap. If it cannot be * added because the colormap is full, an error is returned. * (5) If box is NULL, applies function to the entire image; otherwise, * clips the operation to the intersection of the box and pix. * (6) An DC of use would be to set to a specific color all * the light (background) pixels within a certain region of * a 3-level 2 bpp image, while leaving light pixels outside * this region unchanged. */ l_int32 pixSetSelectCmap(PIX *pixs, BOX *box, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval) { l_int32 i, j, w, h, d, n, x1, y1, x2, y2, bw, bh, val, wpls; l_int32 index; /* of new color to be set */ l_uint32 *lines, *datas; PIXCMAP *cmap; PROCNAME("pixSetSelectCmap"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if ((cmap = pixGetColormap(pixs)) == NULL) return ERROR_INT("no colormap", procName, 1); d = pixGetDepth(pixs); if (d != 1 && d != 2 && d != 4 && d != 8) return ERROR_INT("depth not in {1,2,4,8}", procName, 1); /* Add new color if necessary; get index of this color in cmap */ n = pixcmapGetCount(cmap); if (sindex >= n) return ERROR_INT("sindex too large; no cmap entry", procName, 1); if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ if (pixcmapAddColor(cmap, rval, gval, bval)) return ERROR_INT("error adding cmap entry", procName, 1); else index = n; /* we've added one color */ } /* Determine the region of substitution */ pixGetDimensions(pixs, &w, &h, NULL); if (!box) { x1 = y1 = 0; x2 = w; y2 = h; } else { boxGetGeometry(box, &x1, &y1, &bw, &bh); x2 = x1 + bw - 1; y2 = y1 + bh - 1; } /* Replace pixel value sindex by index in the region */ datas = pixGetData(pixs); wpls = pixGetWpl(pixs); for (i = y1; i <= y2; i++) { if (i < 0 || i >= h) /* clip */ continue; lines = datas + i * wpls; for (j = x1; j <= x2; j++) { if (j < 0 || j >= w) /* clip */ continue; switch (d) { case 1: val = GET_DATA_BIT(lines, j); if (val == sindex) { if (index == 0) CLEAR_DATA_BIT(lines, j); else SET_DATA_BIT(lines, j); } break; case 2: val = GET_DATA_DIBIT(lines, j); if (val == sindex) SET_DATA_DIBIT(lines, j, index); break; case 4: val = GET_DATA_QBIT(lines, j); if (val == sindex) SET_DATA_QBIT(lines, j, index); break; case 8: val = GET_DATA_BYTE(lines, j); if (val == sindex) SET_DATA_BYTE(lines, j, index); break; default: return ERROR_INT("depth not in {1,2,4,8}", procName, 1); } } } return 0; }
/*! * pixSetMaskedCmap() * * Input: pixs (2, 4 or 8 bpp, colormapped) * pixm (<optional> 1 bpp mask; no-op if NULL) * x, y (origin of pixm relative to pixs; can be negative) * rval, gval, bval (new color to set at each masked pixel) * Return: 0 if OK; 1 on error * * Notes: * (1) This is an in-place operation. * (2) It paints a single color through the mask (as a stencil). * (3) The mask origin is placed at (x,y) on pixs, and the * operation is clipped to the intersection of the mask and pixs. * (4) If pixm == NULL, a warning is given. * (5) Typically, pixm is a small binary mask located somewhere * on the larger pixs. * (6) If the color is in the colormap, it is used. Otherwise, * it is added if possible; an error is returned if the * colormap is already full. */ l_int32 pixSetMaskedCmap(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval) { l_int32 w, h, d, wpl, wm, hm, wplm; l_int32 i, j, index; l_uint32 *data, *datam, *line, *linem; PIXCMAP *cmap; PROCNAME("pixSetMaskedCmap"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if ((cmap = pixGetColormap(pixs)) == NULL) return ERROR_INT("no colormap in pixs", procName, 1); if (!pixm) { L_WARNING("no mask; nothing to do\n", procName); return 0; } d = pixGetDepth(pixs); if (d != 2 && d != 4 && d != 8) return ERROR_INT("depth not in {2,4,8}", procName, 1); if (pixGetDepth(pixm) != 1) return ERROR_INT("pixm not 1 bpp", procName, 1); /* Add new color if necessary; store in 'index' */ if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ if (pixcmapAddColor(cmap, rval, gval, bval)) return ERROR_INT("no room in cmap", procName, 1); index = pixcmapGetCount(cmap) - 1; } pixGetDimensions(pixs, &w, &h, NULL); wpl = pixGetWpl(pixs); data = pixGetData(pixs); pixGetDimensions(pixm, &wm, &hm, NULL); wplm = pixGetWpl(pixm); datam = pixGetData(pixm); for (i = 0; i < hm; i++) { if (i + y < 0 || i + y >= h) continue; line = data + (i + y) * wpl; linem = datam + i * wplm; for (j = 0; j < wm; j++) { if (j + x < 0 || j + x >= w) continue; if (GET_DATA_BIT(linem, j)) { /* paint color */ switch (d) { case 2: SET_DATA_DIBIT(line, j + x, index); break; case 4: SET_DATA_QBIT(line, j + x, index); break; case 8: SET_DATA_BYTE(line, j + x, index); break; default: return ERROR_INT("depth not in {2,4,8}", procName, 1); } } } } return 0; }
/*! * pixSetSelectMaskedCmap() * * Input: pixs (2, 4 or 8 bpp, with colormap) * pixm (<optional> 1 bpp mask; no-op if NULL) * x, y (UL corner of mask relative to pixs) * sindex (colormap index of pixels in pixs to be changed) * rval, gval, bval (new color to substitute) * Return: 0 if OK, 1 on error * * Note: * (1) This is an in-place operation. * (2) This paints through the fg of pixm and replaces all pixels * in pixs that have a particular value (sindex) with the new color. * (3) If pixm == NULL, a warning is given. * (4) sindex must be in the existing colormap; otherwise an * error is returned. * (5) If the new color exists in the colormap, it is used; * otherwise, it is added to the colormap. If the colormap * is full, an error is returned. */ l_int32 pixSetSelectMaskedCmap(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval) { l_int32 i, j, w, h, d, n, wm, hm, wpls, wplm, val; l_int32 index; /* of new color to be set */ l_uint32 *lines, *linem, *datas, *datam; PIXCMAP *cmap; PROCNAME("pixSetSelectMaskedCmap"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if ((cmap = pixGetColormap(pixs)) == NULL) return ERROR_INT("no colormap", procName, 1); if (!pixm) { L_WARNING("no mask; nothing to do\n", procName); return 0; } d = pixGetDepth(pixs); if (d != 2 && d != 4 && d != 8) return ERROR_INT("depth not in {2, 4, 8}", procName, 1); /* add new color if necessary; get index of this color in cmap */ n = pixcmapGetCount(cmap); if (sindex >= n) return ERROR_INT("sindex too large; no cmap entry", procName, 1); if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ if (pixcmapAddColor(cmap, rval, gval, bval)) return ERROR_INT("error adding cmap entry", procName, 1); else index = n; /* we've added one color */ } /* replace pixel value sindex by index when fg pixel in pixmc * overlays it */ w = pixGetWidth(pixs); h = pixGetHeight(pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); wm = pixGetWidth(pixm); hm = pixGetHeight(pixm); datam = pixGetData(pixm); wplm = pixGetWpl(pixm); for (i = 0; i < hm; i++) { if (i + y < 0 || i + y >= h) continue; lines = datas + (y + i) * wpls; linem = datam + i * wplm; for (j = 0; j < wm; j++) { if (j + x < 0 || j + x >= w) continue; if (GET_DATA_BIT(linem, j)) { switch (d) { case 1: val = GET_DATA_BIT(lines, x + j); if (val == sindex) { if (index == 0) CLEAR_DATA_BIT(lines, x + j); else SET_DATA_BIT(lines, x + j); } break; case 2: val = GET_DATA_DIBIT(lines, x + j); if (val == sindex) SET_DATA_DIBIT(lines, x + j, index); break; case 4: val = GET_DATA_QBIT(lines, x + j); if (val == sindex) SET_DATA_QBIT(lines, x + j, index); break; case 8: val = GET_DATA_BYTE(lines, x + j); if (val == sindex) SET_DATA_BYTE(lines, x + j, index); break; default: return ERROR_INT("depth not in {1,2,4,8}", procName, 1); } } } } return 0; }
/*! * pixColorGrayMaskedCmap() * * Input: pixs (8 bpp, with colormap) * pixm (1 bpp mask, through which to apply color) * type (L_PAINT_LIGHT, L_PAINT_DARK) * rval, gval, bval (target color) * Return: 0 if OK, 1 on error * * Notes: * (1) This is an in-place operation. * (2) If type == L_PAINT_LIGHT, it colorizes non-black pixels, * preserving antialiasing. * If type == L_PAINT_DARK, it colorizes non-white pixels, * preserving antialiasing. See pixColorGrayCmap() for details. * (3) This increases the colormap size by the number of * different gray (non-black or non-white) colors in the * input colormap. If there is not enough room in the colormap * for this expansion, it returns 1 (error). */ l_int32 pixColorGrayMaskedCmap(PIX *pixs, PIX *pixm, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval) { l_int32 i, j, w, h, wm, hm, wmin, hmin, wpl, wplm; l_int32 val, nval; l_int32 *map; l_uint32 *line, *data, *linem, *datam; NUMA *na; PIXCMAP *cmap; PROCNAME("pixColorGrayMaskedCmap"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixm || pixGetDepth(pixm) != 1) return ERROR_INT("pixm undefined or not 1 bpp", procName, 1); if ((cmap = pixGetColormap(pixs)) == NULL) return ERROR_INT("no colormap", procName, 1); if (pixGetDepth(pixs) != 8) return ERROR_INT("depth not 8 bpp", procName, 1); if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) return ERROR_INT("invalid type", procName, 1); if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na)) return ERROR_INT("no room; cmap full", procName, 1); map = numaGetIArray(na); numaDestroy(&na); if (!map) return ERROR_INT("map not made", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); pixGetDimensions(pixm, &wm, &hm, NULL); if (wm != w) L_WARNING("wm = %d differs from w = %d\n", procName, wm, w); if (hm != h) L_WARNING("hm = %d differs from h = %d\n", procName, hm, h); wmin = L_MIN(w, wm); hmin = L_MIN(h, hm); data = pixGetData(pixs); wpl = pixGetWpl(pixs); datam = pixGetData(pixm); wplm = pixGetWpl(pixm); /* Remap gray pixels in the region */ for (i = 0; i < hmin; i++) { line = data + i * wpl; linem = datam + i * wplm; for (j = 0; j < wmin; j++) { if (GET_DATA_BIT(linem, j) == 0) continue; val = GET_DATA_BYTE(line, j); nval = map[val]; if (nval != 256) SET_DATA_BYTE(line, j, nval); } } FREE(map); return 0; }
/*! * \brief pixSeedfill8BB() * * \param[in] pixs 1 bpp * \param[in] stack for holding fillsegs * \param[in] x,y location of seed pixel * \return box or NULL on error. * * <pre> * Notes: * (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm. * (2) This operates on the input 1 bpp pix to remove the fg seed * pixel, at (x,y), and all pixels that are 8-connected to it. * The seed pixel at (x,y) must initially be ON. * (3) Returns the bounding box of the erased 8-cc component. * (4) Reference: see Paul Heckbert's stack-based seed fill algorithm * in "Graphic Gems", ed. Andrew Glassner, Academic * Press, 1990. The algorithm description is given * on pp. 275-277; working C code is on pp. 721-722.) * The code here follows Heckbert's closely, except * the leak checks are changed for 8 connectivity. * See comments on pixSeedfill4BB() for more details. * </pre> */ BOX * pixSeedfill8BB(PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y) { l_int32 w, h, xstart, wpl, x1, x2, dy; l_int32 xmax, ymax; l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ l_uint32 *data, *line; BOX *box; PROCNAME("pixSeedfill8BB"); if (!pixs || pixGetDepth(pixs) != 1) return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (!stack) return (BOX *)ERROR_PTR("stack not defined", procName, NULL); if (!stack->auxstack) stack->auxstack = lstackCreate(0); pixGetDimensions(pixs, &w, &h, NULL); xmax = w - 1; ymax = h - 1; data = pixGetData(pixs); wpl = pixGetWpl(pixs); line = data + y * wpl; /* Check pix value of seed; must be ON */ if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) return NULL; /* Init stack to seed: * Must first init b.b. values to prevent valgrind from complaining; * then init b.b. boundaries correctly to seed. */ minx = miny = 100000; maxx = maxy = 0; pushFillsegBB(stack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); pushFillsegBB(stack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); minx = maxx = x; miny = maxy = y; while (lstackGetCount(stack) > 0) { /* Pop segment off stack and fill a neighboring scan line */ popFillseg(stack, &x1, &x2, &y, &dy); line = data + y * wpl; /* A segment of scanline y - dy for x1 <= x <= x2 was * previously filled. We now explore adjacent pixels * in scan line y. There are three regions: to the * left of x1, between x1 and x2, and to the right of x2. * These regions are handled differently. Leaks are * possible expansions beyond the previous segment and * going back in the -dy direction. These can happen * for x < x1 and for x > x2. Any "leak" segments * are plugged with a push in the -dy (opposite) direction. * And any segments found anywhere are always extended * in the +dy direction. */ for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) CLEAR_DATA_BIT(line,x); if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ goto skip; xstart = x + 1; if (xstart < x1) /* leak on left? */ pushFillsegBB(stack, xstart, x1 - 1, y, -dy, ymax, &minx, &maxx, &miny, &maxy); x = x1; do { for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) CLEAR_DATA_BIT(line, x); pushFillsegBB(stack, xstart, x - 1, y, dy, ymax, &minx, &maxx, &miny, &maxy); if (x > x2) /* leak on right? */ pushFillsegBB(stack, x2 + 1, x - 1, y, -dy, ymax, &minx, &maxx, &miny, &maxy); skip: for (x++; x <= x2 + 1 && x <= xmax && (GET_DATA_BIT(line, x) == 0); x++) ; xstart = x; } while (x <= x2 + 1 && x <= xmax); } if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) == NULL) return (BOX *)ERROR_PTR("box not made", procName, NULL); return box; }
/*! * accumulateLow() */ void accumulateLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 d, l_int32 wpls, l_int32 op) { l_int32 i, j; l_uint32 *lines, *lined; switch (d) { case 1: for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; if (op == L_ARITH_ADD) { for (j = 0; j < w; j++) lined[j] += GET_DATA_BIT(lines, j); } else { /* op == L_ARITH_SUBTRACT */ for (j = 0; j < w; j++) lined[j] -= GET_DATA_BIT(lines, j); } } break; case 8: for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; if (op == L_ARITH_ADD) { for (j = 0; j < w; j++) lined[j] += GET_DATA_BYTE(lines, j); } else { /* op == L_ARITH_SUBTRACT */ for (j = 0; j < w; j++) lined[j] -= GET_DATA_BYTE(lines, j); } } break; case 16: for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; if (op == L_ARITH_ADD) { for (j = 0; j < w; j++) lined[j] += GET_DATA_TWO_BYTES(lines, j); } else { /* op == L_ARITH_SUBTRACT */ for (j = 0; j < w; j++) lined[j] -= GET_DATA_TWO_BYTES(lines, j); } } break; case 32: for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; if (op == L_ARITH_ADD) { for (j = 0; j < w; j++) lined[j] += lines[j]; } else { /* op == L_ARITH_SUBTRACT */ for (j = 0; j < w; j++) lined[j] -= lines[j]; } } break; } return; }
/*! * \brief pixSeedfill8() * * \param[in] pixs 1 bpp * \param[in] stack for holding fillsegs * \param[in] x,y location of seed pixel * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm. * (2) This operates on the input 1 bpp pix to remove the fg seed * pixel, at (x,y), and all pixels that are 8-connected to it. * The seed pixel at (x,y) must initially be ON. * (3) Reference: see pixSeedFill8BB() * </pre> */ l_int32 pixSeedfill8(PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y) { l_int32 w, h, xstart, wpl, x1, x2, dy; l_int32 xmax, ymax; l_uint32 *data, *line; PROCNAME("pixSeedfill8"); if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); if (!stack) return ERROR_INT("stack not defined", procName, 1); if (!stack->auxstack) stack->auxstack = lstackCreate(0); pixGetDimensions(pixs, &w, &h, NULL); xmax = w - 1; ymax = h - 1; data = pixGetData(pixs); wpl = pixGetWpl(pixs); line = data + y * wpl; /* Check pix value of seed; must be ON */ if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) return 0; /* Init stack to seed */ pushFillseg(stack, x, x, y, 1, ymax); pushFillseg(stack, x, x, y + 1, -1, ymax); while (lstackGetCount(stack) > 0) { /* Pop segment off stack and fill a neighboring scan line */ popFillseg(stack, &x1, &x2, &y, &dy); line = data + y * wpl; /* A segment of scanline y - dy for x1 <= x <= x2 was * previously filled. We now explore adjacent pixels * in scan line y. There are three regions: to the * left of x1, between x1 and x2, and to the right of x2. * These regions are handled differently. Leaks are * possible expansions beyond the previous segment and * going back in the -dy direction. These can happen * for x < x1 and for x > x2. Any "leak" segments * are plugged with a push in the -dy (opposite) direction. * And any segments found anywhere are always extended * in the +dy direction. */ for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) CLEAR_DATA_BIT(line,x); if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ goto skip; xstart = x + 1; if (xstart < x1) /* leak on left? */ pushFillseg(stack, xstart, x1 - 1, y, -dy, ymax); x = x1; do { for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) CLEAR_DATA_BIT(line, x); pushFillseg(stack, xstart, x - 1, y, dy, ymax); if (x > x2) /* leak on right? */ pushFillseg(stack, x2 + 1, x - 1, y, -dy, ymax); skip: for (x++; x <= x2 + 1 && x <= xmax && (GET_DATA_BIT(line, x) == 0); x++) ; xstart = x; } while (x <= x2 + 1 && x <= xmax); } return 0; }
/*! * pixSearchBinaryMaze() * * Input: pixs (1 bpp, maze) * xi, yi (beginning point; use same initial point * that was used to generate the maze) * xf, yf (end point, or close to it) * &ppixd (<optional return> maze with path illustrated, or * if no path possible, the part of the maze * that was searched) * Return: pta (shortest path), or null if either no path * exists or on error * * Notes: * (1) Because of the overhead in calling pixGetPixel() and * pixSetPixel(), we have used raster line pointers and the * GET_DATA* and SET_DATA* macros for many of the pix accesses. * (2) Commentary: * The goal is to find the shortest path between beginning and * end points, without going through walls, and there are many * ways to solve this problem. * We use a queue to implement a breadth-first search. Two auxiliary * "image" data structures can be used: one to mark the visited * pixels and one to give the direction to the parent for each * visited pixels. The first structure is used to avoid putting * pixels on the queue more than once, and the second is used * for retracing back to the origin, like the breadcrumbs in * Hansel and Gretel. Each pixel taken off the queue is destroyed * after it is used to locate the allowed neighbors. In fact, * only one distance image is required, if you initialize it * to some value that signifies "not yet visited." (We use * a binary image for marking visited pixels because it is clearer.) * This method for a simple search of a binary maze is implemented in * searchBinaryMaze(). * An alternative method would store the (manhattan) distance * from the start point with each pixel on the queue. The children * of each pixel get a distance one larger than the parent. These * values can be stored in an auxiliary distance map image * that is constructed simultaneously with the search. Once the * end point is reached, the distance map is used to backtrack * along a minimum path. There may be several equal length * minimum paths, any one of which can be chosen this way. */ PTA * pixSearchBinaryMaze(PIX *pixs, l_int32 xi, l_int32 yi, l_int32 xf, l_int32 yf, PIX **ppixd) { l_int32 i, j, x, y, w, h, d, found; l_uint32 val, rpixel, gpixel, bpixel; void **lines1, **linem1, **linep8, **lined32; MAZEEL *el, *elp; PIX *pixd; /* the shortest path written on the maze image */ PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ PIX *pixp; /* for bookkeeping, to indicate direction to parent */ L_QUEUE *lq; PTA *pta; PROCNAME("pixSearchBinaryMaze"); if (ppixd) *ppixd = NULL; if (!pixs) return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); if (xi <= 0 || xi >= w) return (PTA *)ERROR_PTR("xi not valid", procName, NULL); if (yi <= 0 || yi >= h) return (PTA *)ERROR_PTR("yi not valid", procName, NULL); pixGetPixel(pixs, xi, yi, &val); if (val != 0) return (PTA *)ERROR_PTR("(xi,yi) not bg pixel", procName, NULL); pixd = NULL; pta = NULL; /* Find a bg pixel near input point (xf, yf) */ localSearchForBackground(pixs, &xf, &yf, 5); #if DEBUG_MAZE fprintf(stderr, "(xi, yi) = (%d, %d), (xf, yf) = (%d, %d)\n", xi, yi, xf, yf); #endif /* DEBUG_MAZE */ pixm = pixCreate(w, h, 1); /* initialized to OFF */ pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ lines1 = pixGetLinePtrs(pixs, NULL); linem1 = pixGetLinePtrs(pixm, NULL); linep8 = pixGetLinePtrs(pixp, NULL); lq = lqueueCreate(0); /* Prime the queue with the first pixel; it is OFF */ el = mazeelCreate(xi, yi, 0); /* don't need direction here */ pixSetPixel(pixm, xi, yi, 1); /* mark visited */ lqueueAdd(lq, el); /* Fill up the pix storing directions to parents, * stopping when we hit the point (xf, yf) */ found = FALSE; while (lqueueGetCount(lq) > 0) { elp = (MAZEEL *)lqueueRemove(lq); x = elp->x; y = elp->y; if (x == xf && y == yf) { found = TRUE; FREE(elp); break; } if (x > 0) { /* check to west */ val = GET_DATA_BIT(linem1[y], x - 1); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y], x - 1); /* mark visited */ val = GET_DATA_BIT(lines1[y], x - 1); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent E */ el = mazeelCreate(x - 1, y, 0); lqueueAdd(lq, el); } } } if (y > 0) { /* check north */ val = GET_DATA_BIT(linem1[y - 1], x); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y - 1], x); /* mark visited */ val = GET_DATA_BIT(lines1[y - 1], x); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent S */ el = mazeelCreate(x, y - 1, 0); lqueueAdd(lq, el); } } } if (x < w - 1) { /* check east */ val = GET_DATA_BIT(linem1[y], x + 1); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y], x + 1); /* mark visited */ val = GET_DATA_BIT(lines1[y], x + 1); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent W */ el = mazeelCreate(x + 1, y, 0); lqueueAdd(lq, el); } } } if (y < h - 1) { /* check south */ val = GET_DATA_BIT(linem1[y + 1], x); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y + 1], x); /* mark visited */ val = GET_DATA_BIT(lines1[y + 1], x); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent N */ el = mazeelCreate(x, y + 1, 0); lqueueAdd(lq, el); } } } FREE(elp); } lqueueDestroy(&lq, TRUE); pixDestroy(&pixm); FREE(linem1); if (ppixd) { pixd = pixUnpackBinary(pixs, 32, 1); *ppixd = pixd; } composeRGBPixel(255, 0, 0, &rpixel); /* start point */ composeRGBPixel(0, 255, 0, &gpixel); composeRGBPixel(0, 0, 255, &bpixel); /* end point */ if (!found) { L_INFO(" No path found", procName); if (pixd) { /* paint all visited locations */ lined32 = pixGetLinePtrs(pixd, NULL); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { val = GET_DATA_BYTE(linep8[i], j); if (val != 0 && pixd) SET_DATA_FOUR_BYTES(lined32[i], j, gpixel); } } FREE(lined32); } } else { /* write path onto pixd */ L_INFO(" Path found", procName); pta = ptaCreate(0); x = xf; y = yf; while (1) { ptaAddPt(pta, x, y); if (x == xi && y == yi) break; if (pixd) pixSetPixel(pixd, x, y, gpixel); pixGetPixel(pixp, x, y, &val); if (val == DIR_NORTH) y--; else if (val == DIR_SOUTH) y++; else if (val == DIR_EAST) x++; else if (val == DIR_WEST) x--; } } if (pixd) { pixSetPixel(pixd, xi, yi, rpixel); pixSetPixel(pixd, xf, yf, bpixel); } pixDestroy(&pixp); FREE(lines1); FREE(linep8); return pta; }
/*! * \brief pixToGif() * * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp * \param[in] gif opened gif stream * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This encodes the pix to the gif stream. The stream is not * closes by this function. * (2) It is static to make this function private. * </pre> */ static l_int32 pixToGif(PIX *pix, GifFileType *gif) { char *text; l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval; l_int32 gif_ncolor = 0; l_uint32 *data, *line; PIX *pixd; PIXCMAP *cmap; ColorMapObject *gif_cmap; GifByteType *gif_line; #if (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5 int giferr; #endif /* 5.1 and beyond */ PROCNAME("pixToGif"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (!gif) return ERROR_INT("gif not defined", procName, 1); d = pixGetDepth(pix); if (d == 32) { pixd = pixConvertRGBToColormap(pix, 1); } else if (d > 1) { pixd = pixConvertTo8(pix, TRUE); } else { /* d == 1; make sure there's a colormap */ pixd = pixClone(pix); if (!pixGetColormap(pixd)) { cmap = pixcmapCreate(1); pixcmapAddColor(cmap, 255, 255, 255); pixcmapAddColor(cmap, 0, 0, 0); pixSetColormap(pixd, cmap); } } if (!pixd) return ERROR_INT("failed to convert image to indexed", procName, 1); d = pixGetDepth(pixd); if ((cmap = pixGetColormap(pixd)) == NULL) { pixDestroy(&pixd); return ERROR_INT("cmap is missing", procName, 1); } /* 'Round' the number of gif colors up to a power of 2 */ ncolor = pixcmapGetCount(cmap); for (i = 0; i <= 8; i++) { if ((1 << i) >= ncolor) { gif_ncolor = (1 << i); break; } } if (gif_ncolor < 1) { pixDestroy(&pixd); return ERROR_INT("number of colors is invalid", procName, 1); } /* Save the cmap colors in a gif_cmap */ if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) { pixDestroy(&pixd); return ERROR_INT("failed to create GIF color map", procName, 1); } for (i = 0; i < gif_ncolor; i++) { rval = gval = bval = 0; if (ncolor > 0) { if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) { pixDestroy(&pixd); GifFreeMapObject(gif_cmap); return ERROR_INT("failed to get color from color map", procName, 1); } ncolor--; } gif_cmap->Colors[i].Red = rval; gif_cmap->Colors[i].Green = gval; gif_cmap->Colors[i].Blue = bval; } pixGetDimensions(pixd, &w, &h, NULL); if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap) != GIF_OK) { pixDestroy(&pixd); GifFreeMapObject(gif_cmap); return ERROR_INT("failed to write screen description", procName, 1); } GifFreeMapObject(gif_cmap); /* not needed after this point */ if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) { pixDestroy(&pixd); return ERROR_INT("failed to image screen description", procName, 1); } data = pixGetData(pixd); wpl = pixGetWpl(pixd); if (d != 1 && d != 2 && d != 4 && d != 8) { pixDestroy(&pixd); return ERROR_INT("image depth is not in {1, 2, 4, 8}", procName, 1); } if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w)) == NULL) { pixDestroy(&pixd); return ERROR_INT("mem alloc fail for data line", procName, 1); } for (i = 0; i < h; i++) { line = data + i * wpl; /* Gif's way of setting the raster line up for compression */ for (j = 0; j < w; j++) { switch(d) { case 8: gif_line[j] = GET_DATA_BYTE(line, j); break; case 4: gif_line[j] = GET_DATA_QBIT(line, j); break; case 2: gif_line[j] = GET_DATA_DIBIT(line, j); break; case 1: gif_line[j] = GET_DATA_BIT(line, j); break; } } /* Compress and save the line */ if (EGifPutLine(gif, gif_line, w) != GIF_OK) { LEPT_FREE(gif_line); pixDestroy(&pixd); return ERROR_INT("failed to write data line into GIF", procName, 1); } } /* Write a text comment. This must be placed after writing the * data (!!) Note that because libgif does not provide a function * for reading comments from file, you will need another way * to read comments. */ if ((text = pixGetText(pix)) != NULL) { if (EGifPutComment(gif, text) != GIF_OK) L_WARNING("gif comment not written\n", procName); } LEPT_FREE(gif_line); pixDestroy(&pixd); return 0; }
/*! * pixFindLargestRectangle() * * Input: pixs (1 bpp) * polarity (0 within background, 1 within foreground) * &box (<return> largest rectangle, either by area or * by perimeter) * debugflag (1 to output image with rectangle drawn on it) * Return: 0 if OK, 1 on error * * Notes: * (1) Why is this here? This is a simple and elegant solution to * a problem in computational geometry that at first appears * quite difficult: what is the largest rectangle that can * be placed in the image, covering only pixels of one polarity * (bg or fg)? The solution is O(n), where n is the number * of pixels in the image, and it requires nothing more than * using a simple recursion relation in a single sweep of the image. * (2) In a sweep from UL to LR with left-to-right being the fast * direction, calculate the largest white rectangle at (x, y), * using previously calculated values at pixels #1 and #2: * #1: (x, y - 1) * #2: (x - 1, y) * We also need the most recent "black" pixels that were seen * in the current row and column. * Consider the largest area. There are only two possibilities: * (a) Min(w(1), horizdist) * (h(1) + 1) * (b) Min(h(2), vertdist) * (w(2) + 1) * where * horizdist: the distance from the rightmost "black" pixel seen * in the current row across to the current pixel * vertdist: the distance from the lowest "black" pixel seen * in the current column down to the current pixel * and we choose the Max of (a) and (b). * (3) To convince yourself that these recursion relations are correct, * it helps to draw the maximum rectangles at #1 and #2. * Then for #1, you try to extend the rectangle down one line, * so that the height is h(1) + 1. Do you get the full * width of #1, w(1)? It depends on where the black pixels are * in the current row. You know the final width is bounded by w(1) * and w(2) + 1, but the actual value depends on the distribution * of black pixels in the current row that are at a distance * from the current pixel that is between these limits. * We call that value "horizdist", and the area is then given * by the expression (a) above. Using similar reasoning for #2, * where you attempt to extend the rectangle to the right * by 1 pixel, you arrive at (b). The largest rectangle is * then found by taking the Max. */ l_int32 pixFindLargestRectangle(PIX *pixs, l_int32 polarity, BOX **pbox, const char *debugfile) { l_int32 i, j, w, h, d, wpls, val; l_int32 wp, hp, w1, w2, h1, h2, wmin, hmin, area1, area2; l_int32 xmax, ymax; /* LR corner of the largest rectangle */ l_int32 maxarea, wmax, hmax, vertdist, horizdist, prevfg; l_int32 *lowestfg; l_uint32 *datas, *lines; l_uint32 **linew, **lineh; BOX *box; PIX *pixw, *pixh; /* keeps the width and height for the largest */ /* rectangles whose LR corner is located there. */ PROCNAME("pixFindLargestRectangle"); if (!pbox) return ERROR_INT("&box not defined", procName, 1); *pbox = NULL; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return ERROR_INT("pixs not 1 bpp", procName, 1); if (polarity != 0 && polarity != 1) return ERROR_INT("invalid polarity", procName, 1); /* Initialize lowest "fg" seen so far for each column */ lowestfg = (l_int32 *)CALLOC(w, sizeof(l_int32)); for (i = 0; i < w; i++) lowestfg[i] = -1; /* The combination (val ^ polarity) is the color for which we * are searching for the maximum rectangle. For polarity == 0, * we search in the bg (white). */ pixw = pixCreate(w, h, 32); /* stores width */ pixh = pixCreate(w, h, 32); /* stores height */ linew = (l_uint32 **)pixGetLinePtrs(pixw, NULL); lineh = (l_uint32 **)pixGetLinePtrs(pixh, NULL); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); maxarea = xmax = ymax = wmax = hmax = 0; for (i = 0; i < h; i++) { lines = datas + i * wpls; prevfg = -1; for (j = 0; j < w; j++) { val = GET_DATA_BIT(lines, j); if ((val ^ polarity) == 0) { /* bg (0) if polarity == 0, etc. */ if (i == 0 && j == 0) { wp = hp = 1; } else if (i == 0) { wp = linew[i][j - 1] + 1; hp = 1; } else if (j == 0) { wp = 1; hp = lineh[i - 1][j] + 1; } else { /* Expand #1 prev rectangle down */ w1 = linew[i - 1][j]; h1 = lineh[i - 1][j]; horizdist = j - prevfg; wmin = L_MIN(w1, horizdist); /* width of new rectangle */ area1 = wmin * (h1 + 1); /* Expand #2 prev rectangle to right */ w2 = linew[i][j - 1]; h2 = lineh[i][j - 1]; vertdist = i - lowestfg[j]; hmin = L_MIN(h2, vertdist); /* height of new rectangle */ area2 = hmin * (w2 + 1); if (area1 > area2) { wp = wmin; hp = h1 + 1; } else { wp = w2 + 1; hp = hmin; } } } else { /* fg (1) if polarity == 0; bg (0) if polarity == 1 */ prevfg = j; lowestfg[j] = i; wp = hp = 0; } linew[i][j] = wp; lineh[i][j] = hp; if (wp * hp > maxarea) { maxarea = wp * hp; xmax = j; ymax = i; wmax = wp; hmax = hp; } } } /* Translate from LR corner to Box coords (UL corner, w, h) */ box = boxCreate(xmax - wmax + 1, ymax - hmax + 1, wmax, hmax); *pbox = box; if (debugfile) { PIX *pixdb; pixdb = pixConvertTo8(pixs, TRUE); pixRenderHashBoxArb(pixdb, box, 6, 2, L_NEG_SLOPE_LINE, 1, 255, 0, 0); pixWrite(debugfile, pixdb, IFF_PNG); pixDestroy(&pixdb); } FREE(linew); FREE(lineh); FREE(lowestfg); pixDestroy(&pixw); pixDestroy(&pixh); return 0; }