Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * convolve.c 00018 * 00019 * Top level grayscale or color block convolution 00020 * PIX *pixBlockconv() 00021 * 00022 * Grayscale block convolution 00023 * PIX *pixBlockconvGray() 00024 * 00025 * Accumulator for 1, 8 and 32 bpp convolution 00026 * PIX *pixBlockconvAccum() 00027 * 00028 * Un-normalized grayscale block convolution 00029 * PIX *pixBlockconvGrayUnnormalized() 00030 * 00031 * Tiled grayscale or color block convolution 00032 * PIX *pixBlockconvTiled() 00033 * PIX *pixBlockconvGrayTile() 00034 * 00035 * Convolution for mean, mean square, variance and rms deviation 00036 * in specified window 00037 * l_int32 pixWindowedStats() 00038 * PIX *pixWindowedMean() 00039 * PIX *pixWindowedMeanSquare() 00040 * l_int32 pixWindowedVariance() 00041 * DPIX *pixMeanSquareAccum() 00042 * 00043 * Binary block sum and rank filter 00044 * PIX *pixBlockrank() 00045 * PIX *pixBlocksum() 00046 * 00047 * Census transform 00048 * PIX *pixCensusTransform() 00049 * 00050 * Generic convolution (with Pix) 00051 * PIX *pixConvolve() 00052 * PIX *pixConvolveSep() 00053 * PIX *pixConvolveRGB() 00054 * PIX *pixConvolveRGBSep() 00055 * 00056 * Generic convolution (with float arrays) 00057 * FPIX *fpixConvolve() 00058 * FPIX *fpixConvolveSep() 00059 * 00060 * Set parameter for convolution subsampling 00061 * void l_setConvolveSampling() 00062 */ 00063 00064 #include <math.h> 00065 #include "allheaders.h" 00066 00067 /* These globals determine the subsampling factors for 00068 * generic convolution of pix and fpix. Declare extern to use. 00069 * To change the values, use l_setConvolveSampling(). */ 00070 LEPT_DLL l_int32 ConvolveSamplingFactX = 1; 00071 LEPT_DLL l_int32 ConvolveSamplingFactY = 1; 00072 00073 /*----------------------------------------------------------------------* 00074 * Top-level grayscale or color block convolution * 00075 *----------------------------------------------------------------------*/ 00076 /*! 00077 * pixBlockconv() 00078 * 00079 * Input: pix (8 or 32 bpp; or 2, 4 or 8 bpp with colormap) 00080 * wc, hc (half width/height of convolution kernel) 00081 * Return: pixd, or null on error 00082 * 00083 * Notes: 00084 * (1) The full width and height of the convolution kernel 00085 * are (2 * wc + 1) and (2 * hc + 1) 00086 * (2) Returns a copy if both wc and hc are 0 00087 * (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, 00088 * where (w,h) are the dimensions of pixs. 00089 */ 00090 PIX * 00091 pixBlockconv(PIX *pix, 00092 l_int32 wc, 00093 l_int32 hc) 00094 { 00095 l_int32 w, h, d; 00096 PIX *pixs, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; 00097 00098 PROCNAME("pixBlockconv"); 00099 00100 if (!pix) 00101 return (PIX *)ERROR_PTR("pix not defined", procName, NULL); 00102 if (wc < 0) wc = 0; 00103 if (hc < 0) hc = 0; 00104 pixGetDimensions(pix, &w, &h, &d); 00105 if (w < 2 * wc + 1 || h < 2 * hc + 1) { 00106 wc = L_MIN(wc, (w - 1) / 2); 00107 hc = L_MIN(hc, (h - 1) / 2); 00108 L_WARNING("kernel too large; reducing!", procName); 00109 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 00110 } 00111 if (wc == 0 && hc == 0) /* no-op */ 00112 return pixCopy(NULL, pix); 00113 00114 /* Remove colormap if necessary */ 00115 if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { 00116 L_WARNING("pix has colormap; removing", procName); 00117 pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); 00118 d = pixGetDepth(pixs); 00119 } 00120 else 00121 pixs = pixClone(pix); 00122 00123 if (d != 8 && d != 32) { 00124 pixDestroy(&pixs); 00125 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); 00126 } 00127 00128 if (d == 8) 00129 pixd = pixBlockconvGray(pixs, NULL, wc, hc); 00130 else { /* d == 32 */ 00131 pixr = pixGetRGBComponent(pixs, COLOR_RED); 00132 pixrc = pixBlockconvGray(pixr, NULL, wc, hc); 00133 pixDestroy(&pixr); 00134 pixg = pixGetRGBComponent(pixs, COLOR_GREEN); 00135 pixgc = pixBlockconvGray(pixg, NULL, wc, hc); 00136 pixDestroy(&pixg); 00137 pixb = pixGetRGBComponent(pixs, COLOR_BLUE); 00138 pixbc = pixBlockconvGray(pixb, NULL, wc, hc); 00139 pixDestroy(&pixb); 00140 pixd = pixCreateRGBImage(pixrc, pixgc, pixbc); 00141 pixDestroy(&pixrc); 00142 pixDestroy(&pixgc); 00143 pixDestroy(&pixbc); 00144 } 00145 00146 pixDestroy(&pixs); 00147 return pixd; 00148 } 00149 00150 00151 /*----------------------------------------------------------------------* 00152 * Grayscale block convolution * 00153 *----------------------------------------------------------------------*/ 00154 /*! 00155 * pixBlockconvGray() 00156 * 00157 * Input: pix (8 bpp) 00158 * accum pix (32 bpp; can be null) 00159 * wc, hc (half width/height of convolution kernel) 00160 * Return: pix (8 bpp), or null on error 00161 * 00162 * Notes: 00163 * (1) If accum pix is null, make one and destroy it before 00164 * returning; otherwise, just use the input accum pix. 00165 * (2) The full width and height of the convolution kernel 00166 * are (2 * wc + 1) and (2 * hc + 1). 00167 * (3) Returns a copy if both wc and hc are 0. 00168 * (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, 00169 * where (w,h) are the dimensions of pixs. 00170 */ 00171 PIX * 00172 pixBlockconvGray(PIX *pixs, 00173 PIX *pixacc, 00174 l_int32 wc, 00175 l_int32 hc) 00176 { 00177 l_int32 w, h, d, wpl, wpla; 00178 l_uint32 *datad, *dataa; 00179 PIX *pixd, *pixt; 00180 00181 PROCNAME("pixBlockconvGray"); 00182 00183 if (!pixs) 00184 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00185 pixGetDimensions(pixs, &w, &h, &d); 00186 if (d != 8) 00187 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00188 if (wc < 0) wc = 0; 00189 if (hc < 0) hc = 0; 00190 if (w < 2 * wc + 1 || h < 2 * hc + 1) { 00191 wc = L_MIN(wc, (w - 1) / 2); 00192 hc = L_MIN(hc, (h - 1) / 2); 00193 L_WARNING("kernel too large; reducing!", procName); 00194 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 00195 } 00196 if (wc == 0 && hc == 0) /* no-op */ 00197 return pixCopy(NULL, pixs); 00198 00199 if (pixacc) { 00200 if (pixGetDepth(pixacc) == 32) 00201 pixt = pixClone(pixacc); 00202 else { 00203 L_WARNING("pixacc not 32 bpp; making new one", procName); 00204 if ((pixt = pixBlockconvAccum(pixs)) == NULL) 00205 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00206 } 00207 } 00208 else { 00209 if ((pixt = pixBlockconvAccum(pixs)) == NULL) 00210 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00211 } 00212 00213 if ((pixd = pixCreateTemplate(pixs)) == NULL) { 00214 pixDestroy(&pixt); 00215 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00216 } 00217 00218 wpl = pixGetWpl(pixs); 00219 wpla = pixGetWpl(pixt); 00220 datad = pixGetData(pixd); 00221 dataa = pixGetData(pixt); 00222 blockconvLow(datad, w, h, wpl, dataa, wpla, wc, hc); 00223 00224 pixDestroy(&pixt); 00225 return pixd; 00226 } 00227 00228 00229 /*----------------------------------------------------------------------* 00230 * Accumulator for 1, 8 and 32 bpp convolution * 00231 *----------------------------------------------------------------------*/ 00232 /*! 00233 * pixBlockconvAccum() 00234 * 00235 * Input: pixs (1, 8 or 32 bpp) 00236 * Return: accum pix (32 bpp), or null on error. 00237 * 00238 * Notes: 00239 * (1) The general recursion relation is 00240 * a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1) 00241 * For the first line, this reduces to the special case 00242 * a(i,j) = v(i,j) + a(i, j-1) 00243 * For the first column, the special case is 00244 * a(i,j) = v(i,j) + a(i-1, j) 00245 */ 00246 PIX * 00247 pixBlockconvAccum(PIX *pixs) 00248 { 00249 l_int32 w, h, d, wpls, wpld; 00250 l_uint32 *datas, *datad; 00251 PIX *pixd; 00252 00253 PROCNAME("pixBlockconvAccum"); 00254 00255 if (!pixs) 00256 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00257 00258 pixGetDimensions(pixs, &w, &h, &d); 00259 if (d != 1 && d != 8 && d != 32) 00260 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); 00261 if ((pixd = pixCreate(w, h, 32)) == NULL) 00262 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00263 00264 datas = pixGetData(pixs); 00265 datad = pixGetData(pixd); 00266 wpls = pixGetWpl(pixs); 00267 wpld = pixGetWpl(pixd); 00268 blockconvAccumLow(datad, w, h, wpld, datas, d, wpls); 00269 00270 return pixd; 00271 } 00272 00273 00274 /*----------------------------------------------------------------------* 00275 * Un-normalized grayscale block convolution * 00276 *----------------------------------------------------------------------*/ 00277 /*! 00278 * pixBlockconvGrayUnnormalized() 00279 * 00280 * Input: pixs (8 bpp) 00281 * wc, hc (half width/height of convolution kernel) 00282 * Return: pix (32 bpp; containing the convolution without normalizing 00283 * for the window size), or null on error 00284 * 00285 * Notes: 00286 * (1) The full width and height of the convolution kernel 00287 * are (2 * wc + 1) and (2 * hc + 1). 00288 * (2) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, 00289 * where (w,h) are the dimensions of pixs. 00290 * (3) Returns a copy if both wc and hc are 0. 00291 * (3) Adds mirrored border to avoid treating the boundary pixels 00292 * specially. Note that we add wc + 1 pixels to the left 00293 * and wc to the right. The added width is 2 * wc + 1 pixels, 00294 * and the particular choice simplifies the indexing in the loop. 00295 * Likewise, add hc + 1 pixels to the top and hc to the bottom. 00296 * (4) To get the normalized result, divide by the area of the 00297 * convolution kernel: (2 * wc + 1) * (2 * hc + 1) 00298 * Specifically, do this: 00299 * pixc = pixBlockconvGrayUnnormalized(pixs, wc, hc); 00300 * fract = 1. / ((2 * wc + 1) * (2 * hc + 1)); 00301 * pixMultConstantGray(pixc, fract); 00302 * pixd = pixGetRGBComponent(pixc, L_ALPHA_CHANNEL); 00303 * (5) Unlike pixBlockconvGray(), this always computes the accumulation 00304 * pix because its size is tied to wc and hc. 00305 * (6) Compare this implementation with pixBlockconvGray(), where 00306 * most of the code in blockconvLow() is special casing for 00307 * efficiently handling the boundary. Here, the use of 00308 * mirrored borders and destination indexing makes the 00309 * implementation very simple. 00310 */ 00311 PIX * 00312 pixBlockconvGrayUnnormalized(PIX *pixs, 00313 l_int32 wc, 00314 l_int32 hc) 00315 { 00316 l_int32 i, j, w, h, d, wpla, wpld, jmax; 00317 l_uint32 *linemina, *linemaxa, *lined, *dataa, *datad; 00318 PIX *pixsb, *pixacc, *pixd; 00319 00320 PROCNAME("pixBlockconvGrayUnnormalized"); 00321 00322 if (!pixs) 00323 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00324 pixGetDimensions(pixs, &w, &h, &d); 00325 if (d != 8) 00326 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00327 if (wc < 0) wc = 0; 00328 if (hc < 0) hc = 0; 00329 if (w < 2 * wc + 1 || h < 2 * hc + 1) { 00330 wc = L_MIN(wc, (w - 1) / 2); 00331 hc = L_MIN(hc, (h - 1) / 2); 00332 L_WARNING("kernel too large; reducing!", procName); 00333 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 00334 } 00335 if (wc == 0 && hc == 0) /* no-op */ 00336 return pixCopy(NULL, pixs); 00337 00338 if ((pixsb = pixAddMirroredBorder(pixs, wc + 1, wc, hc + 1, hc)) == NULL) 00339 return (PIX *)ERROR_PTR("pixsb not made", procName, NULL); 00340 pixacc = pixBlockconvAccum(pixsb); 00341 pixDestroy(&pixsb); 00342 if (!pixacc) 00343 return (PIX *)ERROR_PTR("pixacc not made", procName, NULL); 00344 if ((pixd = pixCreate(w, h, 32)) == NULL) { 00345 pixDestroy(&pixacc); 00346 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00347 } 00348 00349 wpla = pixGetWpl(pixacc); 00350 wpld = pixGetWpl(pixd); 00351 datad = pixGetData(pixd); 00352 dataa = pixGetData(pixacc); 00353 for (i = 0; i < h; i++) { 00354 lined = datad + i * wpld; 00355 linemina = dataa + i * wpla; 00356 linemaxa = dataa + (i + 2 * hc + 1) * wpla; 00357 for (j = 0; j < w; j++) { 00358 jmax = j + 2 * wc + 1; 00359 lined[j] = linemaxa[jmax] - linemaxa[j] - 00360 linemina[jmax] + linemina[j]; 00361 } 00362 } 00363 00364 pixDestroy(&pixacc); 00365 return pixd; 00366 } 00367 00368 00369 /*----------------------------------------------------------------------* 00370 * Tiled grayscale or color block convolution * 00371 *----------------------------------------------------------------------*/ 00372 /*! 00373 * pixBlockconvTiled() 00374 * 00375 * Input: pix (8 or 32 bpp; or 2, 4 or 8 bpp with colormap) 00376 * wc, hc (half width/height of convolution kernel) 00377 * nx, ny (subdivision into tiles) 00378 * Return: pixd, or null on error 00379 * 00380 * Notes: 00381 * (1) The full width and height of the convolution kernel 00382 * are (2 * wc + 1) and (2 * hc + 1) 00383 * (2) Returns a copy if both wc and hc are 0 00384 * (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, 00385 * where (w,h) are the dimensions of pixs. 00386 * (4) For nx == ny == 1, this defaults to pixBlockconv(), which 00387 * is typically about twice as fast, and gives nearly 00388 * identical results as pixBlockconvGrayTile(). 00389 * (5) If the tiles are too small, nx and/or ny are reduced 00390 * a minimum amount so that the tiles are expanded to the 00391 * smallest workable size in the problematic direction(s). 00392 * (6) Why a tiled version? Three reasons: 00393 * (a) Because the accumulator is a uint32, overflow can occur 00394 * for an image with more than 16M pixels. 00395 * (b) The accumulator array for 16M pixels is 64 MB; using 00396 * tiles reduces the size of this array. 00397 * (c) Each tile can be processed independently, in parallel, 00398 * on a multicore processor. 00399 */ 00400 PIX * 00401 pixBlockconvTiled(PIX *pix, 00402 l_int32 wc, 00403 l_int32 hc, 00404 l_int32 nx, 00405 l_int32 ny) 00406 { 00407 l_int32 i, j, w, h, d, xrat, yrat; 00408 PIX *pixs, *pixd, *pixc, *pixt; 00409 PIX *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; 00410 PIXTILING *pt; 00411 00412 PROCNAME("pixBlockconvTiled"); 00413 00414 if (!pix) 00415 return (PIX *)ERROR_PTR("pix not defined", procName, NULL); 00416 if (wc < 0) wc = 0; 00417 if (hc < 0) hc = 0; 00418 pixGetDimensions(pix, &w, &h, &d); 00419 if (w < 2 * wc + 3 || h < 2 * hc + 3) { 00420 wc = L_MAX(0, L_MIN(wc, (w - 3) / 2)); 00421 hc = L_MAX(0, L_MIN(hc, (h - 3) / 2)); 00422 L_WARNING("kernel too large; reducing!", procName); 00423 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 00424 } 00425 if (wc == 0 && hc == 0) /* no-op */ 00426 return pixCopy(NULL, pix); 00427 if (nx <= 1 && ny <= 1) 00428 return pixBlockconv(pix, wc, hc); 00429 00430 /* Test to see if the tiles are too small. The required 00431 * condition is that the tile dimensions must be at least 00432 * (wc + 2) x (hc + 2). */ 00433 xrat = w / nx; 00434 yrat = h / ny; 00435 if (xrat < wc + 2) { 00436 nx = w / (wc + 2); 00437 L_WARNING_INT("tile width too small; nx reduced to %d", procName, nx); 00438 } 00439 if (yrat < hc + 2) { 00440 ny = h / (hc + 2); 00441 L_WARNING_INT("tile height too small; ny reduced to %d", procName, ny); 00442 } 00443 00444 /* Remove colormap if necessary */ 00445 if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { 00446 L_WARNING("pix has colormap; removing", procName); 00447 pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); 00448 d = pixGetDepth(pixs); 00449 } 00450 else 00451 pixs = pixClone(pix); 00452 00453 if (d != 8 && d != 32) { 00454 pixDestroy(&pixs); 00455 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); 00456 } 00457 00458 /* Note that the overlaps in the width and height that 00459 * are added to the tile are (wc + 2) and (hc + 2). 00460 * These overlaps are removed by pixTilingPaintTile(). 00461 * They are larger than the extent of the filter because 00462 * although the filter is symmetric with respect to its origin, 00463 * the implementation is asymmetric -- see the implementation in 00464 * pixBlockconvGrayTile(). */ 00465 if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) { 00466 pixDestroy(&pixs); 00467 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00468 } 00469 pt = pixTilingCreate(pixs, nx, ny, 0, 0, wc + 2, hc + 2); 00470 for (i = 0; i < ny; i++) { 00471 for (j = 0; j < nx; j++) { 00472 pixt = pixTilingGetTile(pt, i, j); 00473 00474 /* Convolve over the tile */ 00475 if (d == 8) 00476 pixc = pixBlockconvGrayTile(pixt, NULL, wc, hc); 00477 else { /* d == 32 */ 00478 pixr = pixGetRGBComponent(pixt, COLOR_RED); 00479 pixrc = pixBlockconvGrayTile(pixr, NULL, wc, hc); 00480 pixDestroy(&pixr); 00481 pixg = pixGetRGBComponent(pixt, COLOR_GREEN); 00482 pixgc = pixBlockconvGrayTile(pixg, NULL, wc, hc); 00483 pixDestroy(&pixg); 00484 pixb = pixGetRGBComponent(pixt, COLOR_BLUE); 00485 pixbc = pixBlockconvGrayTile(pixb, NULL, wc, hc); 00486 pixDestroy(&pixb); 00487 pixc = pixCreateRGBImage(pixrc, pixgc, pixbc); 00488 pixDestroy(&pixrc); 00489 pixDestroy(&pixgc); 00490 pixDestroy(&pixbc); 00491 } 00492 00493 pixTilingPaintTile(pixd, i, j, pixc, pt); 00494 pixDestroy(&pixt); 00495 pixDestroy(&pixc); 00496 } 00497 } 00498 00499 pixDestroy(&pixs); 00500 pixTilingDestroy(&pt); 00501 return pixd; 00502 } 00503 00504 00505 /*! 00506 * pixBlockconvGrayTile() 00507 * 00508 * Input: pixs (8 bpp gray) 00509 * pixacc (32 bpp accum pix) 00510 * wc, hc (half width/height of convolution kernel) 00511 * Return: pixd, or null on error 00512 * 00513 * Notes: 00514 * (1) The full width and height of the convolution kernel 00515 * are (2 * wc + 1) and (2 * hc + 1) 00516 * (2) Assumes that the input pixs is padded with (wc + 1) pixels on 00517 * left and right, and with (hc + 1) pixels on top and bottom. 00518 * The returned pix has these stripped off; they are only used 00519 * for computation. 00520 * (3) Returns a copy if both wc and hc are 0 00521 * (4) Require that w > 2 * wc + 1 and h > 2 * hc + 1, 00522 * where (w,h) are the dimensions of pixs. 00523 */ 00524 PIX * 00525 pixBlockconvGrayTile(PIX *pixs, 00526 PIX *pixacc, 00527 l_int32 wc, 00528 l_int32 hc) 00529 { 00530 l_int32 w, h, d, wd, hd, i, j, imin, imax, jmin, jmax, wplt, wpld; 00531 l_float32 norm; 00532 l_uint32 val; 00533 l_uint32 *datat, *datad, *lined, *linemint, *linemaxt; 00534 PIX *pixt, *pixd; 00535 00536 PROCNAME("pixBlockconvGrayTile"); 00537 00538 if (!pixs) 00539 return (PIX *)ERROR_PTR("pix not defined", procName, NULL); 00540 pixGetDimensions(pixs, &w, &h, &d); 00541 if (d != 8) 00542 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00543 if (wc < 0) wc = 0; 00544 if (hc < 0) hc = 0; 00545 if (w < 2 * wc + 3 || h < 2 * hc + 3) { 00546 wc = L_MAX(0, L_MIN(wc, (w - 3) / 2)); 00547 hc = L_MAX(0, L_MIN(hc, (h - 3) / 2)); 00548 L_WARNING("kernel too large; reducing!", procName); 00549 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 00550 } 00551 if (wc == 0 && hc == 0) 00552 return pixCopy(NULL, pixs); 00553 wd = w - 2 * wc; 00554 hd = h - 2 * hc; 00555 00556 if (pixacc) { 00557 if (pixGetDepth(pixacc) == 32) 00558 pixt = pixClone(pixacc); 00559 else { 00560 L_WARNING("pixacc not 32 bpp; making new one", procName); 00561 if ((pixt = pixBlockconvAccum(pixs)) == NULL) 00562 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00563 } 00564 } 00565 else { 00566 if ((pixt = pixBlockconvAccum(pixs)) == NULL) 00567 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00568 } 00569 00570 if ((pixd = pixCreateTemplate(pixs)) == NULL) { 00571 pixDestroy(&pixt); 00572 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00573 } 00574 datat = pixGetData(pixt); 00575 wplt = pixGetWpl(pixt); 00576 datad = pixGetData(pixd); 00577 wpld = pixGetWpl(pixd); 00578 norm = 1. / (l_float32)((2 * wc + 1) * (2 * hc + 1)); 00579 00580 /* Do the convolution over the subregion of size (wd - 2, hd - 2), 00581 * which exactly corresponds to the size of the subregion that 00582 * will be extracted by pixTilingPaintTile(). Note that the 00583 * region in which points are computed is not symmetric about 00584 * the center of the images; instead the computation in 00585 * the accumulator image is shifted up and to the left by 1, 00586 * relative to the center, because the 4 accumulator sampling 00587 * points are taken at the LL corner of the filter and at 3 other 00588 * points that are shifted -wc and -hc to the left and above. */ 00589 for (i = hc; i < hc + hd - 2; i++) { 00590 imin = L_MAX(i - hc - 1, 0); 00591 imax = L_MIN(i + hc, h - 1); 00592 lined = datad + i * wpld; 00593 linemint = datat + imin * wplt; 00594 linemaxt = datat + imax * wplt; 00595 for (j = wc; j < wc + wd - 2; j++) { 00596 jmin = L_MAX(j - wc - 1, 0); 00597 jmax = L_MIN(j + wc, w - 1); 00598 val = linemaxt[jmax] - linemaxt[jmin] 00599 + linemint[jmin] - linemint[jmax]; 00600 val = (l_uint8)(norm * val + 0.5); 00601 SET_DATA_BYTE(lined, j, val); 00602 } 00603 } 00604 00605 pixDestroy(&pixt); 00606 return pixd; 00607 } 00608 00609 00610 /*----------------------------------------------------------------------* 00611 * Convolution for mean, mean square, variance and rms deviation * 00612 *----------------------------------------------------------------------*/ 00613 /*! 00614 * pixWindowedStats() 00615 * 00616 * Input: pixs (8 bpp grayscale) 00617 * wc, hc (half width/height of convolution kernel) 00618 * hasborder (use 1 if it already has (wc + 1) border pixels 00619 * on left and right, and (hc + 1) on top and bottom; 00620 * use 0 to add kernel-dependent border) 00621 * &pixm (<optional return> 8 bpp mean value in window) 00622 * &pixms (<optional return> 32 bpp mean square value in window) 00623 * &fpixv (<optional return> float variance in window) 00624 * &fpixrv (<optional return> float rms deviation from the mean) 00625 * Return: 0 if OK, 1 on error 00626 * 00627 * Notes: 00628 * (1) This is a high-level convenience function for calculating 00629 * any or all of these derived images. 00630 * (2) If @hasborder = 0, a border is added and the result is 00631 * computed over all pixels in pixs. Otherwise, no border is 00632 * added and the border pixels are removed from the output images. 00633 * (3) These statistical measures over the pixels in the 00634 * rectangular window are: 00635 * - average value: <p> (pixm) 00636 * - average squared value: <p*p> (pixms) 00637 * - variance: <(p - <p>)*(p - <p>)> = <p*p> - <p>*<p> (pixv) 00638 * - square-root of variance: (pixrv) 00639 * where the brackets < .. > indicate that the average value is 00640 * to be taken over the window. 00641 * (4) Note that the variance is just the mean square difference from 00642 * the mean value; and the square root of the variance is the 00643 * root mean square difference from the mean, sometimes also 00644 * called the 'standard deviation'. 00645 * (5) The added border, along with the use of an accumulator array, 00646 * allows computation without special treatment of pixels near 00647 * the image boundary, and runs in a time that is independent 00648 * of the size of the convolution kernel. 00649 */ 00650 l_int32 00651 pixWindowedStats(PIX *pixs, 00652 l_int32 wc, 00653 l_int32 hc, 00654 l_int32 hasborder, 00655 PIX **ppixm, 00656 PIX **ppixms, 00657 FPIX **pfpixv, 00658 FPIX **pfpixrv) 00659 { 00660 PIX *pixb, *pixm, *pixms; 00661 00662 PROCNAME("pixWindowedStats"); 00663 00664 if (!pixs || pixGetDepth(pixs) != 8) 00665 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); 00666 if (wc < 2 || hc < 2) 00667 return ERROR_INT("wc and hc not >= 2", procName, 1); 00668 if (!ppixm && !ppixms && !pfpixv && !pfpixrv) 00669 return ERROR_INT("no output requested", procName, 1); 00670 if (ppixm) *ppixm = NULL; 00671 if (ppixms) *ppixms = NULL; 00672 if (pfpixv) *pfpixv = NULL; 00673 if (pfpixrv) *pfpixrv = NULL; 00674 00675 /* Add border if requested */ 00676 if (!hasborder) 00677 pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); 00678 else 00679 pixb = pixClone(pixs); 00680 00681 if (!pfpixv && !pfpixrv) { 00682 if (ppixm) *ppixm = pixWindowedMean(pixb, wc, hc, 1, 1); 00683 if (ppixms) *ppixms = pixWindowedMeanSquare(pixb, wc, hc, 1); 00684 pixDestroy(&pixb); 00685 return 0; 00686 } 00687 00688 pixm = pixWindowedMean(pixb, wc, hc, 1, 1); 00689 pixms = pixWindowedMeanSquare(pixb, wc, hc, 1); 00690 pixWindowedVariance(pixm, pixms, pfpixv, pfpixrv); 00691 if (ppixm) 00692 *ppixm = pixm; 00693 else 00694 pixDestroy(&pixm); 00695 if (ppixms) 00696 *ppixms = pixms; 00697 else 00698 pixDestroy(&pixms); 00699 pixDestroy(&pixb); 00700 return 0; 00701 } 00702 00703 00704 /*! 00705 * pixWindowedMean() 00706 * 00707 * Input: pixs (8 or 32 bpp grayscale) 00708 * wc, hc (half width/height of convolution kernel) 00709 * hasborder (use 1 if it already has (wc + 1) border pixels 00710 * on left and right, and (hc + 1) on top and bottom; 00711 * use 0 to add kernel-dependent border) 00712 * normflag (1 for normalization to get average in window; 00713 * 0 for the sum in the window (un-normalized)) 00714 * Return: pixd (8 or 32 bpp, average over kernel window) 00715 * 00716 * Notes: 00717 * (1) The input and output depths are the same. 00718 * (2) A set of border pixels of width (wc + 1) on left and right, 00719 * and of height (hc + 1) on top and bottom, must be on the 00720 * pix before the accumulator is found. The output pixd 00721 * (after convolution) has this border removed. 00722 * If @hasborder = 0, the required border is added. 00723 * (3) Typically, @normflag == 1. However, if you want the sum 00724 * within the window, rather than a normalized convolution, 00725 * use @normflag == 0. 00726 * (4) This builds a block accumulator pix, uses it here, and 00727 * destroys it. 00728 * (5) The added border, along with the use of an accumulator array, 00729 * allows computation without special treatment of pixels near 00730 * the image boundary, and runs in a time that is independent 00731 * of the size of the convolution kernel. 00732 */ 00733 PIX * 00734 pixWindowedMean(PIX *pixs, 00735 l_int32 wc, 00736 l_int32 hc, 00737 l_int32 hasborder, 00738 l_int32 normflag) 00739 { 00740 l_int32 i, j, w, h, d, wd, hd, wplc, wpld, wincr, hincr; 00741 l_uint32 val; 00742 l_uint32 *datac, *datad, *linec1, *linec2, *lined; 00743 l_float32 norm; 00744 PIX *pixb, *pixc, *pixd; 00745 00746 PROCNAME("pixWindowedMean"); 00747 00748 if (!pixs) 00749 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00750 d = pixGetDepth(pixs); 00751 if (d != 8 && d != 32) 00752 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); 00753 if (wc < 2 || hc < 2) 00754 return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL); 00755 00756 /* Add border if requested */ 00757 if (!hasborder) 00758 pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); 00759 else 00760 pixb = pixClone(pixs); 00761 00762 /* The output has wc + 1 border pixels stripped from each side 00763 * of pixb, and hc + 1 border pixels stripped from top and bottom. */ 00764 pixGetDimensions(pixb, &w, &h, NULL); 00765 wd = w - 2 * (wc + 1); 00766 hd = h - 2 * (hc + 1); 00767 if (wd < 2 || hd < 2) 00768 return (PIX *)ERROR_PTR("w or h too small for kernel", procName, NULL); 00769 if ((pixd = pixCreate(wd, hd, d)) == NULL) 00770 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00771 00772 /* Make the accumulator pix from pixb */ 00773 if ((pixc = pixBlockconvAccum(pixb)) == NULL) { 00774 pixDestroy(&pixb); 00775 pixDestroy(&pixd); 00776 return (PIX *)ERROR_PTR("pixc not made", procName, NULL); 00777 } 00778 wplc = pixGetWpl(pixc); 00779 wpld = pixGetWpl(pixd); 00780 datad = pixGetData(pixd); 00781 datac = pixGetData(pixc); 00782 00783 wincr = 2 * wc + 1; 00784 hincr = 2 * hc + 1; 00785 norm = 1.0; /* use this for sum-in-window */ 00786 if (normflag) 00787 norm = 1.0 / (wincr * hincr); 00788 for (i = 0; i < hd; i++) { 00789 linec1 = datac + i * wplc; 00790 linec2 = datac + (i + hincr) * wplc; 00791 lined = datad + i * wpld; 00792 for (j = 0; j < wd; j++) { 00793 val = linec2[j + wincr] - linec2[j] - linec1[j + wincr] + linec1[j]; 00794 if (d == 8) { 00795 val = (l_uint8)(norm * val); 00796 SET_DATA_BYTE(lined, j, val); 00797 } else { /* d == 32 */ 00798 val = (l_uint32)(norm * val); 00799 lined[j] = val; 00800 } 00801 } 00802 } 00803 00804 pixDestroy(&pixc); 00805 pixDestroy(&pixb); 00806 return pixd; 00807 } 00808 00809 00810 /*! 00811 * pixWindowedMeanSquare() 00812 * 00813 * Input: pixs (8 bpp grayscale) 00814 * wc, hc (half width/height of convolution kernel) 00815 * hasborder (use 1 if it already has (wc + 1) border pixels 00816 * on left and right, and (hc + 1) on top and bottom; 00817 * use 0 to add kernel-dependent border) 00818 * Return: pixd (32 bpp, average over rectangular window of 00819 * width = 2 * wc + 1 and height = 2 * hc + 1) 00820 * 00821 * Notes: 00822 * (1) A set of border pixels of width (wc + 1) on left and right, 00823 * and of height (hc + 1) on top and bottom, must be on the 00824 * pix before the accumulator is found. The output pixd 00825 * (after convolution) has this border removed. 00826 * If @hasborder = 0, the required border is added. 00827 * (2) The advantage is that we are unaffected by the boundary, and 00828 * it is not necessary to treat pixels within @wc and @hc of the 00829 * border differently. This is because processing for pixd 00830 * only takes place for pixels in pixs for which the 00831 * kernel is entirely contained in pixs. 00832 * (3) Why do we have an added border of width (@wc + 1) and 00833 * height (@hc + 1), when we only need @wc and @hc pixels 00834 * to satisfy this condition? Answer: the accumulators 00835 * are asymmetric, requiring an extra row and column of 00836 * pixels at top and left to work accurately. 00837 * (4) The added border, along with the use of an accumulator array, 00838 * allows computation without special treatment of pixels near 00839 * the image boundary, and runs in a time that is independent 00840 * of the size of the convolution kernel. 00841 */ 00842 PIX * 00843 pixWindowedMeanSquare(PIX *pixs, 00844 l_int32 wc, 00845 l_int32 hc, 00846 l_int32 hasborder) 00847 { 00848 l_int32 i, j, w, h, wd, hd, wpl, wpld, wincr, hincr; 00849 l_uint32 ival; 00850 l_uint32 *datad, *lined; 00851 l_float64 norm; 00852 l_float64 val; 00853 l_float64 *data, *line1, *line2; 00854 DPIX *dpix; 00855 PIX *pixb, *pixd; 00856 00857 PROCNAME("pixWindowedMeanSquare"); 00858 00859 if (!pixs || (pixGetDepth(pixs) != 8)) 00860 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00861 if (wc < 2 || hc < 2) 00862 return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL); 00863 00864 /* Add border if requested */ 00865 if (!hasborder) 00866 pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); 00867 else 00868 pixb = pixClone(pixs); 00869 00870 if ((dpix = pixMeanSquareAccum(pixb)) == NULL) 00871 return (PIX *)ERROR_PTR("dpix not made", procName, NULL); 00872 wpl = dpixGetWpl(dpix); 00873 data = dpixGetData(dpix); 00874 00875 /* The output has wc + 1 border pixels stripped from each side 00876 * of pixb, and hc + 1 border pixels stripped from top and bottom. */ 00877 pixGetDimensions(pixb, &w, &h, NULL); 00878 wd = w - 2 * (wc + 1); 00879 hd = h - 2 * (hc + 1); 00880 if (wd < 2 || hd < 2) 00881 return (PIX *)ERROR_PTR("w or h too small for kernel", procName, NULL); 00882 if ((pixd = pixCreate(wd, hd, 32)) == NULL) { 00883 dpixDestroy(&dpix); 00884 pixDestroy(&pixb); 00885 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00886 } 00887 wpld = pixGetWpl(pixd); 00888 datad = pixGetData(pixd); 00889 00890 wincr = 2 * wc + 1; 00891 hincr = 2 * hc + 1; 00892 norm = 1.0 / (wincr * hincr); 00893 for (i = 0; i < hd; i++) { 00894 line1 = data + i * wpl; 00895 line2 = data + (i + hincr) * wpl; 00896 lined = datad + i * wpld; 00897 for (j = 0; j < wd; j++) { 00898 val = line2[j + wincr] - line2[j] - line1[j + wincr] + line1[j]; 00899 ival = (l_uint32)(norm * val); 00900 lined[j] = ival; 00901 } 00902 } 00903 00904 dpixDestroy(&dpix); 00905 pixDestroy(&pixb); 00906 return pixd; 00907 } 00908 00909 00910 /*! 00911 * pixWindowedVariance() 00912 * 00913 * Input: pixm (mean over window; 8 or 32 bpp grayscale) 00914 * pixms (mean square over window; 32 bpp) 00915 * &fpixv (<optional return> float variance -- the ms deviation 00916 * from the mean) 00917 * &fpixrv (<optional return> float rms deviation from the mean) 00918 * Return: 0 if OK, 1 on error 00919 * 00920 * Notes: 00921 * (1) The mean and mean square values are precomputed, using 00922 * pixWindowedMean() and pixWindowedMeanSquare(). 00923 * (2) Either or both of the variance and square-root of variance 00924 * are returned as an fpix, where the variance is the 00925 * average over the window of the mean square difference of 00926 * the pixel value from the mean: 00927 * <(p - <p>)*(p - <p>)> = <p*p> - <p>*<p> 00928 * (3) To visualize the results: 00929 * - for both, use fpixDisplayMaxDynamicRange(). 00930 * - for rms deviation, simply convert the output fpix to pix, 00931 */ 00932 l_int32 00933 pixWindowedVariance(PIX *pixm, 00934 PIX *pixms, 00935 FPIX **pfpixv, 00936 FPIX **pfpixrv) 00937 { 00938 l_int32 i, j, w, h, ws, hs, ds, wplm, wplms, wplv, wplrv, valm, valms; 00939 l_float32 var; 00940 l_uint32 *linem, *linems, *datam, *datams; 00941 l_float32 *linev, *linerv, *datav, *datarv; 00942 FPIX *fpixv, *fpixrv; /* variance and square root of variance */ 00943 00944 PROCNAME("pixWindowedVariance"); 00945 00946 if (!pfpixv && !pfpixrv) 00947 return ERROR_INT("&fpixv and &fpixrv not defined", procName, 1); 00948 if (pfpixv) *pfpixv = NULL; 00949 if (pfpixrv) *pfpixrv = NULL; 00950 if (!pixm || pixGetDepth(pixm) != 8) 00951 return ERROR_INT("pixm undefined or not 8 bpp", procName, 1); 00952 if (!pixms || pixGetDepth(pixms) != 32) 00953 return ERROR_INT("pixms undefined or not 32 bpp", procName, 1); 00954 pixGetDimensions(pixm, &w, &h, NULL); 00955 pixGetDimensions(pixms, &ws, &hs, &ds); 00956 if (w != ws || h != hs) 00957 return ERROR_INT("pixm and pixms sizes differ", procName, 1); 00958 00959 if (pfpixv) { 00960 fpixv = fpixCreate(w, h); 00961 *pfpixv = fpixv; 00962 wplv = fpixGetWpl(fpixv); 00963 datav = fpixGetData(fpixv); 00964 } 00965 if (pfpixrv) { 00966 fpixrv = fpixCreate(w, h); 00967 *pfpixrv = fpixrv; 00968 wplrv = fpixGetWpl(fpixrv); 00969 datarv = fpixGetData(fpixrv); 00970 } 00971 00972 wplm = pixGetWpl(pixm); 00973 wplms = pixGetWpl(pixms); 00974 datam = pixGetData(pixm); 00975 datams = pixGetData(pixms); 00976 for (i = 0; i < h; i++) { 00977 linem = datam + i * wplm; 00978 linems = datams + i * wplms; 00979 if (pfpixv) 00980 linev = datav + i * wplv; 00981 if (pfpixrv) 00982 linerv = datarv + i * wplrv; 00983 for (j = 0; j < w; j++) { 00984 valm = GET_DATA_BYTE(linem, j); 00985 if (ds == 8) 00986 valms = GET_DATA_BYTE(linems, j); 00987 else /* ds == 32 */ 00988 valms = (l_int32)linems[j]; 00989 var = (l_float32)valms - (l_float32)valm * valm; 00990 if (pfpixv) 00991 linev[j] = var; 00992 if (pfpixrv) 00993 linerv[j] = (l_float32)sqrt(var); 00994 } 00995 } 00996 00997 return 0; 00998 } 00999 01000 01001 /*! 01002 * pixMeanSquareAccum() 01003 * 01004 * Input: pixs (8 bpp grayscale) 01005 * Return: dpix (64 bit array), or null on error 01006 * 01007 * Notes: 01008 * (1) Similar to pixBlockconvAccum(), this computes the 01009 * sum of the squares of the pixel values in such a way 01010 * that the value at (i,j) is the sum of all squares in 01011 * the rectangle from the origin to (i,j). 01012 * (2) The general recursion relation (v are squared pixel values) is 01013 * a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1) 01014 * For the first line, this reduces to the special case 01015 * a(i,j) = v(i,j) + a(i, j-1) 01016 * For the first column, the special case is 01017 * a(i,j) = v(i,j) + a(i-1, j) 01018 */ 01019 DPIX * 01020 pixMeanSquareAccum(PIX *pixs) 01021 { 01022 l_int32 i, j, w, h, wpl, wpls, val; 01023 l_uint32 *datas, *lines; 01024 l_float64 *data, *line, *linep; 01025 DPIX *dpix; 01026 01027 PROCNAME("pixMeanSquareAccum"); 01028 01029 01030 if (!pixs || (pixGetDepth(pixs) != 8)) 01031 return (DPIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 01032 pixGetDimensions(pixs, &w, &h, NULL); 01033 if ((dpix = dpixCreate(w, h)) == NULL) 01034 return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); 01035 01036 datas = pixGetData(pixs); 01037 wpls = pixGetWpl(pixs); 01038 data = dpixGetData(dpix); 01039 wpl = dpixGetWpl(dpix); 01040 01041 lines = datas; 01042 line = data; 01043 for (j = 0; j < w; j++) { /* first line */ 01044 val = GET_DATA_BYTE(lines, j); 01045 if (j == 0) 01046 line[0] = val * val; 01047 else 01048 line[j] = line[j - 1] + val * val; 01049 } 01050 01051 /* Do the other lines */ 01052 for (i = 1; i < h; i++) { 01053 lines = datas + i * wpls; 01054 line = data + i * wpl; /* current dest line */ 01055 linep = line - wpl;; /* prev dest line */ 01056 for (j = 0; j < w; j++) { 01057 val = GET_DATA_BYTE(lines, j); 01058 if (j == 0) 01059 line[0] = linep[0] + val * val; 01060 else 01061 line[j] = line[j - 1] + linep[j] - linep[j - 1] + val * val; 01062 } 01063 } 01064 01065 return dpix; 01066 } 01067 01068 01069 /*----------------------------------------------------------------------* 01070 * Binary block sum/rank * 01071 *----------------------------------------------------------------------*/ 01072 /*! 01073 * pixBlockrank() 01074 * 01075 * Input: pixs (1 bpp) 01076 * accum pix (<optional> 32 bpp) 01077 * wc, hc (half width/height of block sum/rank kernel) 01078 * rank (between 0.0 and 1.0; 0.5 is median filter) 01079 * Return: pixd (1 bpp) 01080 * 01081 * Notes: 01082 * (1) The full width and height of the convolution kernel 01083 * are (2 * wc + 1) and (2 * hc + 1) 01084 * (2) This returns a pixd where each pixel is a 1 if the 01085 * neighborhood (2 * wc + 1) x (2 * hc + 1)) pixels 01086 * contains the rank fraction of 1 pixels. Otherwise, 01087 * the returned pixel is 0. Note that the special case 01088 * of rank = 0.0 is always satisfied, so the returned 01089 * pixd has all pixels with value 1. 01090 * (3) If accum pix is null, make one, use it, and destroy it 01091 * before returning; otherwise, just use the input accum pix 01092 * (4) If both wc and hc are 0, returns a copy unless rank == 0.0, 01093 * in which case this returns an all-ones image. 01094 * (5) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, 01095 * where (w,h) are the dimensions of pixs. 01096 */ 01097 PIX * 01098 pixBlockrank(PIX *pixs, 01099 PIX *pixacc, 01100 l_int32 wc, 01101 l_int32 hc, 01102 l_float32 rank) 01103 { 01104 l_int32 w, h, d, thresh; 01105 PIX *pixt, *pixd; 01106 01107 PROCNAME("pixBlockrank"); 01108 01109 if (!pixs) 01110 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01111 pixGetDimensions(pixs, &w, &h, &d); 01112 if (d != 1) 01113 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 01114 if (rank < 0.0 || rank > 1.0) 01115 return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); 01116 01117 if (rank == 0.0) { 01118 pixd = pixCreateTemplate(pixs); 01119 pixSetAll(pixd); 01120 return pixd; 01121 } 01122 01123 if (wc < 0) wc = 0; 01124 if (hc < 0) hc = 0; 01125 if (w < 2 * wc + 1 || h < 2 * hc + 1) { 01126 wc = L_MIN(wc, (w - 1) / 2); 01127 hc = L_MIN(hc, (h - 1) / 2); 01128 L_WARNING("kernel too large; reducing!", procName); 01129 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 01130 } 01131 if (wc == 0 && hc == 0) 01132 return pixCopy(NULL, pixs); 01133 01134 if ((pixt = pixBlocksum(pixs, pixacc, wc, hc)) == NULL) 01135 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 01136 01137 /* 1 bpp block rank filter output. 01138 * Must invert because threshold gives 1 for values < thresh, 01139 * but we need a 1 if the value is >= thresh. */ 01140 thresh = (l_int32)(255. * rank); 01141 pixd = pixThresholdToBinary(pixt, thresh); 01142 pixInvert(pixd, pixd); 01143 pixDestroy(&pixt); 01144 return pixd; 01145 } 01146 01147 01148 /*! 01149 * pixBlocksum() 01150 * 01151 * Input: pixs (1 bpp) 01152 * accum pix (<optional> 32 bpp) 01153 * wc, hc (half width/height of block sum/rank kernel) 01154 * Return: pixd (8 bpp) 01155 * 01156 * Notes: 01157 * (1) If accum pix is null, make one and destroy it before 01158 * returning; otherwise, just use the input accum pix 01159 * (2) The full width and height of the convolution kernel 01160 * are (2 * wc + 1) and (2 * hc + 1) 01161 * (3) Use of wc = hc = 1, followed by pixInvert() on the 01162 * 8 bpp result, gives a nice anti-aliased, and somewhat 01163 * darkened, result on text. 01164 * (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, 01165 * where (w,h) are the dimensions of pixs. 01166 * (5) Returns in each dest pixel the sum of all src pixels 01167 * that are within a block of size of the kernel, centered 01168 * on the dest pixel. This sum is the number of src ON 01169 * pixels in the block at each location, normalized to 255 01170 * for a block containing all ON pixels. For pixels near 01171 * the boundary, where the block is not entirely contained 01172 * within the image, we then multiply by a second normalization 01173 * factor that is greater than one, so that all results 01174 * are normalized by the number of participating pixels 01175 * within the block. 01176 */ 01177 PIX * 01178 pixBlocksum(PIX *pixs, 01179 PIX *pixacc, 01180 l_int32 wc, 01181 l_int32 hc) 01182 { 01183 l_int32 w, h, d, wplt, wpld; 01184 l_uint32 *datat, *datad; 01185 PIX *pixt, *pixd; 01186 01187 PROCNAME("pixBlocksum"); 01188 01189 if (!pixs) 01190 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01191 pixGetDimensions(pixs, &w, &h, &d); 01192 if (d != 1) 01193 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 01194 if (wc < 0) wc = 0; 01195 if (hc < 0) hc = 0; 01196 if (w < 2 * wc + 1 || h < 2 * hc + 1) { 01197 wc = L_MIN(wc, (w - 1) / 2); 01198 hc = L_MIN(hc, (h - 1) / 2); 01199 L_WARNING("kernel too large; reducing!", procName); 01200 L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc); 01201 } 01202 if (wc == 0 && hc == 0) 01203 return pixCopy(NULL, pixs); 01204 01205 if (pixacc) { 01206 if (pixGetDepth(pixacc) != 32) 01207 return (PIX *)ERROR_PTR("pixacc not 32 bpp", procName, NULL); 01208 pixt = pixClone(pixacc); 01209 } 01210 else { 01211 if ((pixt = pixBlockconvAccum(pixs)) == NULL) 01212 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 01213 } 01214 01215 /* 8 bpp block sum output */ 01216 if ((pixd = pixCreate(w, h, 8)) == NULL) { 01217 pixDestroy(&pixt); 01218 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01219 } 01220 pixCopyResolution(pixd, pixs); 01221 01222 wpld = pixGetWpl(pixd); 01223 wplt = pixGetWpl(pixt); 01224 datad = pixGetData(pixd); 01225 datat = pixGetData(pixt); 01226 blocksumLow(datad, w, h, wpld, datat, wplt, wc, hc); 01227 01228 pixDestroy(&pixt); 01229 return pixd; 01230 } 01231 01232 01233 /*----------------------------------------------------------------------* 01234 * Census transform * 01235 *----------------------------------------------------------------------*/ 01236 /*! 01237 * pixCensusTransform() 01238 * 01239 * Input: pixs (8 bpp) 01240 * halfsize (of square over which neighbors are averaged) 01241 * accum pix (<optional> 32 bpp) 01242 * Return: pixd (1 bpp) 01243 * 01244 * Notes: 01245 * (1) The Census transform was invented by Ramin Zabih and John Woodfill 01246 * ("Non-parametric local transforms for computing visual 01247 * correspondence", Third European Conference on Computer Vision, 01248 * Stockholm, Sweden, May 1994); see publications at 01249 * http://www.cs.cornell.edu/~rdz/index.htm 01250 * This compares each pixel against the average of its neighbors, 01251 * in a square of odd dimension centered on the pixel. 01252 * If the pixel is greater than the average of its neighbors, 01253 * the output pixel value is 1; otherwise it is 0. 01254 * (2) This can be used as an encoding for an image that is 01255 * fairly robust against slow illumination changes, with 01256 * applications in image comparison and mosaicing. 01257 * (3) The size of the convolution kernel is (2 * halfsize + 1) 01258 * on a side. The halfsize parameter must be >= 1. 01259 * (4) If accum pix is null, make one, use it, and destroy it 01260 * before returning; otherwise, just use the input accum pix 01261 */ 01262 PIX * 01263 pixCensusTransform(PIX *pixs, 01264 l_int32 halfsize, 01265 PIX *pixacc) 01266 { 01267 l_int32 i, j, w, h, wpls, wplv, wpld; 01268 l_int32 vals, valv; 01269 l_uint32 *datas, *datav, *datad, *lines, *linev, *lined; 01270 PIX *pixav, *pixd; 01271 01272 PROCNAME("pixCensusTransform"); 01273 01274 if (!pixs) 01275 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01276 if (pixGetDepth(pixs) != 8) 01277 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01278 if (halfsize < 1) 01279 return (PIX *)ERROR_PTR("halfsize must be >= 1", procName, NULL); 01280 01281 /* Get the average of each pixel with its neighbors */ 01282 if ((pixav = pixBlockconvGray(pixs, pixacc, halfsize, halfsize)) 01283 == NULL) 01284 return (PIX *)ERROR_PTR("pixav not made", procName, NULL); 01285 01286 /* Subtract the pixel from the average, and then compare 01287 * the pixel value with the remaining average */ 01288 pixGetDimensions(pixs, &w, &h, NULL); 01289 if ((pixd = pixCreate(w, h, 1)) == NULL) { 01290 pixDestroy(&pixav); 01291 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01292 } 01293 datas = pixGetData(pixs); 01294 datav = pixGetData(pixav); 01295 datad = pixGetData(pixd); 01296 wpls = pixGetWpl(pixs); 01297 wplv = pixGetWpl(pixav); 01298 wpld = pixGetWpl(pixd); 01299 for (i = 0; i < h; i++) { 01300 lines = datas + i * wpls; 01301 linev = datav + i * wplv; 01302 lined = datad + i * wpld; 01303 for (j = 0; j < w; j++) { 01304 vals = GET_DATA_BYTE(lines, j); 01305 valv = GET_DATA_BYTE(linev, j); 01306 if (vals > valv) 01307 SET_DATA_BIT(lined, j); 01308 } 01309 } 01310 01311 pixDestroy(&pixav); 01312 return pixd; 01313 } 01314 01315 01316 /*----------------------------------------------------------------------* 01317 * Generic convolution * 01318 *----------------------------------------------------------------------*/ 01319 /*! 01320 * pixConvolve() 01321 * 01322 * Input: pixs (8, 16, 32 bpp; no colormap) 01323 * kernel 01324 * outdepth (of pixd: 8, 16 or 32) 01325 * normflag (1 to normalize kernel to unit sum; 0 otherwise) 01326 * Return: pixd (8, 16 or 32 bpp) 01327 * 01328 * Notes: 01329 * (1) This gives a convolution with an arbitrary kernel. 01330 * (2) The input pixs must have only one sample/pixel. 01331 * To do a convolution on an RGB image, use pixConvolveRGB(). 01332 * (3) The parameter @outdepth determines the depth of the result. 01333 * If the kernel is normalized to unit sum, the output values 01334 * can never exceed 255, so an output depth of 8 bpp is sufficient. 01335 * If the kernel is not normalized, it may be necessary to use 01336 * 16 or 32 bpp output to avoid overflow. 01337 * (4) If normflag == 1, the result is normalized by scaling 01338 * all kernel values for a unit sum. Do not normalize if 01339 * the kernel has null sum, such as a DoG. 01340 * (5) The kernel values can be positive or negative, but the 01341 * result for the convolution can only be stored as a positive 01342 * number. Consequently, if it goes negative, the choices are 01343 * to clip to 0 or take the absolute value. We're choosing 01344 * the former for now. Another possibility would be to output 01345 * a second unsigned image for the negative values. 01346 * (6) This uses a mirrored border to avoid special casing on 01347 * the boundaries. 01348 * (7) To get a subsampled output, call l_setConvolveSampling(). 01349 * The time to make a subsampled output is reduced by the 01350 * product of the sampling factors. 01351 * (8) The function is slow, running at about 12 machine cycles for 01352 * each pixel-op in the convolution. For example, with a 3 GHz 01353 * cpu, a 1 Mpixel grayscale image, and a kernel with 01354 * (sx * sy) = 25 elements, the convolution takes about 100 msec. 01355 */ 01356 PIX * 01357 pixConvolve(PIX *pixs, 01358 L_KERNEL *kel, 01359 l_int32 outdepth, 01360 l_int32 normflag) 01361 { 01362 l_int32 i, j, id, jd, k, m, w, h, d, wd, hd, sx, sy, cx, cy, wplt, wpld; 01363 l_int32 val; 01364 l_uint32 *datat, *datad, *linet, *lined; 01365 l_float32 sum; 01366 L_KERNEL *keli, *keln; 01367 PIX *pixt, *pixd; 01368 01369 PROCNAME("pixConvolve"); 01370 01371 if (!pixs) 01372 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01373 if (pixGetColormap(pixs)) 01374 return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); 01375 pixGetDimensions(pixs, &w, &h, &d); 01376 if (d != 8 && d != 16 && d != 32) 01377 return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL); 01378 if (!kel) 01379 return (PIX *)ERROR_PTR("kel not defined", procName, NULL); 01380 01381 keli = kernelInvert(kel); 01382 kernelGetParameters(keli, &sy, &sx, &cy, &cx); 01383 if (normflag) 01384 keln = kernelNormalize(keli, 1.0); 01385 else 01386 keln = kernelCopy(keli); 01387 01388 if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) 01389 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 01390 01391 wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX; 01392 hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY; 01393 pixd = pixCreate(wd, hd, outdepth); 01394 datat = pixGetData(pixt); 01395 datad = pixGetData(pixd); 01396 wplt = pixGetWpl(pixt); 01397 wpld = pixGetWpl(pixd); 01398 for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) { 01399 lined = datad + id * wpld; 01400 for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) { 01401 sum = 0.0; 01402 for (k = 0; k < sy; k++) { 01403 linet = datat + (i + k) * wplt; 01404 if (d == 8) { 01405 for (m = 0; m < sx; m++) { 01406 val = GET_DATA_BYTE(linet, j + m); 01407 sum += val * keln->data[k][m]; 01408 } 01409 } 01410 else if (d == 16) { 01411 for (m = 0; m < sx; m++) { 01412 val = GET_DATA_TWO_BYTES(linet, j + m); 01413 sum += val * keln->data[k][m]; 01414 } 01415 } 01416 else { /* d == 32 */ 01417 for (m = 0; m < sx; m++) { 01418 val = *(linet + j + m); 01419 sum += val * keln->data[k][m]; 01420 } 01421 } 01422 } 01423 #if 1 01424 if (sum < 0.0) sum = -sum; /* make it non-negative */ 01425 #endif 01426 if (outdepth == 8) 01427 SET_DATA_BYTE(lined, jd, (l_int32)(sum + 0.5)); 01428 else if (outdepth == 16) 01429 SET_DATA_TWO_BYTES(lined, jd, (l_int32)(sum + 0.5)); 01430 else /* outdepth == 32 */ 01431 *(lined + jd) = (l_uint32)(sum + 0.5); 01432 } 01433 } 01434 01435 kernelDestroy(&keli); 01436 kernelDestroy(&keln); 01437 pixDestroy(&pixt); 01438 return pixd; 01439 } 01440 01441 01442 /*! 01443 * pixConvolveSep() 01444 * 01445 * Input: pixs (8, 16, 32 bpp; no colormap) 01446 * kelx (x-dependent kernel) 01447 * kely (y-dependent kernel) 01448 * outdepth (of pixd: 8, 16 or 32) 01449 * normflag (1 to normalize kernel to unit sum; 0 otherwise) 01450 * Return: pixd (8, 16 or 32 bpp) 01451 * 01452 * Notes: 01453 * (1) This does a convolution with a separable kernel that is 01454 * is a sequence of convolutions in x and y. The two 01455 * one-dimensional kernel components must be input separately; 01456 * the full kernel is the product of these components. 01457 * The support for the full kernel is thus a rectangular region. 01458 * (2) The input pixs must have only one sample/pixel. 01459 * To do a convolution on an RGB image, use pixConvolveSepRGB(). 01460 * (3) The parameter @outdepth determines the depth of the result. 01461 * If the kernel is normalized to unit sum, the output values 01462 * can never exceed 255, so an output depth of 8 bpp is sufficient. 01463 * If the kernel is not normalized, it may be necessary to use 01464 * 16 or 32 bpp output to avoid overflow. 01465 * (2) The @normflag parameter is used as in pixConvolve(). 01466 * (4) The kernel values can be positive or negative, but the 01467 * result for the convolution can only be stored as a positive 01468 * number. Consequently, if it goes negative, the choices are 01469 * to clip to 0 or take the absolute value. We're choosing 01470 * the former for now. Another possibility would be to output 01471 * a second unsigned image for the negative values. 01472 * (5) Warning: if you use l_setConvolveSampling() to get a 01473 * subsampled output, and the sampling factor is larger than 01474 * the kernel half-width, it is faster to use the non-separable 01475 * version pixConvolve(). This is because the first convolution 01476 * here must be done on every raster line, regardless of the 01477 * vertical sampling factor. If the sampling factor is smaller 01478 * than kernel half-width, it's faster to use the separable 01479 * convolution. 01480 * (6) This uses mirrored borders to avoid special casing on 01481 * the boundaries. 01482 */ 01483 PIX * 01484 pixConvolveSep(PIX *pixs, 01485 L_KERNEL *kelx, 01486 L_KERNEL *kely, 01487 l_int32 outdepth, 01488 l_int32 normflag) 01489 { 01490 l_int32 d, xfact, yfact; 01491 L_KERNEL *kelxn, *kelyn; 01492 PIX *pixt, *pixd; 01493 01494 PROCNAME("pixConvolveSep"); 01495 01496 if (!pixs) 01497 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01498 d = pixGetDepth(pixs); 01499 if (d != 8 && d != 16 && d != 32) 01500 return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL); 01501 if (!kelx) 01502 return (PIX *)ERROR_PTR("kelx not defined", procName, NULL); 01503 if (!kely) 01504 return (PIX *)ERROR_PTR("kely not defined", procName, NULL); 01505 01506 xfact = ConvolveSamplingFactX; 01507 yfact = ConvolveSamplingFactY; 01508 if (normflag) { 01509 kelxn = kernelNormalize(kelx, 1000.0); 01510 kelyn = kernelNormalize(kely, 0.001); 01511 l_setConvolveSampling(xfact, 1); 01512 pixt = pixConvolve(pixs, kelxn, 32, 0); 01513 l_setConvolveSampling(1, yfact); 01514 pixd = pixConvolve(pixt, kelyn, outdepth, 0); 01515 l_setConvolveSampling(xfact, yfact); /* restore */ 01516 kernelDestroy(&kelxn); 01517 kernelDestroy(&kelyn); 01518 } 01519 else { /* don't normalize */ 01520 l_setConvolveSampling(xfact, 1); 01521 pixt = pixConvolve(pixs, kelx, 32, 0); 01522 l_setConvolveSampling(1, yfact); 01523 pixd = pixConvolve(pixt, kely, outdepth, 0); 01524 l_setConvolveSampling(xfact, yfact); 01525 } 01526 01527 pixDestroy(&pixt); 01528 return pixd; 01529 } 01530 01531 01532 /*! 01533 * pixConvolveRGB() 01534 * 01535 * Input: pixs (32 bpp rgb) 01536 * kernel 01537 * Return: pixd (32 bpp rgb) 01538 * 01539 * Notes: 01540 * (1) This gives a convolution on an RGB image using an 01541 * arbitrary kernel (which we normalize to keep each 01542 * component within the range [0 ... 255]. 01543 * (2) The input pixs must be RGB. 01544 * (3) The kernel values can be positive or negative, but the 01545 * result for the convolution can only be stored as a positive 01546 * number. Consequently, if it goes negative, we clip the 01547 * result to 0. 01548 * (4) To get a subsampled output, call l_setConvolveSampling(). 01549 * The time to make a subsampled output is reduced by the 01550 * product of the sampling factors. 01551 * (5) This uses a mirrored border to avoid special casing on 01552 * the boundaries. 01553 */ 01554 PIX * 01555 pixConvolveRGB(PIX *pixs, 01556 L_KERNEL *kel) 01557 { 01558 PIX *pixt, *pixr, *pixg, *pixb, *pixd; 01559 01560 PROCNAME("pixConvolveRGB"); 01561 01562 if (!pixs) 01563 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01564 if (pixGetDepth(pixs) != 32) 01565 return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL); 01566 if (!kel) 01567 return (PIX *)ERROR_PTR("kel not defined", procName, NULL); 01568 01569 pixt = pixGetRGBComponent(pixs, COLOR_RED); 01570 pixr = pixConvolve(pixt, kel, 8, 1); 01571 pixDestroy(&pixt); 01572 pixt = pixGetRGBComponent(pixs, COLOR_GREEN); 01573 pixg = pixConvolve(pixt, kel, 8, 1); 01574 pixDestroy(&pixt); 01575 pixt = pixGetRGBComponent(pixs, COLOR_BLUE); 01576 pixb = pixConvolve(pixt, kel, 8, 1); 01577 pixDestroy(&pixt); 01578 pixd = pixCreateRGBImage(pixr, pixg, pixb); 01579 01580 pixDestroy(&pixr); 01581 pixDestroy(&pixg); 01582 pixDestroy(&pixb); 01583 return pixd; 01584 } 01585 01586 01587 /*! 01588 * pixConvolveRGBSep() 01589 * 01590 * Input: pixs (32 bpp rgb) 01591 * kelx (x-dependent kernel) 01592 * kely (y-dependent kernel) 01593 * Return: pixd (32 bpp rgb) 01594 * 01595 * Notes: 01596 * (1) This does a convolution on an RGB image using a separable 01597 * kernel that is a sequence of convolutions in x and y. The two 01598 * one-dimensional kernel components must be input separately; 01599 * the full kernel is the product of these components. 01600 * The support for the full kernel is thus a rectangular region. 01601 * (2) The kernel values can be positive or negative, but the 01602 * result for the convolution can only be stored as a positive 01603 * number. Consequently, if it goes negative, we clip the 01604 * result to 0. 01605 * (3) To get a subsampled output, call l_setConvolveSampling(). 01606 * The time to make a subsampled output is reduced by the 01607 * product of the sampling factors. 01608 * (4) This uses a mirrored border to avoid special casing on 01609 * the boundaries. 01610 */ 01611 PIX * 01612 pixConvolveRGBSep(PIX *pixs, 01613 L_KERNEL *kelx, 01614 L_KERNEL *kely) 01615 { 01616 PIX *pixt, *pixr, *pixg, *pixb, *pixd; 01617 01618 PROCNAME("pixConvolveRGBSep"); 01619 01620 if (!pixs) 01621 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01622 if (pixGetDepth(pixs) != 32) 01623 return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL); 01624 if (!kelx || !kely) 01625 return (PIX *)ERROR_PTR("kelx, kely not both defined", procName, NULL); 01626 01627 pixt = pixGetRGBComponent(pixs, COLOR_RED); 01628 pixr = pixConvolveSep(pixt, kelx, kely, 8, 1); 01629 pixDestroy(&pixt); 01630 pixt = pixGetRGBComponent(pixs, COLOR_GREEN); 01631 pixg = pixConvolveSep(pixt, kelx, kely, 8, 1); 01632 pixDestroy(&pixt); 01633 pixt = pixGetRGBComponent(pixs, COLOR_BLUE); 01634 pixb = pixConvolveSep(pixt, kelx, kely, 8, 1); 01635 pixDestroy(&pixt); 01636 pixd = pixCreateRGBImage(pixr, pixg, pixb); 01637 01638 pixDestroy(&pixr); 01639 pixDestroy(&pixg); 01640 pixDestroy(&pixb); 01641 return pixd; 01642 } 01643 01644 01645 /*----------------------------------------------------------------------* 01646 * Generic convolution with float array * 01647 *----------------------------------------------------------------------*/ 01648 /*! 01649 * fpixConvolve() 01650 * 01651 * Input: fpixs (32 bit float array) 01652 * kernel 01653 * normflag (1 to normalize kernel to unit sum; 0 otherwise) 01654 * Return: fpixd (32 bit float array) 01655 * 01656 * Notes: 01657 * (1) This gives a float convolution with an arbitrary kernel. 01658 * (2) If normflag == 1, the result is normalized by scaling 01659 * all kernel values for a unit sum. Do not normalize if 01660 * the kernel has null sum, such as a DoG. 01661 * (3) With the FPix, there are no issues about negative 01662 * array or kernel values. The convolution is performed 01663 * with single precision arithmetic. 01664 * (4) To get a subsampled output, call l_setConvolveSampling(). 01665 * The time to make a subsampled output is reduced by the 01666 * product of the sampling factors. 01667 * (5) This uses a mirrored border to avoid special casing on 01668 * the boundaries. 01669 */ 01670 FPIX * 01671 fpixConvolve(FPIX *fpixs, 01672 L_KERNEL *kel, 01673 l_int32 normflag) 01674 { 01675 l_int32 i, j, id, jd, k, m, w, h, wd, hd, sx, sy, cx, cy, wplt, wpld; 01676 l_float32 val; 01677 l_float32 *datat, *datad, *linet, *lined; 01678 l_float32 sum; 01679 L_KERNEL *keli, *keln; 01680 FPIX *fpixt, *fpixd; 01681 01682 PROCNAME("fpixConvolve"); 01683 01684 if (!fpixs) 01685 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); 01686 if (!kel) 01687 return (FPIX *)ERROR_PTR("kel not defined", procName, NULL); 01688 01689 keli = kernelInvert(kel); 01690 kernelGetParameters(keli, &sy, &sx, &cy, &cx); 01691 if (normflag) 01692 keln = kernelNormalize(keli, 1.0); 01693 else 01694 keln = kernelCopy(keli); 01695 01696 fpixGetDimensions(fpixs, &w, &h); 01697 fpixt = fpixAddMirroredBorder(fpixs, cx, sx - cx, cy, sy - cy); 01698 if (!fpixt) 01699 return (FPIX *)ERROR_PTR("fpixt not made", procName, NULL); 01700 01701 wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX; 01702 hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY; 01703 fpixd = fpixCreate(wd, hd); 01704 datat = fpixGetData(fpixt); 01705 datad = fpixGetData(fpixd); 01706 wplt = fpixGetWpl(fpixt); 01707 wpld = fpixGetWpl(fpixd); 01708 for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) { 01709 lined = datad + id * wpld; 01710 for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) { 01711 sum = 0.0; 01712 for (k = 0; k < sy; k++) { 01713 linet = datat + (i + k) * wplt; 01714 for (m = 0; m < sx; m++) { 01715 val = *(linet + j + m); 01716 sum += val * keln->data[k][m]; 01717 } 01718 } 01719 *(lined + jd) = sum; 01720 } 01721 } 01722 01723 kernelDestroy(&keli); 01724 kernelDestroy(&keln); 01725 fpixDestroy(&fpixt); 01726 return fpixd; 01727 } 01728 01729 01730 /*! 01731 * fpixConvolveSep() 01732 * 01733 * Input: fpixs (32 bit float array) 01734 * kelx (x-dependent kernel) 01735 * kely (y-dependent kernel) 01736 * normflag (1 to normalize kernel to unit sum; 0 otherwise) 01737 * Return: fpixd (32 bit float array) 01738 * 01739 * Notes: 01740 * (1) This does a convolution with a separable kernel that is 01741 * is a sequence of convolutions in x and y. The two 01742 * one-dimensional kernel components must be input separately; 01743 * the full kernel is the product of these components. 01744 * The support for the full kernel is thus a rectangular region. 01745 * (2) The normflag parameter is used as in fpixConvolve(). 01746 * (3) Warning: if you use l_setConvolveSampling() to get a 01747 * subsampled output, and the sampling factor is larger than 01748 * the kernel half-width, it is faster to use the non-separable 01749 * version pixConvolve(). This is because the first convolution 01750 * here must be done on every raster line, regardless of the 01751 * vertical sampling factor. If the sampling factor is smaller 01752 * than kernel half-width, it's faster to use the separable 01753 * convolution. 01754 * (4) This uses mirrored borders to avoid special casing on 01755 * the boundaries. 01756 */ 01757 FPIX * 01758 fpixConvolveSep(FPIX *fpixs, 01759 L_KERNEL *kelx, 01760 L_KERNEL *kely, 01761 l_int32 normflag) 01762 { 01763 l_int32 xfact, yfact; 01764 L_KERNEL *kelxn, *kelyn; 01765 FPIX *fpixt, *fpixd; 01766 01767 PROCNAME("fpixConvolveSep"); 01768 01769 if (!fpixs) 01770 return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL); 01771 if (!kelx) 01772 return (FPIX *)ERROR_PTR("kelx not defined", procName, NULL); 01773 if (!kely) 01774 return (FPIX *)ERROR_PTR("kely not defined", procName, NULL); 01775 01776 xfact = ConvolveSamplingFactX; 01777 yfact = ConvolveSamplingFactY; 01778 if (normflag) { 01779 kelxn = kernelNormalize(kelx, 1.0); 01780 kelyn = kernelNormalize(kely, 1.0); 01781 l_setConvolveSampling(xfact, 1); 01782 fpixt = fpixConvolve(fpixs, kelxn, 0); 01783 l_setConvolveSampling(1, yfact); 01784 fpixd = fpixConvolve(fpixt, kelyn, 0); 01785 l_setConvolveSampling(xfact, yfact); /* restore */ 01786 kernelDestroy(&kelxn); 01787 kernelDestroy(&kelyn); 01788 } 01789 else { /* don't normalize */ 01790 l_setConvolveSampling(xfact, 1); 01791 fpixt = fpixConvolve(fpixs, kelx, 0); 01792 l_setConvolveSampling(1, yfact); 01793 fpixd = fpixConvolve(fpixt, kely, 0); 01794 l_setConvolveSampling(xfact, yfact); 01795 } 01796 01797 fpixDestroy(&fpixt); 01798 return fpixd; 01799 } 01800 01801 01802 /*------------------------------------------------------------------------* 01803 * Set parameter for convolution subsampling * 01804 *------------------------------------------------------------------------*/ 01805 /*! 01806 * l_setConvolveSampling() 01807 * 01808 * Input: xfact, yfact (integer >= 1) 01809 * Return: void 01810 * 01811 * Notes: 01812 * (1) This sets the x and y output subsampling factors for generic pix 01813 * and fpix convolution. The default values are 1 (no subsampling). 01814 */ 01815 void 01816 l_setConvolveSampling(l_int32 xfact, 01817 l_int32 yfact) 01818 { 01819 if (xfact < 1) xfact = 1; 01820 if (yfact < 1) yfact = 1; 01821 ConvolveSamplingFactX = xfact; 01822 ConvolveSamplingFactY = yfact; 01823 } 01824