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 * enhance.c 00018 * 00019 * Gamma TRC (tone reproduction curve) mapping 00020 * PIX *pixGammaTRC() 00021 * PIX *pixGammaTRCMasked() 00022 * PIX *pixGammaTRCWithAlpha() 00023 * NUMA *numaGammaTRC() 00024 * 00025 * Contrast enhancement 00026 * PIX *pixContrastTRC() 00027 * PIX *pixContrastTRCMasked() 00028 * NUMA *numaContrastTRC() 00029 * 00030 * Histogram equalization 00031 * PIX *pixEqualizeTRC() 00032 * NUMA *numaEqualizeTRC() 00033 * 00034 * Generic TRC mapper 00035 * PIX *pixTRCMap() 00036 * 00037 * Unsharp-masking 00038 * PIX *pixUnsharpMasking() 00039 * PIX *pixUnsharpMaskingGray() 00040 * PIX *pixUnsharpMaskingFast() 00041 * PIX *pixUnsharpMaskingGrayFast() 00042 * PIX *pixUnsharpMaskingGray1D() 00043 * PIX *pixUnsharpMaskingGray2D() 00044 * 00045 * Hue and saturation modification 00046 * PIX *pixModifyHue() 00047 * PIX *pixModifySaturation() 00048 * l_int32 pixMeasureSaturation() 00049 * 00050 * General multiplicative constant color transform 00051 * PIX *pixMultConstantColor() 00052 * PIX *pixMultMatrixColor() 00053 * 00054 * Edge by bandpass 00055 * PIX *pixHalfEdgeByBandpass() 00056 * 00057 * Gamma correction, contrast enhancement and histogram equalization 00058 * apply a simple mapping function to each pixel (or, for color 00059 * images, to each sample (i.e., r,g,b) of the pixel). 00060 * 00061 * - Gamma correction either lightens the image or darkens 00062 * it, depending on whether the gamma factor is greater 00063 * or less than 1.0, respectively. 00064 * 00065 * - Contrast enhancement darkens the pixels that are already 00066 * darker than the middle of the dynamic range (128) 00067 * and lightens pixels that are lighter than 128. 00068 * 00069 * - Histogram equalization remaps to have the same number 00070 * of image pixels at each of 256 intensity values. This is 00071 * a quick and dirty method of adjusting contrast and brightness 00072 * to bring out details in both light and dark regions. 00073 * 00074 * Unsharp masking is a more complicated enhancement. 00075 * A "high frequency" image, generated by subtracting 00076 * the smoothed ("low frequency") part of the image from 00077 * itself, has all the energy at the edges. This "edge image" 00078 * has 0 average value. A fraction of the edge image is 00079 * then added to the original, enhancing the differences 00080 * between pixel values at edges. Because we represent 00081 * images as l_uint8 arrays, we preserve dynamic range and 00082 * handle negative values by doing all the arithmetic on 00083 * shifted l_uint16 arrays; the l_uint8 values are recovered 00084 * at the end. 00085 * 00086 * Hue and saturation modification work in HSV space. Because 00087 * this is too large for efficient table lookup, each pixel value 00088 * is transformed to HSV, modified, and transformed back. 00089 * It's not the fastest way to do this, but the method is 00090 * easily understood. 00091 * 00092 * Unsharp masking is never in-place, and returns a clone if no 00093 * operation is to be performed. 00094 */ 00095 00096 00097 #include <stdio.h> 00098 #include <stdlib.h> 00099 #include <math.h> 00100 #include "allheaders.h" 00101 00102 /* Scales contrast enhancement factor to have a useful range 00103 * between 0.0 and 1.0 */ 00104 static const l_float32 ENHANCE_SCALE_FACTOR = 5.; 00105 00106 /* Default number of pixels sampled to determine histogram */ 00107 static const l_int32 DEFAULT_HISTO_SAMPLES = 100000; 00108 00109 00110 /*-------------------------------------------------------------* 00111 * Gamma TRC (tone reproduction curve) mapping * 00112 *-------------------------------------------------------------*/ 00113 /*! 00114 * pixGammaTRC() 00115 * 00116 * Input: pixd (<optional> null or equal to pixs) 00117 * pixs (8 or 32 bpp; or 2, 4 or 8 bpp with colormap) 00118 * gamma (gamma correction; must be > 0.0) 00119 * minval (input value that gives 0 for output; can be < 0) 00120 * maxval (input value that gives 255 for output; can be > 255) 00121 * Return: pixd always 00122 * 00123 * Notes: 00124 * (1) pixd must either be null or equal to pixs. 00125 * For in-place operation, set pixd == pixs: 00126 * pixGammaTRC(pixs, pixs, ...); 00127 * To get a new image, set pixd == null: 00128 * pixd = pixGammaTRC(NULL, pixs, ...); 00129 * (2) If pixs is colormapped, the colormap is transformed, 00130 * either in-place or in a copy of pixs. 00131 * (3) We use a gamma mapping between minval and maxval. 00132 * (4) If gamma < 1.0, the image will appear darker; 00133 * if gamma > 1.0, the image will appear lighter; 00134 * (5) If gamma = 1.0 and minval = 0 and maxval = 255, no 00135 * enhancement is performed; return a copy unless in-place, 00136 * in which case this is a no-op. 00137 * (6) For color images that are not colormapped, the mapping 00138 * is applied to each component. 00139 * (7) minval and maxval are not restricted to the interval [0, 255]. 00140 * If minval < 0, an input value of 0 is mapped to a 00141 * nonzero output. This will turn black to gray. 00142 * If maxval > 255, an input value of 255 is mapped to 00143 * an output value less than 255. This will turn 00144 * white (e.g., in the background) to gray. 00145 * (8) Increasing minval darkens the image. 00146 * (9) Decreasing maxval bleaches the image. 00147 * (10) Simultaneously increasing minval and decreasing maxval 00148 * will darken the image and make the colors more intense; 00149 * e.g., minval = 50, maxval = 200. 00150 * (11) See numaGammaTRC() for further examples of use. 00151 */ 00152 PIX * 00153 pixGammaTRC(PIX *pixd, 00154 PIX *pixs, 00155 l_float32 gamma, 00156 l_int32 minval, 00157 l_int32 maxval) 00158 { 00159 l_int32 d; 00160 NUMA *nag; 00161 PIXCMAP *cmap; 00162 00163 PROCNAME("pixGammaTRC"); 00164 00165 if (!pixs) 00166 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00167 if (pixd && (pixd != pixs)) 00168 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 00169 if (gamma <= 0.0) { 00170 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00171 gamma = 1.0; 00172 } 00173 if (minval >= maxval) 00174 return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); 00175 cmap = pixGetColormap(pixs); 00176 d = pixGetDepth(pixs); 00177 if (!cmap && d != 8 && d != 32) 00178 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); 00179 00180 if (gamma == 1.0 && minval == 0 && maxval == 255) /* no-op */ 00181 return pixCopy(pixd, pixs); 00182 00183 if (!pixd) /* start with a copy if not in-place */ 00184 pixd = pixCopy(NULL, pixs); 00185 00186 if (cmap) { 00187 pixcmapGammaTRC(pixGetColormap(pixd), gamma, minval, maxval); 00188 return pixd; 00189 } 00190 00191 /* pixd is 8 or 32 bpp */ 00192 if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) 00193 return (PIX *)ERROR_PTR("nag not made", procName, pixd); 00194 pixTRCMap(pixd, NULL, nag); 00195 numaDestroy(&nag); 00196 00197 return pixd; 00198 } 00199 00200 00201 /*! 00202 * pixGammaTRCMasked() 00203 * 00204 * Input: pixd (<optional> null or equal to pixs) 00205 * pixs (8 or 32 bpp; not colormapped) 00206 * pixm (<optional> null or 1 bpp) 00207 * gamma (gamma correction; must be > 0.0) 00208 * minval (input value that gives 0 for output; can be < 0) 00209 * maxval (input value that gives 255 for output; can be > 255) 00210 * Return: pixd always 00211 * 00212 * Notes: 00213 * (1) Same as pixGammaTRC() except mapping is optionally over 00214 * a subset of pixels described by pixm. 00215 * (2) Masking does not work for colormapped images. 00216 * (3) See pixGammaTRC() for details on how to use the parameters. 00217 */ 00218 PIX * 00219 pixGammaTRCMasked(PIX *pixd, 00220 PIX *pixs, 00221 PIX *pixm, 00222 l_float32 gamma, 00223 l_int32 minval, 00224 l_int32 maxval) 00225 { 00226 l_int32 d; 00227 NUMA *nag; 00228 00229 PROCNAME("pixGammaTRCMasked"); 00230 00231 if (!pixm) 00232 return pixGammaTRC(pixd, pixs, gamma, minval, maxval); 00233 00234 if (!pixs) 00235 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00236 if (pixGetColormap(pixs)) 00237 return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd); 00238 if (pixd && (pixd != pixs)) 00239 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 00240 d = pixGetDepth(pixs); 00241 if (d != 8 && d != 32) 00242 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); 00243 if (minval >= maxval) 00244 return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); 00245 if (gamma <= 0.0) { 00246 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00247 gamma = 1.0; 00248 } 00249 00250 if (gamma == 1.0 && minval == 0 && maxval == 255) 00251 return pixCopy(pixd, pixs); 00252 00253 if (!pixd) /* start with a copy if not in-place */ 00254 pixd = pixCopy(NULL, pixs); 00255 00256 if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) 00257 return (PIX *)ERROR_PTR("nag not made", procName, pixd); 00258 pixTRCMap(pixd, pixm, nag); 00259 numaDestroy(&nag); 00260 00261 return pixd; 00262 } 00263 00264 00265 /*! 00266 * pixGammaTRCWithAlpha() 00267 * 00268 * Input: pixd (<optional> null or equal to pixs) 00269 * pixs (32 bpp) 00270 * gamma (gamma correction; must be > 0.0) 00271 * minval (input value that gives 0 for output; can be < 0) 00272 * maxval (input value that gives 255 for output; can be > 255) 00273 * Return: pixd always 00274 * 00275 * Notes: 00276 * (1) See usage notes in pixGammaTRC(). 00277 * (2) This version saves the alpha channel. It is only valid 00278 * for 32 bpp (no colormap), and is a bit slower. 00279 */ 00280 PIX * 00281 pixGammaTRCWithAlpha(PIX *pixd, 00282 PIX *pixs, 00283 l_float32 gamma, 00284 l_int32 minval, 00285 l_int32 maxval) 00286 { 00287 NUMA *nag; 00288 PIX *pixalpha; 00289 00290 PROCNAME("pixGammaTRCWithAlpha"); 00291 00292 if (!pixs || pixGetDepth(pixs) != 32) 00293 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, pixd); 00294 if (pixd && (pixd != pixs)) 00295 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 00296 if (gamma <= 0.0) { 00297 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00298 gamma = 1.0; 00299 } 00300 if (minval >= maxval) 00301 return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); 00302 00303 if (gamma == 1.0 && minval == 0 && maxval == 255) 00304 return pixCopy(pixd, pixs); 00305 00306 pixalpha = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); /* save */ 00307 if (!pixd) /* start with a copy if not in-place */ 00308 pixd = pixCopy(NULL, pixs); 00309 00310 if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) 00311 return (PIX *)ERROR_PTR("nag not made", procName, pixd); 00312 pixTRCMap(pixd, NULL, nag); 00313 pixSetRGBComponent(pixd, pixalpha, L_ALPHA_CHANNEL); /* restore */ 00314 00315 numaDestroy(&nag); 00316 pixDestroy(&pixalpha); 00317 return pixd; 00318 } 00319 00320 00321 /*! 00322 * numaGammaTRC() 00323 * 00324 * Input: gamma (gamma factor; must be > 0.0) 00325 * minval (input value that gives 0 for output) 00326 * maxval (input value that gives 255 for output) 00327 * Return: na, or null on error 00328 * 00329 * Notes: 00330 * (1) The map is returned as a numa; values are clipped to [0, 255]. 00331 * (2) To force all intensities into a range within fraction delta 00332 * of white, use: minval = -256 * (1 - delta) / delta 00333 * maxval = 255 00334 * (3) To force all intensities into a range within fraction delta 00335 * of black, use: minval = 0 00336 * maxval = 256 * (1 - delta) / delta 00337 */ 00338 NUMA * 00339 numaGammaTRC(l_float32 gamma, 00340 l_int32 minval, 00341 l_int32 maxval) 00342 { 00343 l_int32 i, val; 00344 l_float32 x, invgamma; 00345 NUMA *na; 00346 00347 PROCNAME("numaGammaTRC"); 00348 00349 if (minval >= maxval) 00350 return (NUMA *)ERROR_PTR("minval not < maxval", procName, NULL); 00351 if (gamma <= 0.0) { 00352 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00353 gamma = 1.0; 00354 } 00355 00356 invgamma = 1. / gamma; 00357 na = numaCreate(256); 00358 for (i = 0; i < minval; i++) 00359 numaAddNumber(na, 0); 00360 for (i = minval; i <= maxval; i++) { 00361 if (i < 0) continue; 00362 if (i > 255) continue; 00363 x = (l_float32)(i - minval) / (l_float32)(maxval - minval); 00364 val = (l_int32)(255. * powf(x, invgamma) + 0.5); 00365 val = L_MAX(val, 0); 00366 val = L_MIN(val, 255); 00367 numaAddNumber(na, val); 00368 } 00369 for (i = maxval + 1; i < 256; i++) 00370 numaAddNumber(na, 255); 00371 00372 return na; 00373 } 00374 00375 00376 /*-------------------------------------------------------------* 00377 * Contrast enhancement * 00378 *-------------------------------------------------------------*/ 00379 /*! 00380 * pixContrastTRC() 00381 * 00382 * Input: pixd (<optional> null or equal to pixs) 00383 * pixs (8 or 32 bpp; or 2, 4 or 8 bpp with colormap) 00384 * factor (0.0 is no enhancement) 00385 * Return: pixd always 00386 * 00387 * Notes: 00388 * (1) pixd must either be null or equal to pixs. 00389 * For in-place operation, set pixd == pixs: 00390 * pixContrastTRC(pixs, pixs, ...); 00391 * To get a new image, set pixd == null: 00392 * pixd = pixContrastTRC(NULL, pixs, ...); 00393 * (2) If pixs is colormapped, the colormap is transformed, 00394 * either in-place or in a copy of pixs. 00395 * (3) Contrast is enhanced by mapping each color component 00396 * using an atan function with maximum slope at 127. 00397 * Pixels below 127 are lowered in intensity and pixels 00398 * above 127 are increased. 00399 * (4) The useful range for the contrast factor is scaled to 00400 * be in (0.0 to 1.0), but larger values can also be used. 00401 * (5) If factor == 0.0, no enhancement is performed; return a copy 00402 * unless in-place, in which case this is a no-op. 00403 * (6) For color images that are not colormapped, the mapping 00404 * is applied to each component. 00405 */ 00406 PIX * 00407 pixContrastTRC(PIX *pixd, 00408 PIX *pixs, 00409 l_float32 factor) 00410 { 00411 l_int32 d; 00412 NUMA *nac; 00413 PIXCMAP *cmap; 00414 00415 PROCNAME("pixContrastTRC"); 00416 00417 if (!pixs) 00418 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00419 if (pixd && (pixd != pixs)) 00420 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 00421 if (factor < 0.0) { 00422 L_WARNING("factor must be >= 0.0; using 0.0", procName); 00423 factor = 0.0; 00424 } 00425 if (factor == 0.0) 00426 return pixCopy(pixd, pixs); 00427 00428 cmap = pixGetColormap(pixs); 00429 d = pixGetDepth(pixs); 00430 if (!cmap && d != 8 && d != 32) 00431 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); 00432 00433 if (!pixd) /* start with a copy if not in-place */ 00434 pixd = pixCopy(NULL, pixs); 00435 00436 if (cmap) { 00437 pixcmapContrastTRC(pixGetColormap(pixd), factor); 00438 return pixd; 00439 } 00440 00441 /* pixd is 8 or 32 bpp */ 00442 if ((nac = numaContrastTRC(factor)) == NULL) 00443 return (PIX *)ERROR_PTR("nac not made", procName, pixd); 00444 pixTRCMap(pixd, NULL, nac); 00445 numaDestroy(&nac); 00446 00447 return pixd; 00448 } 00449 00450 00451 /*! 00452 * pixContrastTRCMasked() 00453 * 00454 * Input: pixd (<optional> null or equal to pixs) 00455 * pixs (8 or 32 bpp; or 2, 4 or 8 bpp with colormap) 00456 * pixm (<optional> null or 1 bpp) 00457 * factor (0.0 is no enhancement) 00458 * Return: pixd always 00459 * 00460 * Notes: 00461 * (1) Same as pixContrastTRC() except mapping is optionally over 00462 * a subset of pixels described by pixm. 00463 * (2) Masking does not work for colormapped images. 00464 * (3) See pixContrastTRC() for details on how to use the parameters. 00465 */ 00466 PIX * 00467 pixContrastTRCMasked(PIX *pixd, 00468 PIX *pixs, 00469 PIX *pixm, 00470 l_float32 factor) 00471 { 00472 l_int32 d; 00473 NUMA *nac; 00474 00475 PROCNAME("pixContrastTRCMasked"); 00476 00477 if (!pixm) 00478 return pixContrastTRC(pixd, pixs, factor); 00479 00480 if (!pixs) 00481 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00482 if (pixGetColormap(pixs)) 00483 return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd); 00484 if (pixd && (pixd != pixs)) 00485 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 00486 d = pixGetDepth(pixs); 00487 if (d != 8 && d != 32) 00488 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); 00489 00490 if (factor < 0.0) { 00491 L_WARNING("factor must be >= 0.0; using 0.0", procName); 00492 factor = 0.0; 00493 } 00494 if (factor == 0.0) 00495 return pixCopy(pixd, pixs); 00496 00497 if (!pixd) /* start with a copy if not in-place */ 00498 pixd = pixCopy(NULL, pixs); 00499 00500 if ((nac = numaContrastTRC(factor)) == NULL) 00501 return (PIX *)ERROR_PTR("nac not made", procName, pixd); 00502 pixTRCMap(pixd, pixm, nac); 00503 numaDestroy(&nac); 00504 00505 return pixd; 00506 } 00507 00508 00509 /*! 00510 * numaContrastTRC() 00511 * 00512 * Input: factor (generally between 0.0 (no enhancement) 00513 * and 1.0, but can be larger than 1.0) 00514 * Return: na, or null on error 00515 * 00516 * Notes: 00517 * (1) The mapping is monotonic increasing, where 0 is mapped 00518 * to 0 and 255 is mapped to 255. 00519 * (2) As 'factor' is increased from 0.0 (where the mapping is linear), 00520 * the map gets closer to its limit as a step function that 00521 * jumps from 0 to 255 at the center (input value = 127). 00522 */ 00523 NUMA * 00524 numaContrastTRC(l_float32 factor) 00525 { 00526 l_int32 i, val; 00527 l_float64 x, ymax, ymin, dely, scale; 00528 NUMA *na; 00529 00530 PROCNAME("numaContrastTRC"); 00531 00532 if (factor < 0.0) { 00533 L_WARNING("factor must be >= 0.0; using 0.0; no enhancement", procName); 00534 factor = 0.0; 00535 } 00536 if (factor == 0.0) 00537 return numaMakeSequence(0, 1, 256); /* linear map */ 00538 00539 scale = ENHANCE_SCALE_FACTOR; 00540 ymax = atan((l_float64)(1.0 * factor * scale)); 00541 ymin = atan((l_float64)(-127. * factor * scale / 128.)); 00542 dely = ymax - ymin; 00543 na = numaCreate(256); 00544 for (i = 0; i < 256; i++) { 00545 x = (l_float64)i; 00546 val = (l_int32)((255. / dely) * 00547 (-ymin + atan((l_float64)(factor * scale * (x - 127.) / 128.))) + 00548 0.5); 00549 numaAddNumber(na, val); 00550 } 00551 00552 return na; 00553 } 00554 00555 00556 /*-------------------------------------------------------------* 00557 * Histogram equalization * 00558 *-------------------------------------------------------------*/ 00559 /*! 00560 * pixEqualizeTRC() 00561 * 00562 * Input: pixd (<optional> null or equal to pixs) 00563 * pixs (8 bpp gray, 32 bpp rgb, or colormapped) 00564 * fract (fraction of equalization movement of pixel values) 00565 * factor (subsampling factor; integer >= 1) 00566 * Return: pixd, or null on error 00567 * 00568 * Notes: 00569 * (1) pixd must either be null or equal to pixs. 00570 * For in-place operation, set pixd == pixs: 00571 * pixEqualizeTRC(pixs, pixs, ...); 00572 * To get a new image, set pixd == null: 00573 * pixd = pixEqualizeTRC(NULL, pixs, ...); 00574 * (2) In histogram equalization, a tone reproduction curve 00575 * mapping is used to make the number of pixels at each 00576 * intensity equal. 00577 * (3) If fract == 0.0, no equalization is performed; return a copy 00578 * unless in-place, in which case this is a no-op. 00579 * If fract == 1.0, equalization is complete. 00580 * (4) Set the subsampling factor > 1 to reduce the amount of computation. 00581 * (5) If pixs is colormapped, the colormap is removed and 00582 * converted to rgb or grayscale. 00583 * (6) If pixs has color, equalization is done in each channel 00584 * separately. 00585 * (7) Note that even if there is a colormap, we can get an 00586 * in-place operation because the intermediate image pixt 00587 * is copied back to pixs (which for in-place is the same 00588 * as pixd). 00589 */ 00590 PIX * 00591 pixEqualizeTRC(PIX *pixd, 00592 PIX *pixs, 00593 l_float32 fract, 00594 l_int32 factor) 00595 { 00596 l_int32 d; 00597 NUMA *na; 00598 PIX *pixt, *pix8; 00599 PIXCMAP *cmap; 00600 00601 PROCNAME("pixEqualizeTRC"); 00602 00603 if (!pixs) 00604 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00605 if (pixd && (pixd != pixs)) 00606 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 00607 cmap = pixGetColormap(pixs); 00608 d = pixGetDepth(pixs); 00609 if (d != 8 && d != 32 && !cmap) 00610 return (PIX *)ERROR_PTR("pixs not 8/32 bpp or cmapped", procName, NULL); 00611 if (fract < 0.0 || fract > 1.0) 00612 return (PIX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); 00613 if (factor < 1) 00614 return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL); 00615 00616 if (fract == 0.0) 00617 return pixCopy(pixd, pixs); 00618 00619 /* If there is a colormap, remove it. */ 00620 if (cmap) 00621 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 00622 else 00623 pixt = pixClone(pixs); 00624 00625 /* Make a copy if necessary */ 00626 pixd = pixCopy(pixd, pixt); 00627 pixDestroy(&pixt); 00628 00629 d = pixGetDepth(pixd); 00630 if (d == 8) { 00631 na = numaEqualizeTRC(pixd, fract, factor); 00632 pixTRCMap(pixd, NULL, na); 00633 numaDestroy(&na); 00634 } 00635 else { /* 32 bpp */ 00636 pix8 = pixGetRGBComponent(pixd, COLOR_RED); 00637 na = numaEqualizeTRC(pix8, fract, factor); 00638 pixTRCMap(pix8, NULL, na); 00639 pixSetRGBComponent(pixd, pix8, COLOR_RED); 00640 numaDestroy(&na); 00641 pixDestroy(&pix8); 00642 pix8 = pixGetRGBComponent(pixd, COLOR_GREEN); 00643 na = numaEqualizeTRC(pix8, fract, factor); 00644 pixTRCMap(pix8, NULL, na); 00645 pixSetRGBComponent(pixd, pix8, COLOR_GREEN); 00646 numaDestroy(&na); 00647 pixDestroy(&pix8); 00648 pix8 = pixGetRGBComponent(pixd, COLOR_BLUE); 00649 na = numaEqualizeTRC(pix8, fract, factor); 00650 pixTRCMap(pix8, NULL, na); 00651 pixSetRGBComponent(pixd, pix8, COLOR_BLUE); 00652 numaDestroy(&na); 00653 pixDestroy(&pix8); 00654 } 00655 00656 return pixd; 00657 } 00658 00659 00660 /*! 00661 * numaEqualizeTRC() 00662 * 00663 * Input: pix (8 bpp, no colormap) 00664 * fract (fraction of equalization movement of pixel values) 00665 * factor (subsampling factor; integer >= 1) 00666 * Return: nad, or null on error 00667 * 00668 * Notes: 00669 * (1) If fract == 0.0, no equalization will be performed. 00670 * If fract == 1.0, equalization is complete. 00671 * (2) Set the subsampling factor > 1 to reduce the amount of computation. 00672 * (3) The map is returned as a numa with 256 values, specifying 00673 * the equalized value (array value) for every input value 00674 * (the array index). 00675 */ 00676 NUMA * 00677 numaEqualizeTRC(PIX *pix, 00678 l_float32 fract, 00679 l_int32 factor) 00680 { 00681 l_int32 iin, iout, itarg; 00682 l_float32 val, sum; 00683 NUMA *nah, *nasum, *nad; 00684 00685 PROCNAME("numaEqualizeTRC"); 00686 00687 if (!pix) 00688 return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); 00689 if (pixGetDepth(pix) != 8) 00690 return (NUMA *)ERROR_PTR("pix not 8 bpp", procName, NULL); 00691 if (fract < 0.0 || fract > 1.0) 00692 return (NUMA *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); 00693 if (factor < 1) 00694 return (NUMA *)ERROR_PTR("sampling factor < 1", procName, NULL); 00695 00696 if (fract == 0.0) 00697 L_WARNING("fract = 0.0; no equalization requested", procName); 00698 00699 if ((nah = pixGetGrayHistogram(pix, factor)) == NULL) 00700 return (NUMA *)ERROR_PTR("histogram not made", procName, NULL); 00701 numaGetSum(nah, &sum); 00702 nasum = numaGetPartialSums(nah); 00703 00704 nad = numaCreate(256); 00705 for (iin = 0; iin < 256; iin++) { 00706 numaGetFValue(nasum, iin, &val); 00707 itarg = (l_int32)(255. * val / sum + 0.5); 00708 iout = iin + (l_int32)(fract * (itarg - iin)); 00709 iout = L_MIN(iout, 255); /* to be safe */ 00710 numaAddNumber(nad, iout); 00711 } 00712 00713 numaDestroy(&nah); 00714 numaDestroy(&nasum); 00715 return nad; 00716 } 00717 00718 00719 /*-------------------------------------------------------------* 00720 * Generic TRC mapping * 00721 *-------------------------------------------------------------*/ 00722 /*! 00723 * pixTRCMap() 00724 * 00725 * Input: pixs (8 grayscale or 32 bpp rgb; not colormapped) 00726 * pixm (<optional> 1 bpp mask) 00727 * na (mapping array) 00728 * Return: pixd, or null on error 00729 * 00730 * Notes: 00731 * (1) This operation is in-place on pixs. 00732 * (2) For 32 bpp, this applies the same map to each of the r,g,b 00733 * components. 00734 * (3) The mapping array is of size 256, and it maps the input 00735 * index into values in the range [0, 255]. 00736 * (4) If defined, the optional 1 bpp mask pixm has its origin 00737 * aligned with pixs, and the map function is applied only 00738 * to pixels in pixs under the fg of pixm. 00739 * (5) For 32 bpp, this does not save the alpha channel. 00740 */ 00741 l_int32 00742 pixTRCMap(PIX *pixs, 00743 PIX *pixm, 00744 NUMA *na) 00745 { 00746 l_int32 w, h, d, wm, hm, wpl, wplm, i, j, sval8, dval8; 00747 l_int32 *tab; 00748 l_uint32 sval32, dval32; 00749 l_uint32 *data, *datam, *line, *linem; 00750 00751 PROCNAME("pixTRCMap"); 00752 00753 if (!pixs) 00754 return ERROR_INT("pixs not defined", procName, 1); 00755 if (pixGetColormap(pixs)) 00756 return ERROR_INT("pixs is colormapped", procName, 1); 00757 if (!na) 00758 return ERROR_INT("na not defined", procName, 1); 00759 if (numaGetCount(na) != 256) 00760 return ERROR_INT("na not of size 256", procName, 1); 00761 pixGetDimensions(pixs, &w, &h, &d); 00762 if (d != 8 && d != 32) 00763 return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); 00764 if (pixm) { 00765 if (pixGetDepth(pixm) != 1) 00766 return ERROR_INT("pixm not 1 bpp", procName, 1); 00767 } 00768 00769 tab = numaGetIArray(na); /* get the array for efficiency */ 00770 wpl = pixGetWpl(pixs); 00771 data = pixGetData(pixs); 00772 if (!pixm) { 00773 if (d == 8) { 00774 for (i = 0; i < h; i++) { 00775 line = data + i * wpl; 00776 for (j = 0; j < w; j++) { 00777 sval8 = GET_DATA_BYTE(line, j); 00778 dval8 = tab[sval8]; 00779 SET_DATA_BYTE(line, j, dval8); 00780 } 00781 } 00782 } 00783 else { /* d == 32 */ 00784 for (i = 0; i < h; i++) { 00785 line = data + i * wpl; 00786 for (j = 0; j < w; j++) { 00787 sval32 = *(line + j); 00788 dval32 = 00789 tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | 00790 tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | 00791 tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; 00792 *(line + j) = dval32; 00793 } 00794 } 00795 } 00796 } 00797 else { 00798 datam = pixGetData(pixm); 00799 wplm = pixGetWpl(pixm); 00800 pixGetDimensions(pixm, &wm, &hm, NULL); 00801 if (d == 8) { 00802 for (i = 0; i < h; i++) { 00803 if (i >= hm) 00804 break; 00805 line = data + i * wpl; 00806 linem = datam + i * wplm; 00807 for (j = 0; j < w; j++) { 00808 if (j >= wm) 00809 break; 00810 if (GET_DATA_BIT(linem, j) == 0) 00811 continue; 00812 sval8 = GET_DATA_BYTE(line, j); 00813 dval8 = tab[sval8]; 00814 SET_DATA_BYTE(line, j, dval8); 00815 } 00816 } 00817 } 00818 else { /* d == 32 */ 00819 for (i = 0; i < h; i++) { 00820 if (i >= hm) 00821 break; 00822 line = data + i * wpl; 00823 linem = datam + i * wplm; 00824 for (j = 0; j < w; j++) { 00825 if (j >= wm) 00826 break; 00827 if (GET_DATA_BIT(linem, j) == 0) 00828 continue; 00829 sval32 = *(line + j); 00830 dval32 = 00831 tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | 00832 tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | 00833 tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; 00834 *(line + j) = dval32; 00835 } 00836 } 00837 } 00838 } 00839 00840 FREE(tab); 00841 return 0; 00842 } 00843 00844 00845 00846 /*-----------------------------------------------------------------------* 00847 * Unsharp masking * 00848 *-----------------------------------------------------------------------*/ 00849 /*! 00850 * pixUnsharpMasking() 00851 * 00852 * Input: pixs (all depths except 1 bpp; with or without colormaps) 00853 * halfwidth ("half-width" of smoothing filter) 00854 * fract (fraction of edge added back into image) 00855 * Return: pixd, or null on error 00856 * 00857 * Notes: 00858 * (1) We use symmetric smoothing filters of odd dimension, 00859 * typically use sizes of 3, 5, 7, etc. The @halfwidth parameter 00860 * for these is (size - 1)/2; i.e., 1, 2, 3, etc. 00861 * (2) The fract parameter is typically taken in the 00862 * range: 0.2 < fract < 0.7 00863 * (3) Returns a clone if no sharpening is requested. 00864 */ 00865 PIX * 00866 pixUnsharpMasking(PIX *pixs, 00867 l_int32 halfwidth, 00868 l_float32 fract) 00869 { 00870 l_int32 d; 00871 PIX *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs; 00872 00873 PROCNAME("pixUnsharpMasking"); 00874 00875 if (!pixs || (pixGetDepth(pixs) == 1)) 00876 return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); 00877 if (fract <= 0.0 || halfwidth <= 0) { 00878 L_WARNING("no sharpening requested; clone returned", procName); 00879 return pixClone(pixs); 00880 } 00881 00882 if (halfwidth == 1 || halfwidth == 2) 00883 return pixUnsharpMaskingFast(pixs, halfwidth, fract, L_BOTH_DIRECTIONS); 00884 00885 /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ 00886 if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL) 00887 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00888 00889 /* Sharpen */ 00890 d = pixGetDepth(pixt); 00891 if (d == 8) 00892 pixd = pixUnsharpMaskingGray(pixt, halfwidth, fract); 00893 else { /* d == 32 */ 00894 pixr = pixGetRGBComponent(pixs, COLOR_RED); 00895 pixrs = pixUnsharpMaskingGray(pixr, halfwidth, fract); 00896 pixDestroy(&pixr); 00897 pixg = pixGetRGBComponent(pixs, COLOR_GREEN); 00898 pixgs = pixUnsharpMaskingGray(pixg, halfwidth, fract); 00899 pixDestroy(&pixg); 00900 pixb = pixGetRGBComponent(pixs, COLOR_BLUE); 00901 pixbs = pixUnsharpMaskingGray(pixb, halfwidth, fract); 00902 pixDestroy(&pixb); 00903 pixd = pixCreateRGBImage(pixrs, pixgs, pixbs); 00904 pixDestroy(&pixrs); 00905 pixDestroy(&pixgs); 00906 pixDestroy(&pixbs); 00907 } 00908 00909 pixDestroy(&pixt); 00910 return pixd; 00911 } 00912 00913 00914 /*! 00915 * pixUnsharpMaskingGray() 00916 * 00917 * Input: pixs (8 bpp; no colormap) 00918 * halfwidth ("half-width" of smoothing filter) 00919 * fract (fraction of edge added back into image) 00920 * Return: pixd, or null on error 00921 * 00922 * Notes: 00923 * (1) We use symmetric smoothing filters of odd dimension, 00924 * typically use sizes of 3, 5, 7, etc. The @halfwidth parameter 00925 * for these is (size - 1)/2; i.e., 1, 2, 3, etc. 00926 * (2) The fract parameter is typically taken in the range: 00927 * 0.2 < fract < 0.7 00928 * (3) Returns a clone if no sharpening is requested. 00929 */ 00930 PIX * 00931 pixUnsharpMaskingGray(PIX *pixs, 00932 l_int32 halfwidth, 00933 l_float32 fract) 00934 { 00935 l_int32 w, h, d; 00936 PIX *pixc, *pixd; 00937 PIXACC *pixacc; 00938 00939 PROCNAME("pixUnsharpMaskingGray"); 00940 00941 if (!pixs) 00942 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00943 pixGetDimensions(pixs, &w, &h, &d); 00944 if (d != 8 || pixGetColormap(pixs) != NULL) 00945 return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); 00946 if (fract <= 0.0 || halfwidth <= 0) { 00947 L_WARNING("no sharpening requested; clone returned", procName); 00948 return pixClone(pixs); 00949 } 00950 if (halfwidth == 1 || halfwidth == 2) 00951 return pixUnsharpMaskingGrayFast(pixs, halfwidth, fract, 00952 L_BOTH_DIRECTIONS); 00953 00954 if ((pixc = pixBlockconvGray(pixs, NULL, halfwidth, halfwidth)) == NULL) 00955 return (PIX *)ERROR_PTR("pixc not made", procName, NULL); 00956 00957 /* Steps: 00958 * (1) edge image is pixs - pixc (this is highpass part) 00959 * (2) multiply edge image by fract 00960 * (3) add fraction of edge to pixs 00961 * 00962 * To show how this is done with both interfaces to arithmetic 00963 * on integer Pix, here is the implementation in the lower-level 00964 * function calls: 00965 * pixt = pixInitAccumulate(w, h, 0x10000000)) == NULL) 00966 * pixAccumulate(pixt, pixs, L_ARITH_ADD); 00967 * pixAccumulate(pixt, pixc, L_ARITH_SUBTRACT); 00968 * pixMultConstAccumulate(pixt, fract, 0x10000000); 00969 * pixAccumulate(pixt, pixs, L_ARITH_ADD); 00970 * pixd = pixFinalAccumulate(pixt, 0x10000000, 8)) == NULL) 00971 * pixDestroy(&pixt); 00972 * 00973 * The code below does the same thing using the Pixacc accumulator, 00974 * hiding the details of the offset that is needed for subtraction. 00975 */ 00976 pixacc = pixaccCreate(w, h, 1); 00977 pixaccAdd(pixacc, pixs); 00978 pixaccSubtract(pixacc, pixc); 00979 pixaccMultConst(pixacc, fract); 00980 pixaccAdd(pixacc, pixs); 00981 pixd = pixaccFinal(pixacc, 8); 00982 pixaccDestroy(&pixacc); 00983 00984 pixDestroy(&pixc); 00985 return pixd; 00986 } 00987 00988 00989 /*! 00990 * pixUnsharpMaskingFast() 00991 * 00992 * Input: pixs (all depths except 1 bpp; with or without colormaps) 00993 * halfwidth ("half-width" of smoothing filter; 1 and 2 only) 00994 * fract (fraction of high frequency added to image) 00995 * direction (L_HORIZ, L_VERT, L_BOTH_DIRECTIONS) 00996 * Return: pixd, or null on error 00997 * 00998 * Notes: 00999 * (1) The fast version uses separable 1-D filters directly on 01000 * the input image. The halfwidth is either 1 (full width = 3) 01001 * or 2 (full width = 5). 01002 * (2) The fract parameter is typically taken in the 01003 * range: 0.2 < fract < 0.7 01004 * (3) To skip horizontal sharpening, use @fracth = 0.0; ditto for @fractv 01005 * (4) For one dimensional filtering (as an example): 01006 * For @halfwidth = 1, the low-pass filter is 01007 * L: 1/3 1/3 1/3 01008 * and the high-pass filter is 01009 * H = I - L: -1/3 2/3 -1/3 01010 * For @halfwidth = 2, the low-pass filter is 01011 * L: 1/5 1/5 1/5 1/5 1/5 01012 * and the high-pass filter is 01013 * H = I - L: -1/5 -1/5 4/5 -1/5 -1/5 01014 * The new sharpened pixel value is found by adding some fraction 01015 * of the high-pass filter value (which sums to 0) to the 01016 * initial pixel value: 01017 * N = I + fract * H 01018 * (5) For 2D, the sharpening filter is not separable, because the 01019 * vertical filter depends on the horizontal location relative 01020 * to the filter origin, and v.v. So we either do the full 01021 * 2D filter (for @halfwidth == 1) or do the low-pass 01022 * convolution separably and then compose with the original pix. 01023 * (6) Returns a clone if no sharpening is requested. 01024 */ 01025 PIX * 01026 pixUnsharpMaskingFast(PIX *pixs, 01027 l_int32 halfwidth, 01028 l_float32 fract, 01029 l_int32 direction) 01030 { 01031 l_int32 d; 01032 PIX *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs; 01033 01034 PROCNAME("pixUnsharpMaskingFast"); 01035 01036 if (!pixs || (pixGetDepth(pixs) == 1)) 01037 return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); 01038 if (fract <= 0.0 || halfwidth <= 0) { 01039 L_WARNING("no sharpening requested; clone returned", procName); 01040 return pixClone(pixs); 01041 } 01042 if (halfwidth != 1 && halfwidth != 2) 01043 return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); 01044 if (direction != L_HORIZ && direction != L_VERT && 01045 direction != L_BOTH_DIRECTIONS) 01046 return (PIX *)ERROR_PTR("invalid direction", procName, NULL); 01047 01048 /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ 01049 if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL) 01050 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 01051 01052 /* Sharpen */ 01053 d = pixGetDepth(pixt); 01054 if (d == 8) 01055 pixd = pixUnsharpMaskingGrayFast(pixt, halfwidth, fract, direction); 01056 else { /* d == 32 */ 01057 pixr = pixGetRGBComponent(pixs, COLOR_RED); 01058 pixrs = pixUnsharpMaskingGrayFast(pixr, halfwidth, fract, direction); 01059 pixDestroy(&pixr); 01060 pixg = pixGetRGBComponent(pixs, COLOR_GREEN); 01061 pixgs = pixUnsharpMaskingGrayFast(pixg, halfwidth, fract, direction); 01062 pixDestroy(&pixg); 01063 pixb = pixGetRGBComponent(pixs, COLOR_BLUE); 01064 pixbs = pixUnsharpMaskingGrayFast(pixb, halfwidth, fract, direction); 01065 pixDestroy(&pixb); 01066 pixd = pixCreateRGBImage(pixrs, pixgs, pixbs); 01067 pixDestroy(&pixrs); 01068 pixDestroy(&pixgs); 01069 pixDestroy(&pixbs); 01070 } 01071 01072 pixDestroy(&pixt); 01073 return pixd; 01074 } 01075 01076 01077 01078 /*! 01079 * pixUnsharpMaskingGrayFast() 01080 * 01081 * Input: pixs (8 bpp; no colormap) 01082 * halfwidth ("half-width" of smoothing filter: 1 or 2) 01083 * fract (fraction of high frequency added to image) 01084 * direction (L_HORIZ, L_VERT, L_BOTH_DIRECTIONS) 01085 * Return: pixd, or null on error 01086 * 01087 * Notes: 01088 * (1) For usage and explanation of the algorithm, see notes 01089 * in pixUnsharpMaskingFast(). 01090 * (2) Returns a clone if no sharpening is requested. 01091 */ 01092 PIX * 01093 pixUnsharpMaskingGrayFast(PIX *pixs, 01094 l_int32 halfwidth, 01095 l_float32 fract, 01096 l_int32 direction) 01097 { 01098 PIX *pixd; 01099 01100 PROCNAME("pixUnsharpMaskingGrayFast"); 01101 01102 if (!pixs) 01103 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01104 if (pixGetDepth(pixs) != 8 || pixGetColormap(pixs) != NULL) 01105 return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); 01106 if (fract <= 0.0 || halfwidth <= 0) { 01107 L_WARNING("no sharpening requested; clone returned", procName); 01108 return pixClone(pixs); 01109 } 01110 if (halfwidth != 1 && halfwidth != 2) 01111 return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); 01112 if (direction != L_HORIZ && direction != L_VERT && 01113 direction != L_BOTH_DIRECTIONS) 01114 return (PIX *)ERROR_PTR("invalid direction", procName, NULL); 01115 01116 if (direction != L_BOTH_DIRECTIONS) 01117 pixd = pixUnsharpMaskingGray1D(pixs, halfwidth, fract, direction); 01118 else /* 2D sharpening */ 01119 pixd = pixUnsharpMaskingGray2D(pixs, halfwidth, fract); 01120 01121 return pixd; 01122 } 01123 01124 01125 /*! 01126 * pixUnsharpMaskingGray1D() 01127 * 01128 * Input: pixs (8 bpp; no colormap) 01129 * halfwidth ("half-width" of smoothing filter: 1 or 2) 01130 * fract (fraction of high frequency added to image) 01131 * direction (of filtering; use L_HORIZ or L_VERT) 01132 * Return: pixd, or null on error 01133 * 01134 * Notes: 01135 * (1) For usage and explanation of the algorithm, see notes 01136 * in pixUnsharpMaskingFast(). 01137 * (2) Returns a clone if no sharpening is requested. 01138 */ 01139 PIX * 01140 pixUnsharpMaskingGray1D(PIX *pixs, 01141 l_int32 halfwidth, 01142 l_float32 fract, 01143 l_int32 direction) 01144 { 01145 l_int32 w, h, d, wpls, wpld, i, j, ival; 01146 l_uint32 *datas, *datad; 01147 l_uint32 *lines, *lines0, *lines1, *lines2, *lines3, *lines4, *lined; 01148 l_float32 val, a[5]; 01149 PIX *pixd; 01150 01151 PROCNAME("pixUnsharpMaskingGray1D"); 01152 01153 if (!pixs) 01154 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01155 pixGetDimensions(pixs, &w, &h, &d); 01156 if (d != 8 || pixGetColormap(pixs) != NULL) 01157 return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); 01158 if (fract <= 0.0 || halfwidth <= 0) { 01159 L_WARNING("no sharpening requested; clone returned", procName); 01160 return pixClone(pixs); 01161 } 01162 if (halfwidth != 1 && halfwidth != 2) 01163 return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); 01164 01165 /* Initialize pixd with pixels from pixs that will not be 01166 * set when computing the sharpened values. */ 01167 pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth, 01168 halfwidth, halfwidth); 01169 datas = pixGetData(pixs); 01170 datad = pixGetData(pixd); 01171 wpls = pixGetWpl(pixs); 01172 wpld = pixGetWpl(pixd); 01173 01174 if (halfwidth == 1) { 01175 a[0] = -fract / 3.0; 01176 a[1] = 1.0 + fract * 2.0 / 3.0; 01177 a[2] = a[0]; 01178 } 01179 else { /* halfwidth == 2 */ 01180 a[0] = -fract / 5.0; 01181 a[1] = a[0]; 01182 a[2] = 1.0 + fract * 4.0 / 5.0; 01183 a[3] = a[0]; 01184 a[4] = a[0]; 01185 } 01186 01187 if (direction == L_HORIZ) { 01188 for (i = 0; i < h; i++) { 01189 lines = datas + i * wpls; 01190 lined = datad + i * wpld; 01191 if (halfwidth == 1) { 01192 for (j = 1; j < w - 1; j++) { 01193 val = a[0] * GET_DATA_BYTE(lines, j - 1) + 01194 a[1] * GET_DATA_BYTE(lines, j) + 01195 a[2] * GET_DATA_BYTE(lines, j + 1); 01196 ival = (l_int32)val; 01197 ival = L_MAX(0, ival); 01198 ival = L_MIN(255, ival); 01199 SET_DATA_BYTE(lined, j, ival); 01200 } 01201 } 01202 else { /* halfwidth == 2 */ 01203 for (j = 2; j < w - 2; j++) { 01204 val = a[0] * GET_DATA_BYTE(lines, j - 2) + 01205 a[1] * GET_DATA_BYTE(lines, j - 1) + 01206 a[2] * GET_DATA_BYTE(lines, j) + 01207 a[3] * GET_DATA_BYTE(lines, j + 1) + 01208 a[4] * GET_DATA_BYTE(lines, j + 2); 01209 ival = (l_int32)val; 01210 ival = L_MAX(0, ival); 01211 ival = L_MIN(255, ival); 01212 SET_DATA_BYTE(lined, j, ival); 01213 } 01214 } 01215 } 01216 } 01217 else { /* direction == L_VERT */ 01218 if (halfwidth == 1) { 01219 for (i = 1; i < h - 1; i++) { 01220 lines0 = datas + (i - 1) * wpls; 01221 lines1 = datas + i * wpls; 01222 lines2 = datas + (i + 1) * wpls; 01223 lined = datad + i * wpld; 01224 for (j = 0; j < w; j++) { 01225 val = a[0] * GET_DATA_BYTE(lines0, j) + 01226 a[1] * GET_DATA_BYTE(lines1, j) + 01227 a[2] * GET_DATA_BYTE(lines2, j); 01228 ival = (l_int32)val; 01229 ival = L_MAX(0, ival); 01230 ival = L_MIN(255, ival); 01231 SET_DATA_BYTE(lined, j, ival); 01232 } 01233 } 01234 } 01235 else { /* halfwidth == 2 */ 01236 for (i = 2; i < h - 2; i++) { 01237 lines0 = datas + (i - 2) * wpls; 01238 lines1 = datas + (i - 1) * wpls; 01239 lines2 = datas + i * wpls; 01240 lines3 = datas + (i + 1) * wpls; 01241 lines4 = datas + (i + 2) * wpls; 01242 lined = datad + i * wpld; 01243 for (j = 0; j < w; j++) { 01244 val = a[0] * GET_DATA_BYTE(lines0, j) + 01245 a[1] * GET_DATA_BYTE(lines1, j) + 01246 a[2] * GET_DATA_BYTE(lines2, j) + 01247 a[3] * GET_DATA_BYTE(lines3, j) + 01248 a[4] * GET_DATA_BYTE(lines4, j); 01249 ival = (l_int32)val; 01250 ival = L_MAX(0, ival); 01251 ival = L_MIN(255, ival); 01252 SET_DATA_BYTE(lined, j, ival); 01253 } 01254 } 01255 } 01256 } 01257 01258 return pixd; 01259 } 01260 01261 01262 /*! 01263 * pixUnsharpMaskingGray2D() 01264 * 01265 * Input: pixs (8 bpp; no colormap) 01266 * halfwidth ("half-width" of smoothing filter: 1 or 2) 01267 * fract (fraction of high frequency added to image) 01268 * Return: pixd, or null on error 01269 * 01270 * Notes: 01271 * (1) For halfwidth == 1, we implement the full sharpening filter 01272 * directly. For halfwidth == 2, we implement the the lowpass 01273 * filter separably and then compute the sharpening result locally. 01274 * (2) Returns a clone if no sharpening is requested. 01275 */ 01276 PIX * 01277 pixUnsharpMaskingGray2D(PIX *pixs, 01278 l_int32 halfwidth, 01279 l_float32 fract) 01280 { 01281 l_int32 w, h, d, wpls, wpld, wplf, i, j, ival, sval; 01282 l_uint32 *datas, *datad, *lines, *lines0, *lines1, *lines2, *lined; 01283 l_float32 val, a[9]; 01284 l_float32 *dataf, *linef, *linef0, *linef1, *linef2, *linef3, *linef4; 01285 PIX *pixd; 01286 FPIX *fpix; 01287 01288 PROCNAME("pixUnsharpMaskingGray2D"); 01289 01290 if (!pixs) 01291 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01292 pixGetDimensions(pixs, &w, &h, &d); 01293 if (d != 8 || pixGetColormap(pixs) != NULL) 01294 return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); 01295 if (fract <= 0.0 || halfwidth <= 0) { 01296 L_WARNING("no sharpening requested; clone returned", procName); 01297 return pixClone(pixs); 01298 } 01299 if (halfwidth != 1 && halfwidth != 2) 01300 return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); 01301 01302 pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth, 01303 halfwidth, halfwidth); 01304 datad = pixGetData(pixd); 01305 wpld = pixGetWpl(pixd); 01306 datas = pixGetData(pixs); 01307 wpls = pixGetWpl(pixs); 01308 01309 if (halfwidth == 1) { 01310 for (i = 0; i < 9; i++) 01311 a[i] = -fract / 9.0; 01312 a[4] = 1.0 + fract * 8.0 / 9.0; 01313 for (i = 1; i < h - 1; i++) { 01314 lines0 = datas + (i - 1) * wpls; 01315 lines1 = datas + i * wpls; 01316 lines2 = datas + (i + 1) * wpls; 01317 lined = datad + i * wpld; 01318 for (j = 1; j < w - 1; j++) { 01319 val = a[0] * GET_DATA_BYTE(lines0, j - 1) + 01320 a[1] * GET_DATA_BYTE(lines0, j) + 01321 a[2] * GET_DATA_BYTE(lines0, j + 1) + 01322 a[3] * GET_DATA_BYTE(lines1, j - 1) + 01323 a[4] * GET_DATA_BYTE(lines1, j) + 01324 a[5] * GET_DATA_BYTE(lines1, j + 1) + 01325 a[6] * GET_DATA_BYTE(lines2, j - 1) + 01326 a[7] * GET_DATA_BYTE(lines2, j) + 01327 a[8] * GET_DATA_BYTE(lines2, j + 1); 01328 ival = (l_int32)(val + 0.5); 01329 ival = L_MAX(0, ival); 01330 ival = L_MIN(255, ival); 01331 SET_DATA_BYTE(lined, j, ival); 01332 } 01333 } 01334 01335 return pixd; 01336 } 01337 01338 /* For halfwidth == 2, do the low pass separably. Store 01339 * the result of horizontal smoothing in an intermediate fpix. */ 01340 fpix = fpixCreate(w, h); 01341 dataf = fpixGetData(fpix); 01342 wplf = fpixGetWpl(fpix); 01343 for (i = 2; i < h - 2; i++) { 01344 lines = datas + i * wpls; 01345 linef = dataf + i * wplf; 01346 for (j = 2; j < w - 2; j++) { 01347 val = GET_DATA_BYTE(lines, j - 2) + 01348 GET_DATA_BYTE(lines, j - 1) + 01349 GET_DATA_BYTE(lines, j) + 01350 GET_DATA_BYTE(lines, j + 1) + 01351 GET_DATA_BYTE(lines, j + 2); 01352 linef[j] = val; 01353 } 01354 } 01355 01356 /* Do vertical smoothing to finish the low-pass filter. 01357 * At each pixel, if L is the lowpass value, I is the 01358 * src pixel value and f is the fraction of highpass to 01359 * be added to I, then the highpass filter value is 01360 * H = I - L 01361 * and the new sharpened value is 01362 * N = I + f * H. 01363 */ 01364 for (i = 2; i < h - 2; i++) { 01365 linef0 = dataf + (i - 2) * wplf; 01366 linef1 = dataf + (i - 1) * wplf; 01367 linef2 = dataf + i * wplf; 01368 linef3 = dataf + (i + 1) * wplf; 01369 linef4 = dataf + (i + 2) * wplf; 01370 lined = datad + i * wpld; 01371 lines = datas + i * wpls; 01372 for (j = 2; j < w - 2; j++) { 01373 val = 0.04 * (linef0[j] + linef1[j] + linef2[j] + 01374 linef3[j] + linef4[j]); /* L: lowpass filter value */ 01375 sval = GET_DATA_BYTE(lines, j); /* I: source pixel */ 01376 ival = (l_int32)(sval + fract * (sval - val) + 0.5); 01377 ival = L_MAX(0, ival); 01378 ival = L_MIN(255, ival); 01379 SET_DATA_BYTE(lined, j, ival); 01380 } 01381 } 01382 01383 fpixDestroy(&fpix); 01384 return pixd; 01385 } 01386 01387 01388 01389 /*-----------------------------------------------------------------------* 01390 * Hue and saturation modification * 01391 *-----------------------------------------------------------------------*/ 01392 /*! 01393 * pixModifyHue() 01394 * 01395 * Input: pixd (<optional> can be null or equal to pixs) 01396 * pixs (32 bpp rgb) 01397 * fract (between -1.0 and 1.0) 01398 * Return: pixd, or null on error 01399 * 01400 * Notes: 01401 * (1) pixd must either be null or equal to pixs. 01402 * For in-place operation, set pixd == pixs: 01403 * pixEqualizeTRC(pixs, pixs, ...); 01404 * To get a new image, set pixd == null: 01405 * pixd = pixEqualizeTRC(NULL, pixs, ...); 01406 * (1) Use fract > 0.0 to increase hue value; < 0.0 to decrease it. 01407 * 1.0 (or -1.0) represents a 360 degree rotation; i.e., no change. 01408 * (2) If no modification is requested (fract = -1.0 or 0 or 1.0), 01409 * return a copy unless in-place, in which case this is a no-op. 01410 */ 01411 PIX * 01412 pixModifyHue(PIX *pixd, 01413 PIX *pixs, 01414 l_float32 fract) 01415 { 01416 l_int32 w, h, d, i, j, wpl, delhue; 01417 l_int32 rval, gval, bval, hval, sval, vval; 01418 l_uint32 *data, *line; 01419 01420 PROCNAME("pixModifyHue"); 01421 01422 if (!pixs) 01423 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01424 if (pixGetColormap(pixs) != NULL) 01425 return (PIX *)ERROR_PTR("pixs colormapped", procName, NULL); 01426 if (pixd && (pixd != pixs)) 01427 return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); 01428 pixGetDimensions(pixs, &w, &h, &d); 01429 if (d != 32) 01430 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); 01431 if (L_ABS(fract) > 1.0) 01432 return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); 01433 01434 pixd = pixCopy(pixd, pixs); 01435 01436 delhue = (l_int32)(240 * fract); 01437 if (delhue == 0 || delhue == 240 || delhue == -240) { 01438 L_WARNING("no change requested in hue", procName); 01439 return pixd; 01440 } 01441 if (delhue < 0) 01442 delhue += 240; 01443 01444 data = pixGetData(pixd); 01445 wpl = pixGetWpl(pixd); 01446 for (i = 0; i < h; i++) { 01447 line = data + i * wpl; 01448 for (j = 0; j < w; j++) { 01449 extractRGBValues(line[j], &rval, &gval, &bval); 01450 convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); 01451 hval = (hval + delhue) % 240; 01452 convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); 01453 composeRGBPixel(rval, gval, bval, line + j); 01454 } 01455 } 01456 01457 return pixd; 01458 } 01459 01460 01461 /*! 01462 * pixModifySaturation() 01463 * 01464 * Input: pixd (<optional> can be null, existing or equal to pixs) 01465 * pixs (32 bpp rgb) 01466 * fract (between -1.0 and 1.0) 01467 * Return: pixd, or null on error 01468 * 01469 * Notes: 01470 * (1) If fract > 0.0, it gives the fraction that the pixel 01471 * saturation is moved from its initial value toward 255. 01472 * If fract < 0.0, it gives the fraction that the pixel 01473 * saturation is moved from its initial value toward 0. 01474 * The limiting values for fract = -1.0 (1.0) thus set the 01475 * saturation to 0 (255). 01476 * (2) If fract = 0, no modification is requested; return a copy 01477 * unless in-place, in which case this is a no-op. 01478 */ 01479 PIX * 01480 pixModifySaturation(PIX *pixd, 01481 PIX *pixs, 01482 l_float32 fract) 01483 { 01484 l_int32 w, h, d, i, j, wpl; 01485 l_int32 rval, gval, bval, hval, sval, vval; 01486 l_uint32 *data, *line; 01487 01488 PROCNAME("pixModifySaturation"); 01489 01490 if (!pixs) 01491 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01492 pixGetDimensions(pixs, &w, &h, &d); 01493 if (d != 32) 01494 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); 01495 if (L_ABS(fract) > 1.0) 01496 return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); 01497 01498 pixd = pixCopy(pixd, pixs); 01499 if (fract == 0.0) { 01500 L_WARNING("no change requested in saturation", procName); 01501 return pixd; 01502 } 01503 01504 data = pixGetData(pixd); 01505 wpl = pixGetWpl(pixd); 01506 for (i = 0; i < h; i++) { 01507 line = data + i * wpl; 01508 for (j = 0; j < w; j++) { 01509 extractRGBValues(line[j], &rval, &gval, &bval); 01510 convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); 01511 if (fract < 0.0) 01512 sval = (l_int32)(sval * (1.0 + fract)); 01513 else 01514 sval = (l_int32)(sval + fract * (255 - sval)); 01515 convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); 01516 composeRGBPixel(rval, gval, bval, line + j); 01517 } 01518 } 01519 01520 return pixd; 01521 } 01522 01523 01524 /*! 01525 * pixMeasureSaturation() 01526 * 01527 * Input: pixs (32 bpp rgb) 01528 * factor (subsampling factor; integer >= 1) 01529 * &sat (<return> average saturation) 01530 * Return: pixd, or null on error 01531 */ 01532 l_int32 01533 pixMeasureSaturation(PIX *pixs, 01534 l_int32 factor, 01535 l_float32 *psat) 01536 { 01537 l_int32 w, h, d, i, j, wpl, sum, count; 01538 l_int32 rval, gval, bval, hval, sval, vval; 01539 l_uint32 *data, *line; 01540 01541 PROCNAME("pixMeasureSaturation"); 01542 01543 if (!psat) 01544 return ERROR_INT("pixs not defined", procName, 1); 01545 *psat = 0.0; 01546 if (!pixs) 01547 return ERROR_INT("pixs not defined", procName, 1); 01548 pixGetDimensions(pixs, &w, &h, &d); 01549 if (d != 32) 01550 return ERROR_INT("pixs not 32 bpp", procName, 1); 01551 if (factor < 1) 01552 return ERROR_INT("subsampling factor < 1", procName, 1); 01553 01554 data = pixGetData(pixs); 01555 wpl = pixGetWpl(pixs); 01556 for (i = 0, sum = 0, count = 0; i < h; i += factor) { 01557 line = data + i * wpl; 01558 for (j = 0; j < w; j += factor) { 01559 extractRGBValues(line[j], &rval, &gval, &bval); 01560 convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); 01561 sum += sval; 01562 count++; 01563 } 01564 } 01565 01566 if (count > 0) 01567 *psat = (l_float32)sum / (l_float32)count; 01568 return 0; 01569 } 01570 01571 01572 /*-----------------------------------------------------------------------* 01573 * General multiplicative constant color transform * 01574 *-----------------------------------------------------------------------*/ 01575 /* 01576 * pixMultConstantColor() 01577 * 01578 * Input: pixs (colormapped or rgb) 01579 * rfact, gfact, bfact (multiplicative factors on each component) 01580 * Return: pixd (colormapped or rgb, with colors scaled), or null on error 01581 * 01582 * Notes: 01583 * (1) rfact, gfact and bfact can only have non-negative values. 01584 * They can be greater than 1.0. All transformed component 01585 * values are clipped to the interval [0, 255]. 01586 * (2) For multiplication with a general 3x3 matrix of constants, 01587 * use pixMultMatrixColor(). 01588 */ 01589 PIX * 01590 pixMultConstantColor(PIX *pixs, 01591 l_float32 rfact, 01592 l_float32 gfact, 01593 l_float32 bfact) 01594 { 01595 l_int32 i, j, w, h, d, wpls, wpld; 01596 l_int32 ncolors, rval, gval, bval, nrval, ngval, nbval; 01597 l_uint32 nval; 01598 l_uint32 *datas, *datad, *lines, *lined; 01599 PIX *pixd; 01600 PIXCMAP *cmap; 01601 01602 PROCNAME("pixMultConstantColor"); 01603 01604 if (!pixs) 01605 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01606 pixGetDimensions(pixs, &w, &h, &d); 01607 cmap = pixGetColormap(pixs); 01608 if (!cmap && d != 32) 01609 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); 01610 rfact = L_MAX(0.0, rfact); 01611 gfact = L_MAX(0.0, gfact); 01612 bfact = L_MAX(0.0, bfact); 01613 01614 if (cmap) { 01615 if ((pixd = pixCopy(NULL, pixs)) == NULL) 01616 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01617 cmap = pixGetColormap(pixd); 01618 ncolors = pixcmapGetCount(cmap); 01619 for (i = 0; i < ncolors; i++) { 01620 pixcmapGetColor(cmap, i, &rval, &gval, &bval); 01621 nrval = (l_int32)(rfact * rval); 01622 ngval = (l_int32)(gfact * gval); 01623 nbval = (l_int32)(bfact * bval); 01624 nrval = L_MIN(255, nrval); 01625 ngval = L_MIN(255, ngval); 01626 nbval = L_MIN(255, nbval); 01627 pixcmapResetColor(cmap, i, nrval, ngval, nbval); 01628 } 01629 return pixd; 01630 } 01631 01632 if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) 01633 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01634 datas = pixGetData(pixs); 01635 datad = pixGetData(pixd); 01636 wpls = pixGetWpl(pixs); 01637 wpld = pixGetWpl(pixd); 01638 for (i = 0; i < h; i++) { 01639 lines = datas + i * wpls; 01640 lined = datad + i * wpld; 01641 for (j = 0; j < w; j++) { 01642 extractRGBValues(lines[j], &rval, &gval, &bval); 01643 nrval = (l_int32)(rfact * rval); 01644 ngval = (l_int32)(gfact * gval); 01645 nbval = (l_int32)(bfact * bval); 01646 nrval = L_MIN(255, nrval); 01647 ngval = L_MIN(255, ngval); 01648 nbval = L_MIN(255, nbval); 01649 composeRGBPixel(nrval, ngval, nbval, &nval); 01650 *(lined + j) = nval; 01651 } 01652 } 01653 01654 return pixd; 01655 } 01656 01657 01658 /* 01659 * pixMultMatrixColor() 01660 * 01661 * Input: pixs (colormapped or rgb) 01662 * kernel (3x3 matrix of floats) 01663 * Return: pixd (colormapped or rgb), or null on error 01664 * 01665 * Notes: 01666 * (1) The kernel is a data structure used mostly for floating point 01667 * convolution. Here it is a 3x3 matrix of floats that are used 01668 * to transform the pixel values by matrix multiplication: 01669 * nrval = a[0,0] * rval + a[0,1] * gval + a[0,2] * bval 01670 * ngval = a[1,0] * rval + a[1,1] * gval + a[1,2] * bval 01671 * nbval = a[2,0] * rval + a[2,1] * gval + a[2,2] * bval 01672 * (2) The matrix can be generated in several ways. 01673 * See kernel.c for details. Here are two of them: 01674 * (a) kel = kernelCreate(3, 3); 01675 * kernelSetElement(kel, 0, 0, val00); 01676 * kernelSetElement(kel, 0, 1, val01); 01677 * ... 01678 * (b) from a static string; e.g.,: 01679 * const char *kdata = " 0.6 0.3 -0.2 " 01680 * " 0.1 1.2 0.4 " 01681 * " -0.4 0.2 0.9 "; 01682 * kel = kernelCreateFromString(3, 3, 0, 0, kdata); 01683 * (3) For the special case where the matrix is diagonal, it is easier 01684 * to use pixMultConstantColor(). 01685 * (4) Matrix entries can have positive and negative values, and can 01686 * be larger than 1.0. All transformed component values 01687 * are clipped to [0, 255]. 01688 */ 01689 PIX * 01690 pixMultMatrixColor(PIX *pixs, 01691 L_KERNEL *kel) 01692 { 01693 l_int32 i, j, index, kw, kh, w, h, d, wpls, wpld; 01694 l_int32 ncolors, rval, gval, bval, nrval, ngval, nbval; 01695 l_uint32 nval; 01696 l_uint32 *datas, *datad, *lines, *lined; 01697 l_float32 v[9]; /* use linear array for convenience */ 01698 PIX *pixd; 01699 PIXCMAP *cmap; 01700 01701 PROCNAME("pixMultMatrixColor"); 01702 01703 if (!pixs) 01704 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01705 if (!kel) 01706 return (PIX *)ERROR_PTR("kel not defined", procName, NULL); 01707 kernelGetParameters(kel, &kw, &kh, NULL, NULL); 01708 if (kw != 3 || kh != 3) 01709 return (PIX *)ERROR_PTR("matrix not 3x3", procName, NULL); 01710 pixGetDimensions(pixs, &w, &h, &d); 01711 cmap = pixGetColormap(pixs); 01712 if (!cmap && d != 32) 01713 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); 01714 01715 for (i = 0, index = 0; i < 3; i++) 01716 for (j = 0; j < 3; j++, index++) 01717 kernelGetElement(kel, i, j, v + index); 01718 01719 if (cmap) { 01720 if ((pixd = pixCopy(NULL, pixs)) == NULL) 01721 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01722 cmap = pixGetColormap(pixd); 01723 ncolors = pixcmapGetCount(cmap); 01724 for (i = 0; i < ncolors; i++) { 01725 pixcmapGetColor(cmap, i, &rval, &gval, &bval); 01726 nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval); 01727 ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval); 01728 nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval); 01729 nrval = L_MAX(0, L_MIN(255, nrval)); 01730 ngval = L_MAX(0, L_MIN(255, ngval)); 01731 nbval = L_MAX(0, L_MIN(255, nbval)); 01732 pixcmapResetColor(cmap, i, nrval, ngval, nbval); 01733 } 01734 return pixd; 01735 } 01736 01737 if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) 01738 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01739 datas = pixGetData(pixs); 01740 datad = pixGetData(pixd); 01741 wpls = pixGetWpl(pixs); 01742 wpld = pixGetWpl(pixd); 01743 for (i = 0; i < h; i++) { 01744 lines = datas + i * wpls; 01745 lined = datad + i * wpld; 01746 for (j = 0; j < w; j++) { 01747 extractRGBValues(lines[j], &rval, &gval, &bval); 01748 nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval); 01749 ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval); 01750 nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval); 01751 nrval = L_MAX(0, L_MIN(255, nrval)); 01752 ngval = L_MAX(0, L_MIN(255, ngval)); 01753 nbval = L_MAX(0, L_MIN(255, nbval)); 01754 composeRGBPixel(nrval, ngval, nbval, &nval); 01755 *(lined + j) = nval; 01756 } 01757 } 01758 01759 return pixd; 01760 } 01761 01762 01763 /*-------------------------------------------------------------* 01764 * Half-edge by bandpass * 01765 *-------------------------------------------------------------*/ 01766 /*! 01767 * pixHalfEdgeByBandpass() 01768 * 01769 * Input: pixs (8 bpp gray or 32 bpp rgb) 01770 * sm1h, sm1v ("half-widths" of smoothing filter sm1) 01771 * sm2h, sm2v ("half-widths" of smoothing filter sm2) 01772 * (require sm2 != sm1) 01773 * Return: pixd, or null on error 01774 * 01775 * Notes: 01776 * (1) We use symmetric smoothing filters of odd dimension, 01777 * typically use 3, 5, 7, etc. The smoothing parameters 01778 * for these are 1, 2, 3, etc. The filter size is related 01779 * to the smoothing parameter by 01780 * size = 2 * smoothing + 1 01781 * (2) Because we take the difference of two lowpass filters, 01782 * this is actually a bandpass filter. 01783 * (3) We allow both filters to be anisotropic. 01784 * (4) Consider either the h or v component of the 2 filters. 01785 * Depending on whether sm1 > sm2 or sm2 > sm1, we get 01786 * different halves of the smoothed gradients (or "edges"). 01787 * This difference of smoothed signals looks more like 01788 * a second derivative of a transition, which we rectify 01789 * by not allowing the signal to go below zero. If sm1 < sm2, 01790 * the sm2 transition is broader, so the difference between 01791 * sm1 and sm2 signals is positive on the upper half of 01792 * the transition. Likewise, if sm1 > sm2, the sm1 - sm2 01793 * signal difference is positive on the lower half of 01794 * the transition. 01795 */ 01796 PIX * 01797 pixHalfEdgeByBandpass(PIX *pixs, 01798 l_int32 sm1h, 01799 l_int32 sm1v, 01800 l_int32 sm2h, 01801 l_int32 sm2v) 01802 { 01803 l_int32 d; 01804 PIX *pixg, *pixacc, *pixc1, *pixc2; 01805 01806 PROCNAME("pixHalfEdgeByBandpass"); 01807 01808 if (!pixs) 01809 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01810 if (sm1h == sm2h && sm1v == sm2v) 01811 return (PIX *)ERROR_PTR("sm2 = sm1", procName, NULL); 01812 d = pixGetDepth(pixs); 01813 if (d != 8 && d != 32) 01814 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); 01815 if (d == 32) 01816 pixg = pixConvertRGBToLuminance(pixs); 01817 else /* d == 8 */ 01818 pixg = pixClone(pixs); 01819 01820 /* Make a convolution accumulator and use it twice */ 01821 if ((pixacc = pixBlockconvAccum(pixg)) == NULL) 01822 return (PIX *)ERROR_PTR("pixacc not made", procName, NULL); 01823 if ((pixc1 = pixBlockconvGray(pixg, pixacc, sm1h, sm1v)) == NULL) 01824 return (PIX *)ERROR_PTR("pixc1 not made", procName, NULL); 01825 if ((pixc2 = pixBlockconvGray(pixg, pixacc, sm2h, sm2v)) == NULL) 01826 return (PIX *)ERROR_PTR("pixc2 not made", procName, NULL); 01827 pixDestroy(&pixacc); 01828 01829 /* Compute the half-edge using pixc1 - pixc2. */ 01830 pixSubtractGray(pixc1, pixc1, pixc2); 01831 01832 pixDestroy(&pixg); 01833 pixDestroy(&pixc2); 01834 return pixc1; 01835 } 01836