00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "magick/studio.h"
00044 #include "magick/property.h"
00045 #include "magick/blob.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/colorspace.h"
00050 #include "magick/constitute.h"
00051 #include "magick/decorate.h"
00052 #include "magick/draw.h"
00053 #include "magick/enhance.h"
00054 #include "magick/exception.h"
00055 #include "magick/exception-private.h"
00056 #include "magick/effect.h"
00057 #include "magick/fx.h"
00058 #include "magick/gem.h"
00059 #include "magick/geometry.h"
00060 #include "magick/image-private.h"
00061 #include "magick/list.h"
00062 #include "magick/log.h"
00063 #include "magick/memory_.h"
00064 #include "magick/monitor.h"
00065 #include "magick/monitor-private.h"
00066 #include "magick/montage.h"
00067 #include "magick/paint.h"
00068 #include "magick/pixel-private.h"
00069 #include "magick/property.h"
00070 #include "magick/quantize.h"
00071 #include "magick/quantum.h"
00072 #include "magick/random_.h"
00073 #include "magick/resample.h"
00074 #include "magick/resample-private.h"
00075 #include "magick/resize.h"
00076 #include "magick/resource_.h"
00077 #include "magick/segment.h"
00078 #include "magick/shear.h"
00079 #include "magick/signature-private.h"
00080 #include "magick/string_.h"
00081 #include "magick/transform.h"
00082 #include "magick/threshold.h"
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
00125 const double sigma,ExceptionInfo *exception)
00126 {
00127 Image
00128 *blur_image;
00129
00130 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
00131 exception);
00132 return(blur_image);
00133 }
00134
00135 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
00136 const ChannelType channel,const double radius,const double sigma,
00137 ExceptionInfo *exception)
00138 {
00139 #define AdaptiveBlurImageTag "Convolve/Image"
00140 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00141
00142 double
00143 **kernel;
00144
00145 Image
00146 *blur_image,
00147 *edge_image,
00148 *gaussian_image;
00149
00150 long
00151 j,
00152 progress,
00153 y;
00154
00155 MagickBooleanType
00156 status;
00157
00158 MagickPixelPacket
00159 zero;
00160
00161 MagickRealType
00162 alpha,
00163 normalize;
00164
00165 register long
00166 i,
00167 u,
00168 v;
00169
00170 unsigned long
00171 width;
00172
00173 ViewInfo
00174 **blur_view,
00175 **edge_view,
00176 **image_view;
00177
00178 assert(image != (const Image *) NULL);
00179 assert(image->signature == MagickSignature);
00180 if (image->debug != MagickFalse)
00181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00182 assert(exception != (ExceptionInfo *) NULL);
00183 assert(exception->signature == MagickSignature);
00184 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
00185 if (blur_image == (Image *) NULL)
00186 return((Image *) NULL);
00187 if (fabs(sigma) <= MagickEpsilon)
00188 return(blur_image);
00189 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00190 {
00191 InheritException(exception,&blur_image->exception);
00192 blur_image=DestroyImage(blur_image);
00193 return((Image *) NULL);
00194 }
00195
00196
00197
00198 edge_image=EdgeImage(image,radius,exception);
00199 if (edge_image == (Image *) NULL)
00200 {
00201 blur_image=DestroyImage(blur_image);
00202 return((Image *) NULL);
00203 }
00204 (void) LevelImage(edge_image,"20%,95%");
00205 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00206 if (gaussian_image != (Image *) NULL)
00207 {
00208 edge_image=DestroyImage(edge_image);
00209 edge_image=gaussian_image;
00210 }
00211 (void) LevelImage(edge_image,"10%,95%");
00212
00213
00214
00215 width=GetOptimalKernelWidth2D(radius,sigma);
00216 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00217 if (kernel == (double **) NULL)
00218 {
00219 edge_image=DestroyImage(edge_image);
00220 blur_image=DestroyImage(blur_image);
00221 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00222 }
00223 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00224 for (i=0; i < (long) width; i+=2)
00225 {
00226 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00227 sizeof(**kernel));
00228 if (kernel[i] == (double *) NULL)
00229 break;
00230 j=0;
00231 normalize=0.0;
00232 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00233 {
00234 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00235 {
00236 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00237 kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00238 if (((width-i) < 3) || (u != 0) || (v != 0))
00239 normalize+=kernel[i][j];
00240 j++;
00241 }
00242 }
00243 kernel[i][j/2]=(double) ((-2.0)*normalize);
00244 normalize=0.0;
00245 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00246 normalize+=kernel[i][j];
00247 if (fabs(normalize) <= MagickEpsilon)
00248 normalize=1.0;
00249 normalize=1.0/normalize;
00250 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00251 kernel[i][j]=(double) (normalize*kernel[i][j]);
00252 }
00253 if (i < (long) width)
00254 {
00255 for (i-=2; i >= 0; i-=2)
00256 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00257 kernel=(double **) RelinquishMagickMemory(kernel);
00258 edge_image=DestroyImage(edge_image);
00259 blur_image=DestroyImage(blur_image);
00260 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00261 }
00262
00263
00264
00265 status=MagickTrue;
00266 progress=0;
00267 GetMagickPixelPacket(image,&zero);
00268 image_view=AcquireCacheViewThreadSet(image);
00269 edge_view=AcquireCacheViewThreadSet(edge_image);
00270 blur_view=AcquireCacheViewThreadSet(blur_image);
00271 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00272 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00273 #endif
00274 for (y=0; y < (long) blur_image->rows; y++)
00275 {
00276 register const IndexPacket
00277 *indexes;
00278
00279 register const PixelPacket
00280 *p,
00281 *r;
00282
00283 register IndexPacket
00284 *blur_indexes;
00285
00286 register long
00287 id,
00288 x;
00289
00290 register PixelPacket
00291 *q;
00292
00293 if (status == MagickFalse)
00294 continue;
00295 id=GetCacheViewThreadId();
00296 r=GetCacheViewVirtualPixels(edge_view[id],0,y,edge_image->columns,1,
00297 exception);
00298 q=QueueCacheViewAuthenticPixels(blur_view[id],0,y,blur_image->columns,1,
00299 exception);
00300 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00301 {
00302 status=MagickFalse;
00303 continue;
00304 }
00305 blur_indexes=GetCacheViewAuthenticIndexes(blur_view[id]);
00306 for (x=0; x < (long) blur_image->columns; x++)
00307 {
00308 MagickPixelPacket
00309 pixel;
00310
00311 MagickRealType
00312 alpha,
00313 gamma;
00314
00315 register const double
00316 *k;
00317
00318 register long
00319 i,
00320 u,
00321 v;
00322
00323 gamma=0.0;
00324 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
00325 if (i < 0)
00326 i=0;
00327 else
00328 if (i > (long) width)
00329 i=(long) width;
00330 if ((i & 0x01) != 0)
00331 i--;
00332 p=GetCacheViewVirtualPixels(image_view[id],x-((long) (width-i)/2L),y-
00333 (long) ((width-i)/2L),width-i,width-i,exception);
00334 if (p == (const PixelPacket *) NULL)
00335 break;
00336 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00337 pixel=zero;
00338 k=kernel[i];
00339 for (v=0; v < (long) (width-i); v++)
00340 {
00341 for (u=0; u < (long) (width-i); u++)
00342 {
00343 alpha=1.0;
00344 if (((channel & OpacityChannel) != 0) &&
00345 (image->matte != MagickFalse))
00346 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00347 if ((channel & RedChannel) != 0)
00348 pixel.red+=(*k)*alpha*p->red;
00349 if ((channel & GreenChannel) != 0)
00350 pixel.green+=(*k)*alpha*p->green;
00351 if ((channel & BlueChannel) != 0)
00352 pixel.blue+=(*k)*alpha*p->blue;
00353 if ((channel & OpacityChannel) != 0)
00354 pixel.opacity+=(*k)*p->opacity;
00355 if (((channel & IndexChannel) != 0) &&
00356 (image->colorspace == CMYKColorspace))
00357 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00358 gamma+=(*k)*alpha;
00359 k++;
00360 p++;
00361 }
00362 }
00363 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00364 if ((channel & RedChannel) != 0)
00365 q->red=RoundToQuantum(gamma*pixel.red+image->bias);
00366 if ((channel & GreenChannel) != 0)
00367 q->green=RoundToQuantum(gamma*pixel.green+image->bias);
00368 if ((channel & BlueChannel) != 0)
00369 q->blue=RoundToQuantum(gamma*pixel.blue+image->bias);
00370 if ((channel & OpacityChannel) != 0)
00371 q->opacity=RoundToQuantum(pixel.opacity+image->bias);
00372 if (((channel & IndexChannel) != 0) &&
00373 (image->colorspace == CMYKColorspace))
00374 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+image->bias);
00375 q++;
00376 r++;
00377 }
00378 if (SyncCacheViewAuthenticPixels(blur_view[id],exception) == MagickFalse)
00379 status=MagickFalse;
00380 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00381 {
00382 MagickBooleanType
00383 proceed;
00384
00385 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00386 #pragma omp critical
00387 #endif
00388 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
00389 image->rows);
00390 if (proceed == MagickFalse)
00391 status=MagickFalse;
00392 }
00393 }
00394 blur_image->type=image->type;
00395 blur_view=DestroyCacheViewThreadSet(blur_view);
00396 edge_view=DestroyCacheViewThreadSet(edge_view);
00397 image_view=DestroyCacheViewThreadSet(image_view);
00398 edge_image=DestroyImage(edge_image);
00399 for (i=0; i < (long) width; i+=2)
00400 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00401 kernel=(double **) RelinquishMagickMemory(kernel);
00402 if (status == MagickFalse)
00403 blur_image=DestroyImage(blur_image);
00404 return(blur_image);
00405 }
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
00448 const double sigma,ExceptionInfo *exception)
00449 {
00450 Image
00451 *sharp_image;
00452
00453 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
00454 exception);
00455 return(sharp_image);
00456 }
00457
00458 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
00459 const ChannelType channel,const double radius,const double sigma,
00460 ExceptionInfo *exception)
00461 {
00462 #define AdaptiveSharpenImageTag "Convolve/Image"
00463 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00464
00465 double
00466 **kernel;
00467
00468 Image
00469 *sharp_image,
00470 *edge_image,
00471 *gaussian_image;
00472
00473 long
00474 j,
00475 progress,
00476 y;
00477
00478 MagickBooleanType
00479 status;
00480
00481 MagickPixelPacket
00482 zero;
00483
00484 MagickRealType
00485 alpha,
00486 normalize;
00487
00488 register long
00489 i,
00490 u,
00491 v;
00492
00493 unsigned long
00494 width;
00495
00496 ViewInfo
00497 **sharp_view,
00498 **edge_view,
00499 **image_view;
00500
00501 assert(image != (const Image *) NULL);
00502 assert(image->signature == MagickSignature);
00503 if (image->debug != MagickFalse)
00504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00505 assert(exception != (ExceptionInfo *) NULL);
00506 assert(exception->signature == MagickSignature);
00507 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
00508 if (sharp_image == (Image *) NULL)
00509 return((Image *) NULL);
00510 if (fabs(sigma) <= MagickEpsilon)
00511 return(sharp_image);
00512 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
00513 {
00514 InheritException(exception,&sharp_image->exception);
00515 sharp_image=DestroyImage(sharp_image);
00516 return((Image *) NULL);
00517 }
00518
00519
00520
00521 edge_image=EdgeImage(image,radius,exception);
00522 if (edge_image == (Image *) NULL)
00523 {
00524 sharp_image=DestroyImage(sharp_image);
00525 return((Image *) NULL);
00526 }
00527 (void) LevelImage(edge_image,"20%,95%");
00528 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00529 if (gaussian_image != (Image *) NULL)
00530 {
00531 edge_image=DestroyImage(edge_image);
00532 edge_image=gaussian_image;
00533 }
00534 (void) LevelImage(edge_image,"10%,95%");
00535
00536
00537
00538 width=GetOptimalKernelWidth2D(radius,sigma);
00539 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00540 if (kernel == (double **) NULL)
00541 {
00542 edge_image=DestroyImage(edge_image);
00543 sharp_image=DestroyImage(sharp_image);
00544 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00545 }
00546 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00547 for (i=0; i < (long) width; i+=2)
00548 {
00549 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00550 sizeof(**kernel));
00551 if (kernel[i] == (double *) NULL)
00552 break;
00553 j=0;
00554 normalize=0.0;
00555 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00556 {
00557 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00558 {
00559 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00560 kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00561 if (((width-i) < 3) || (u != 0) || (v != 0))
00562 normalize+=kernel[i][j];
00563 j++;
00564 }
00565 }
00566 kernel[i][j/2]=(double) ((-2.0)*normalize);
00567 normalize=0.0;
00568 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00569 normalize+=kernel[i][j];
00570 if (fabs(normalize) <= MagickEpsilon)
00571 normalize=1.0;
00572 normalize=1.0/normalize;
00573 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00574 kernel[i][j]=(double) (normalize*kernel[i][j]);
00575 }
00576 if (i < (long) width)
00577 {
00578 for (i-=2; i >= 0; i-=2)
00579 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00580 kernel=(double **) RelinquishMagickMemory(kernel);
00581 edge_image=DestroyImage(edge_image);
00582 sharp_image=DestroyImage(sharp_image);
00583 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00584 }
00585
00586
00587
00588 status=MagickTrue;
00589 progress=0;
00590 GetMagickPixelPacket(image,&zero);
00591 image_view=AcquireCacheViewThreadSet(image);
00592 edge_view=AcquireCacheViewThreadSet(edge_image);
00593 sharp_view=AcquireCacheViewThreadSet(sharp_image);
00594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00595 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00596 #endif
00597 for (y=0; y < (long) sharp_image->rows; y++)
00598 {
00599 register const IndexPacket
00600 *indexes;
00601
00602 register const PixelPacket
00603 *p,
00604 *r;
00605
00606 register IndexPacket
00607 *sharp_indexes;
00608
00609 register long
00610 id,
00611 x;
00612
00613 register PixelPacket
00614 *q;
00615
00616 if (status == MagickFalse)
00617 continue;
00618 id=GetCacheViewThreadId();
00619 r=GetCacheViewVirtualPixels(edge_view[id],0,y,edge_image->columns,1,
00620 exception);
00621 q=QueueCacheViewAuthenticPixels(sharp_view[id],0,y,sharp_image->columns,1,
00622 exception);
00623 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00624 {
00625 status=MagickFalse;
00626 continue;
00627 }
00628 sharp_indexes=GetCacheViewAuthenticIndexes(sharp_view[id]);
00629 for (x=0; x < (long) sharp_image->columns; x++)
00630 {
00631 MagickPixelPacket
00632 pixel;
00633
00634 MagickRealType
00635 alpha,
00636 gamma;
00637
00638 register const double
00639 *k;
00640
00641 register long
00642 i,
00643 u,
00644 v;
00645
00646 gamma=0.0;
00647 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
00648 if (i < 0)
00649 i=0;
00650 else
00651 if (i > (long) width)
00652 i=(long) width;
00653 if ((i & 0x01) != 0)
00654 i--;
00655 p=GetCacheViewVirtualPixels(image_view[id],x-((long) (width-i)/2L),y-
00656 (long) ((width-i)/2L),width-i,width-i,exception);
00657 if (p == (const PixelPacket *) NULL)
00658 break;
00659 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00660 k=kernel[i];
00661 pixel=zero;
00662 for (v=0; v < (long) (width-i); v++)
00663 {
00664 for (u=0; u < (long) (width-i); u++)
00665 {
00666 alpha=1.0;
00667 if (((channel & OpacityChannel) != 0) &&
00668 (image->matte != MagickFalse))
00669 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00670 if ((channel & RedChannel) != 0)
00671 pixel.red+=(*k)*alpha*p->red;
00672 if ((channel & GreenChannel) != 0)
00673 pixel.green+=(*k)*alpha*p->green;
00674 if ((channel & BlueChannel) != 0)
00675 pixel.blue+=(*k)*alpha*p->blue;
00676 if ((channel & OpacityChannel) != 0)
00677 pixel.opacity+=(*k)*p->opacity;
00678 if (((channel & IndexChannel) != 0) &&
00679 (image->colorspace == CMYKColorspace))
00680 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00681 gamma+=(*k)*alpha;
00682 k++;
00683 p++;
00684 }
00685 }
00686 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00687 if ((channel & RedChannel) != 0)
00688 q->red=RoundToQuantum(gamma*pixel.red+image->bias);
00689 if ((channel & GreenChannel) != 0)
00690 q->green=RoundToQuantum(gamma*pixel.green+image->bias);
00691 if ((channel & BlueChannel) != 0)
00692 q->blue=RoundToQuantum(gamma*pixel.blue+image->bias);
00693 if ((channel & OpacityChannel) != 0)
00694 q->opacity=RoundToQuantum(pixel.opacity+image->bias);
00695 if (((channel & IndexChannel) != 0) &&
00696 (image->colorspace == CMYKColorspace))
00697 sharp_indexes[x]=RoundToQuantum(gamma*pixel.index+image->bias);
00698 q++;
00699 r++;
00700 }
00701 if (SyncCacheViewAuthenticPixels(sharp_view[id],exception) == MagickFalse)
00702 status=MagickFalse;
00703 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00704 {
00705 MagickBooleanType
00706 proceed;
00707
00708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00709 #pragma omp critical
00710 #endif
00711 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
00712 image->rows);
00713 if (proceed == MagickFalse)
00714 status=MagickFalse;
00715 }
00716 }
00717 sharp_image->type=image->type;
00718 sharp_view=DestroyCacheViewThreadSet(sharp_view);
00719 edge_view=DestroyCacheViewThreadSet(edge_view);
00720 image_view=DestroyCacheViewThreadSet(image_view);
00721 edge_image=DestroyImage(edge_image);
00722 for (i=0; i < (long) width; i+=2)
00723 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00724 kernel=(double **) RelinquishMagickMemory(kernel);
00725 if (status == MagickFalse)
00726 sharp_image=DestroyImage(sharp_image);
00727 return(sharp_image);
00728 }
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772 MagickExport Image *BlurImage(const Image *image,const double radius,
00773 const double sigma,ExceptionInfo *exception)
00774 {
00775 Image
00776 *blur_image;
00777
00778 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
00779 return(blur_image);
00780 }
00781
00782 static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
00783 {
00784 #define KernelRank 3
00785
00786 double
00787 *kernel;
00788
00789 long
00790 bias;
00791
00792 MagickRealType
00793 alpha,
00794 normalize;
00795
00796 register long
00797 i;
00798
00799
00800
00801
00802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00803 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00804 if (kernel == (double *) NULL)
00805 return(0);
00806 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00807 bias=KernelRank*(long) width/2;
00808 for (i=(-bias); i <= bias; i++)
00809 {
00810 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
00811 MagickSigma*MagickSigma)));
00812 kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
00813 }
00814 normalize=0.0;
00815 for (i=0; i < (long) width; i++)
00816 normalize+=kernel[i];
00817 for (i=0; i < (long) width; i++)
00818 kernel[i]/=normalize;
00819 return(kernel);
00820 }
00821
00822 MagickExport Image *BlurImageChannel(const Image *image,
00823 const ChannelType channel,const double radius,const double sigma,
00824 ExceptionInfo *exception)
00825 {
00826 #define BlurImageTag "Blur/Image"
00827
00828 double
00829 *kernel;
00830
00831 Image
00832 *blur_image;
00833
00834 long
00835 progress,
00836 x,
00837 y;
00838
00839 MagickBooleanType
00840 status;
00841
00842 MagickPixelPacket
00843 zero;
00844
00845 MagickRealType
00846 bias;
00847
00848 register long
00849 i;
00850
00851 unsigned long
00852 width;
00853
00854 ViewInfo
00855 **blur_view,
00856 **image_view;
00857
00858
00859
00860
00861 assert(image != (Image *) NULL);
00862 assert(image->signature == MagickSignature);
00863 if (image->debug != MagickFalse)
00864 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00865 assert(exception != (ExceptionInfo *) NULL);
00866 assert(exception->signature == MagickSignature);
00867 blur_image=CloneImage(image,0,0,MagickTrue,exception);
00868 if (blur_image == (Image *) NULL)
00869 return((Image *) NULL);
00870 if (fabs(sigma) <= MagickEpsilon)
00871 return(blur_image);
00872 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00873 {
00874 InheritException(exception,&blur_image->exception);
00875 blur_image=DestroyImage(blur_image);
00876 return((Image *) NULL);
00877 }
00878 width=GetOptimalKernelWidth1D(radius,sigma);
00879 kernel=GetBlurKernel(width,sigma);
00880 if (kernel == (double *) NULL)
00881 {
00882 blur_image=DestroyImage(blur_image);
00883 return((Image *) NULL);
00884 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00885 }
00886 if (image->debug != MagickFalse)
00887 {
00888 char
00889 format[MaxTextExtent],
00890 *message;
00891
00892 register const double
00893 *k;
00894
00895 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00896 " BlurImage with %ld kernel:",width);
00897 message=AcquireString("");
00898 k=kernel;
00899 for (i=0; i < (long) width; i++)
00900 {
00901 *message='\0';
00902 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
00903 (void) ConcatenateString(&message,format);
00904 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
00905 (void) ConcatenateString(&message,format);
00906 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00907 }
00908 message=DestroyString(message);
00909 }
00910
00911
00912
00913 status=MagickTrue;
00914 progress=0;
00915 GetMagickPixelPacket(image,&zero);
00916 bias=image->bias;
00917 image_view=AcquireCacheViewThreadSet(image);
00918 blur_view=AcquireCacheViewThreadSet(blur_image);
00919 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00920 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00921 #endif
00922 for (y=0; y < (long) blur_image->rows; y++)
00923 {
00924 register const IndexPacket
00925 *indexes;
00926
00927 register const PixelPacket
00928 *p;
00929
00930 register IndexPacket
00931 *blur_indexes;
00932
00933 register long
00934 id,
00935 x;
00936
00937 register PixelPacket
00938 *q;
00939
00940 if (status == MagickFalse)
00941 continue;
00942 id=GetCacheViewThreadId();
00943 p=GetCacheViewVirtualPixels(image_view[id],-((long) width/2L),y,
00944 image->columns+width,1,exception);
00945 q=GetCacheViewAuthenticPixels(blur_view[id],0,y,blur_image->columns,1,
00946 exception);
00947 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00948 {
00949 status=MagickFalse;
00950 continue;
00951 }
00952 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00953 blur_indexes=GetCacheViewAuthenticIndexes(blur_view[id]);
00954 for (x=0; x < (long) blur_image->columns; x++)
00955 {
00956 MagickPixelPacket
00957 pixel;
00958
00959 register const double
00960 *k;
00961
00962 register long
00963 i;
00964
00965 pixel=zero;
00966 k=kernel;
00967 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
00968 {
00969 for (i=0; i < (long) width; i++)
00970 {
00971 pixel.red+=(*k)*(p+i)->red;
00972 pixel.green+=(*k)*(p+i)->green;
00973 pixel.blue+=(*k)*(p+i)->blue;
00974 pixel.opacity+=(*k)*(p+i)->opacity;
00975 k++;
00976 }
00977 if ((channel & RedChannel) != 0)
00978 q->red=RoundToQuantum(pixel.red+bias);
00979 if ((channel & GreenChannel) != 0)
00980 q->green=RoundToQuantum(pixel.green+bias);
00981 if ((channel & BlueChannel) != 0)
00982 q->blue=RoundToQuantum(pixel.blue+bias);
00983 if ((channel & OpacityChannel) != 0)
00984 q->opacity=RoundToQuantum(pixel.opacity+bias);
00985 if (((channel & IndexChannel) != 0) &&
00986 (image->colorspace == CMYKColorspace))
00987 {
00988 k=kernel;
00989 for (i=0; i < (long) width; i++)
00990 {
00991 pixel.index+=(*k)*indexes[x+i];
00992 k++;
00993 }
00994 blur_indexes[x]=RoundToQuantum(pixel.index+bias);
00995 }
00996 }
00997 else
00998 {
00999 MagickRealType
01000 alpha,
01001 gamma;
01002
01003 gamma=0.0;
01004 for (i=0; i < (long) width; i++)
01005 {
01006 alpha=(MagickRealType) (QuantumScale*(QuantumRange-(p+i)->opacity));
01007 pixel.red+=(*k)*alpha*(p+i)->red;
01008 pixel.green+=(*k)*alpha*(p+i)->green;
01009 pixel.blue+=(*k)*alpha*(p+i)->blue;
01010 pixel.opacity+=(*k)*(p+i)->opacity;
01011 gamma+=(*k)*alpha;
01012 k++;
01013 }
01014 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01015 if ((channel & RedChannel) != 0)
01016 q->red=RoundToQuantum(gamma*pixel.red+bias);
01017 if ((channel & GreenChannel) != 0)
01018 q->green=RoundToQuantum(gamma*pixel.green+bias);
01019 if ((channel & BlueChannel) != 0)
01020 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
01021 if ((channel & OpacityChannel) != 0)
01022 q->opacity=RoundToQuantum(pixel.opacity+bias);
01023 if (((channel & IndexChannel) != 0) &&
01024 (image->colorspace == CMYKColorspace))
01025 {
01026 k=kernel;
01027 for (i=0; i < (long) width; i++)
01028 {
01029 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01030 (p+i)->opacity));
01031 pixel.index+=(*k)*alpha*indexes[x+i];
01032 k++;
01033 }
01034 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
01035 }
01036 }
01037 p++;
01038 q++;
01039 }
01040 if (SyncCacheViewAuthenticPixels(blur_view[id],exception) == MagickFalse)
01041 status=MagickFalse;
01042 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01043 {
01044 MagickBooleanType
01045 proceed;
01046
01047 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01048 #pragma omp critical
01049 #endif
01050 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01051 blur_image->columns);
01052 if (proceed == MagickFalse)
01053 status=MagickFalse;
01054 }
01055 }
01056
01057
01058
01059 image_view=DestroyCacheViewThreadSet(image_view);
01060 image_view=AcquireCacheViewThreadSet(blur_image);
01061 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01062 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
01063 #endif
01064 for (x=0; x < (long) blur_image->columns; x++)
01065 {
01066 register const IndexPacket
01067 *indexes;
01068
01069 register const PixelPacket
01070 *p;
01071
01072 register IndexPacket
01073 *blur_indexes;
01074
01075 register long
01076 id,
01077 y;
01078
01079 register PixelPacket
01080 *q;
01081
01082 if (status == MagickFalse)
01083 continue;
01084 id=GetCacheViewThreadId();
01085 p=GetCacheViewVirtualPixels(image_view[id],x,-((long) width/2L),1,
01086 image->rows+width,exception);
01087 q=GetCacheVi