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/random-private.h"
00074 #include "magick/resample.h"
00075 #include "magick/resample-private.h"
00076 #include "magick/resize.h"
00077 #include "magick/resource_.h"
00078 #include "magick/segment.h"
00079 #include "magick/shear.h"
00080 #include "magick/signature-private.h"
00081 #include "magick/string_.h"
00082 #include "magick/thread-private.h"
00083 #include "magick/transform.h"
00084 #include "magick/threshold.h"
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
00125
00126 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
00127 const double sigma,ExceptionInfo *exception)
00128 {
00129 Image
00130 *blur_image;
00131
00132 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
00133 exception);
00134 return(blur_image);
00135 }
00136
00137 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
00138 const ChannelType channel,const double radius,const double sigma,
00139 ExceptionInfo *exception)
00140 {
00141 #define AdaptiveBlurImageTag "Convolve/Image"
00142 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00143
00144 double
00145 **kernel;
00146
00147 Image
00148 *blur_image,
00149 *edge_image,
00150 *gaussian_image;
00151
00152 long
00153 j,
00154 progress,
00155 y;
00156
00157 MagickBooleanType
00158 status;
00159
00160 MagickPixelPacket
00161 bias;
00162
00163 MagickRealType
00164 alpha,
00165 normalize;
00166
00167 register long
00168 i,
00169 u,
00170 v;
00171
00172 unsigned long
00173 width;
00174
00175 CacheView
00176 *blur_view,
00177 *edge_view,
00178 *image_view;
00179
00180 assert(image != (const Image *) NULL);
00181 assert(image->signature == MagickSignature);
00182 if (image->debug != MagickFalse)
00183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00184 assert(exception != (ExceptionInfo *) NULL);
00185 assert(exception->signature == MagickSignature);
00186 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
00187 if (blur_image == (Image *) NULL)
00188 return((Image *) NULL);
00189 if (fabs(sigma) <= MagickEpsilon)
00190 return(blur_image);
00191 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00192 {
00193 InheritException(exception,&blur_image->exception);
00194 blur_image=DestroyImage(blur_image);
00195 return((Image *) NULL);
00196 }
00197
00198
00199
00200 edge_image=EdgeImage(image,radius,exception);
00201 if (edge_image == (Image *) NULL)
00202 {
00203 blur_image=DestroyImage(blur_image);
00204 return((Image *) NULL);
00205 }
00206 (void) LevelImage(edge_image,"20%,95%");
00207 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00208 if (gaussian_image != (Image *) NULL)
00209 {
00210 edge_image=DestroyImage(edge_image);
00211 edge_image=gaussian_image;
00212 }
00213 (void) LevelImage(edge_image,"10%,95%");
00214
00215
00216
00217 width=GetOptimalKernelWidth2D(radius,sigma);
00218 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00219 if (kernel == (double **) NULL)
00220 {
00221 edge_image=DestroyImage(edge_image);
00222 blur_image=DestroyImage(blur_image);
00223 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00224 }
00225 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00226 for (i=0; i < (long) width; i+=2)
00227 {
00228 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00229 sizeof(**kernel));
00230 if (kernel[i] == (double *) NULL)
00231 break;
00232 j=0;
00233 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00234 {
00235 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00236 {
00237 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00238 kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00239 j++;
00240 }
00241 }
00242 normalize=0.0;
00243 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00244 normalize+=kernel[i][j];
00245 if (fabs(normalize) <= MagickEpsilon)
00246 normalize=1.0;
00247 normalize=1.0/normalize;
00248 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00249 kernel[i][j]=(double) (normalize*kernel[i][j]);
00250 }
00251 if (i < (long) width)
00252 {
00253 for (i-=2; i >= 0; i-=2)
00254 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00255 kernel=(double **) RelinquishMagickMemory(kernel);
00256 edge_image=DestroyImage(edge_image);
00257 blur_image=DestroyImage(blur_image);
00258 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00259 }
00260
00261
00262
00263 status=MagickTrue;
00264 progress=0;
00265 GetMagickPixelPacket(image,&bias);
00266 SetMagickPixelPacketBias(image,&bias);
00267 image_view=AcquireCacheView(image);
00268 edge_view=AcquireCacheView(edge_image);
00269 blur_view=AcquireCacheView(blur_image);
00270 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00271 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00272 #endif
00273 for (y=0; y < (long) blur_image->rows; y++)
00274 {
00275 register const IndexPacket
00276 *__restrict indexes;
00277
00278 register const PixelPacket
00279 *__restrict p,
00280 *__restrict r;
00281
00282 register IndexPacket
00283 *__restrict blur_indexes;
00284
00285 register long
00286 x;
00287
00288 register PixelPacket
00289 *__restrict q;
00290
00291 if (status == MagickFalse)
00292 continue;
00293 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
00294 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
00295 exception);
00296 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00297 {
00298 status=MagickFalse;
00299 continue;
00300 }
00301 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
00302 for (x=0; x < (long) blur_image->columns; x++)
00303 {
00304 MagickPixelPacket
00305 pixel;
00306
00307 MagickRealType
00308 alpha,
00309 gamma;
00310
00311 register const double
00312 *__restrict k;
00313
00314 register long
00315 i,
00316 u,
00317 v;
00318
00319 gamma=0.0;
00320 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
00321 if (i < 0)
00322 i=0;
00323 else
00324 if (i > (long) width)
00325 i=(long) width;
00326 if ((i & 0x01) != 0)
00327 i--;
00328 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
00329 ((width-i)/2L),width-i,width-i,exception);
00330 if (p == (const PixelPacket *) NULL)
00331 break;
00332 indexes=GetCacheViewVirtualIndexQueue(image_view);
00333 pixel=bias;
00334 k=kernel[i];
00335 for (v=0; v < (long) (width-i); v++)
00336 {
00337 for (u=0; u < (long) (width-i); u++)
00338 {
00339 alpha=1.0;
00340 if (((channel & OpacityChannel) != 0) &&
00341 (image->matte != MagickFalse))
00342 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00343 if ((channel & RedChannel) != 0)
00344 pixel.red+=(*k)*alpha*p->red;
00345 if ((channel & GreenChannel) != 0)
00346 pixel.green+=(*k)*alpha*p->green;
00347 if ((channel & BlueChannel) != 0)
00348 pixel.blue+=(*k)*alpha*p->blue;
00349 if ((channel & OpacityChannel) != 0)
00350 pixel.opacity+=(*k)*p->opacity;
00351 if (((channel & IndexChannel) != 0) &&
00352 (image->colorspace == CMYKColorspace))
00353 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00354 gamma+=(*k)*alpha;
00355 k++;
00356 p++;
00357 }
00358 }
00359 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00360 if ((channel & RedChannel) != 0)
00361 q->red=RoundToQuantum(gamma*pixel.red);
00362 if ((channel & GreenChannel) != 0)
00363 q->green=RoundToQuantum(gamma*pixel.green);
00364 if ((channel & BlueChannel) != 0)
00365 q->blue=RoundToQuantum(gamma*pixel.blue);
00366 if ((channel & OpacityChannel) != 0)
00367 q->opacity=RoundToQuantum(pixel.opacity);
00368 if (((channel & IndexChannel) != 0) &&
00369 (image->colorspace == CMYKColorspace))
00370 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
00371 q++;
00372 r++;
00373 }
00374 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
00375 status=MagickFalse;
00376 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00377 {
00378 MagickBooleanType
00379 proceed;
00380
00381 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00382 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
00383 #endif
00384 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
00385 image->rows);
00386 if (proceed == MagickFalse)
00387 status=MagickFalse;
00388 }
00389 }
00390 blur_image->type=image->type;
00391 blur_view=DestroyCacheView(blur_view);
00392 edge_view=DestroyCacheView(edge_view);
00393 image_view=DestroyCacheView(image_view);
00394 edge_image=DestroyImage(edge_image);
00395 for (i=0; i < (long) width; i+=2)
00396 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00397 kernel=(double **) RelinquishMagickMemory(kernel);
00398 if (status == MagickFalse)
00399 blur_image=DestroyImage(blur_image);
00400 return(blur_image);
00401 }
00402
00403
00404
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 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
00444 const double sigma,ExceptionInfo *exception)
00445 {
00446 Image
00447 *sharp_image;
00448
00449 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
00450 exception);
00451 return(sharp_image);
00452 }
00453
00454 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
00455 const ChannelType channel,const double radius,const double sigma,
00456 ExceptionInfo *exception)
00457 {
00458 #define AdaptiveSharpenImageTag "Convolve/Image"
00459 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00460
00461 double
00462 **kernel;
00463
00464 Image
00465 *sharp_image,
00466 *edge_image,
00467 *gaussian_image;
00468
00469 long
00470 j,
00471 progress,
00472 y;
00473
00474 MagickBooleanType
00475 status;
00476
00477 MagickPixelPacket
00478 bias;
00479
00480 MagickRealType
00481 alpha,
00482 normalize;
00483
00484 register long
00485 i,
00486 u,
00487 v;
00488
00489 unsigned long
00490 width;
00491
00492 CacheView
00493 *sharp_view,
00494 *edge_view,
00495 *image_view;
00496
00497 assert(image != (const Image *) NULL);
00498 assert(image->signature == MagickSignature);
00499 if (image->debug != MagickFalse)
00500 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00501 assert(exception != (ExceptionInfo *) NULL);
00502 assert(exception->signature == MagickSignature);
00503 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
00504 if (sharp_image == (Image *) NULL)
00505 return((Image *) NULL);
00506 if (fabs(sigma) <= MagickEpsilon)
00507 return(sharp_image);
00508 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
00509 {
00510 InheritException(exception,&sharp_image->exception);
00511 sharp_image=DestroyImage(sharp_image);
00512 return((Image *) NULL);
00513 }
00514
00515
00516
00517 edge_image=EdgeImage(image,radius,exception);
00518 if (edge_image == (Image *) NULL)
00519 {
00520 sharp_image=DestroyImage(sharp_image);
00521 return((Image *) NULL);
00522 }
00523 (void) LevelImage(edge_image,"20%,95%");
00524 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00525 if (gaussian_image != (Image *) NULL)
00526 {
00527 edge_image=DestroyImage(edge_image);
00528 edge_image=gaussian_image;
00529 }
00530 (void) LevelImage(edge_image,"10%,95%");
00531
00532
00533
00534 width=GetOptimalKernelWidth2D(radius,sigma);
00535 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00536 if (kernel == (double **) NULL)
00537 {
00538 edge_image=DestroyImage(edge_image);
00539 sharp_image=DestroyImage(sharp_image);
00540 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00541 }
00542 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00543 for (i=0; i < (long) width; i+=2)
00544 {
00545 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00546 sizeof(**kernel));
00547 if (kernel[i] == (double *) NULL)
00548 break;
00549 j=0;
00550 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00551 {
00552 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00553 {
00554 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00555 kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00556 j++;
00557 }
00558 }
00559 normalize=0.0;
00560 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00561 normalize+=kernel[i][j];
00562 if (fabs(normalize) <= MagickEpsilon)
00563 normalize=1.0;
00564 normalize=1.0/normalize;
00565 for (j=0; j < (long) ((width-i)*(width-i)); j++)
00566 kernel[i][j]=(double) (normalize*kernel[i][j]);
00567 }
00568 if (i < (long) width)
00569 {
00570 for (i-=2; i >= 0; i-=2)
00571 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00572 kernel=(double **) RelinquishMagickMemory(kernel);
00573 edge_image=DestroyImage(edge_image);
00574 sharp_image=DestroyImage(sharp_image);
00575 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00576 }
00577
00578
00579
00580 status=MagickTrue;
00581 progress=0;
00582 GetMagickPixelPacket(image,&bias);
00583 SetMagickPixelPacketBias(image,&bias);
00584 image_view=AcquireCacheView(image);
00585 edge_view=AcquireCacheView(edge_image);
00586 sharp_view=AcquireCacheView(sharp_image);
00587 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00588 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00589 #endif
00590 for (y=0; y < (long) sharp_image->rows; y++)
00591 {
00592 register const IndexPacket
00593 *__restrict indexes;
00594
00595 register const PixelPacket
00596 *__restrict p,
00597 *__restrict r;
00598
00599 register IndexPacket
00600 *__restrict sharp_indexes;
00601
00602 register long
00603 x;
00604
00605 register PixelPacket
00606 *__restrict q;
00607
00608 if (status == MagickFalse)
00609 continue;
00610 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
00611 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
00612 exception);
00613 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00614 {
00615 status=MagickFalse;
00616 continue;
00617 }
00618 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
00619 for (x=0; x < (long) sharp_image->columns; x++)
00620 {
00621 MagickPixelPacket
00622 pixel;
00623
00624 MagickRealType
00625 alpha,
00626 gamma;
00627
00628 register const double
00629 *__restrict k;
00630
00631 register long
00632 i,
00633 u,
00634 v;
00635
00636 gamma=0.0;
00637 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
00638 if (i < 0)
00639 i=0;
00640 else
00641 if (i > (long) width)
00642 i=(long) width;
00643 if ((i & 0x01) != 0)
00644 i--;
00645 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
00646 ((width-i)/2L),width-i,width-i,exception);
00647 if (p == (const PixelPacket *) NULL)
00648 break;
00649 indexes=GetCacheViewVirtualIndexQueue(image_view);
00650 k=kernel[i];
00651 pixel=bias;
00652 for (v=0; v < (long) (width-i); v++)
00653 {
00654 for (u=0; u < (long) (width-i); u++)
00655 {
00656 alpha=1.0;
00657 if (((channel & OpacityChannel) != 0) &&
00658 (image->matte != MagickFalse))
00659 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00660 if ((channel & RedChannel) != 0)
00661 pixel.red+=(*k)*alpha*p->red;
00662 if ((channel & GreenChannel) != 0)
00663 pixel.green+=(*k)*alpha*p->green;
00664 if ((channel & BlueChannel) != 0)
00665 pixel.blue+=(*k)*alpha*p->blue;
00666 if ((channel & OpacityChannel) != 0)
00667 pixel.opacity+=(*k)*p->opacity;
00668 if (((channel & IndexChannel) != 0) &&
00669 (image->colorspace == CMYKColorspace))
00670 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00671 gamma+=(*k)*alpha;
00672 k++;
00673 p++;
00674 }
00675 }
00676 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00677 if ((channel & RedChannel) != 0)
00678 q->red=RoundToQuantum(gamma*pixel.red);
00679 if ((channel & GreenChannel) != 0)
00680 q->green=RoundToQuantum(gamma*pixel.green);
00681 if ((channel & BlueChannel) != 0)
00682 q->blue=RoundToQuantum(gamma*pixel.blue);
00683 if ((channel & OpacityChannel) != 0)
00684 q->opacity=RoundToQuantum(pixel.opacity);
00685 if (((channel & IndexChannel) != 0) &&
00686 (image->colorspace == CMYKColorspace))
00687 sharp_indexes[x]=RoundToQuantum(gamma*pixel.index);
00688 q++;
00689 r++;
00690 }
00691 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
00692 status=MagickFalse;
00693 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00694 {
00695 MagickBooleanType
00696 proceed;
00697
00698 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00699 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
00700 #endif
00701 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
00702 image->rows);
00703 if (proceed == MagickFalse)
00704 status=MagickFalse;
00705 }
00706 }
00707 sharp_image->type=image->type;
00708 sharp_view=DestroyCacheView(sharp_view);
00709 edge_view=DestroyCacheView(edge_view);
00710 image_view=DestroyCacheView(image_view);
00711 edge_image=DestroyImage(edge_image);
00712 for (i=0; i < (long) width; i+=2)
00713 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00714 kernel=(double **) RelinquishMagickMemory(kernel);
00715 if (status == MagickFalse)
00716 sharp_image=DestroyImage(sharp_image);
00717 return(sharp_image);
00718 }
00719
00720
00721
00722
00723
00724
00725
00726
00727
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 MagickExport Image *BlurImage(const Image *image,const double radius,
00763 const double sigma,ExceptionInfo *exception)
00764 {
00765 Image
00766 *blur_image;
00767
00768 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
00769 return(blur_image);
00770 }
00771
00772 static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
00773 {
00774 #define KernelRank 3
00775
00776 double
00777 *kernel;
00778
00779 long
00780 bias;
00781
00782 MagickRealType
00783 alpha,
00784 normalize;
00785
00786 register long
00787 i;
00788
00789
00790
00791
00792 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00793 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00794 if (kernel == (double *) NULL)
00795 return(0);
00796 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00797 bias=KernelRank*(long) width/2;
00798 for (i=(-bias); i <= bias; i++)
00799 {
00800 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
00801 MagickSigma*MagickSigma)));
00802 kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
00803 }
00804 normalize=0.0;
00805 for (i=0; i < (long) width; i++)
00806 normalize+=kernel[i];
00807 for (i=0; i < (long) width; i++)
00808 kernel[i]/=normalize;
00809 return(kernel);
00810 }
00811
00812 MagickExport Image *BlurImageChannel(const Image *image,
00813 const ChannelType channel,const double radius,const double sigma,
00814 ExceptionInfo *exception)
00815 {
00816 #define BlurImageTag "Blur/Image"
00817
00818 double
00819 *kernel;
00820
00821 Image
00822 *blur_image;
00823
00824 long
00825 progress,
00826 x,
00827 y;
00828
00829 MagickBooleanType
00830 status;
00831
00832 MagickPixelPacket
00833 bias;
00834
00835 register long
00836 i;
00837
00838 unsigned long
00839 width;
00840
00841 CacheView
00842 *blur_view,
00843 *image_view;
00844
00845
00846
00847
00848 assert(image != (Image *) NULL);
00849 assert(image->signature == MagickSignature);
00850 if (image->debug != MagickFalse)
00851 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00852 assert(exception != (ExceptionInfo *) NULL);
00853 assert(exception->signature == MagickSignature);
00854 blur_image=CloneImage(image,0,0,MagickTrue,exception);
00855 if (blur_image == (Image *) NULL)
00856 return((Image *) NULL);
00857 if (fabs(sigma) <= MagickEpsilon)
00858 return(blur_image);
00859 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00860 {
00861 InheritException(exception,&blur_image->exception);
00862 blur_image=DestroyImage(blur_image);
00863 return((Image *) NULL);
00864 }
00865 width=GetOptimalKernelWidth1D(radius,sigma);
00866 kernel=GetBlurKernel(width,sigma);
00867 if (kernel == (double *) NULL)
00868 {
00869 blur_image=DestroyImage(blur_image);
00870 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00871 }
00872 if (image->debug != MagickFalse)
00873 {
00874 char
00875 format[MaxTextExtent],
00876 *message;
00877
00878 register const double
00879 *k;
00880
00881 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00882 " BlurImage with %ld kernel:",width);
00883 message=AcquireString("");
00884 k=kernel;
00885 for (i=0; i < (long) width; i++)
00886 {
00887 *message='\0';
00888 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
00889 (void) ConcatenateString(&message,format);
00890 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
00891 (void) ConcatenateString(&message,format);
00892 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00893 }
00894 message=DestroyString(message);
00895 }
00896
00897
00898
00899 status=MagickTrue;
00900 progress=0;
00901 GetMagickPixelPacket(image,&bias);
00902 SetMagickPixelPacketBias(image,&bias);
00903 image_view=AcquireCacheView(image);
00904 blur_view=AcquireCacheView(blur_image);
00905 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00906 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00907 #endif
00908 for (y=0; y < (long) blur_image->rows; y++)
00909 {
00910 register const IndexPacket
00911 *__restrict indexes;
00912
00913 register const PixelPacket
00914 *__restrict p;
00915
00916 register IndexPacket
00917 *__restrict blur_indexes;
00918
00919 register long
00920 x;
00921
00922 register PixelPacket
00923 *__restrict q;
00924
00925 if (status == MagickFalse)
00926 continue;
00927 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
00928 width,1,exception);
00929 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
00930 exception);
00931 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00932 {
00933 status=MagickFalse;
00934 continue;
00935 }
00936 indexes=GetCacheViewVirtualIndexQueue(image_view);
00937 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
00938 for (x=0; x < (long) blur_image->columns; x++)
00939 {
00940 MagickPixelPacket
00941 pixel;
00942
00943 register const double
00944 *__restrict k;
00945
00946 register const PixelPacket
00947 *__restrict kernel_pixels;
00948
00949 register long
00950 i;
00951
00952 pixel=bias;
00953 k=kernel;
00954 kernel_pixels=p;
00955 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
00956 {
00957 for (i=0; i < (long) width; i++)
00958 {
00959 pixel.red+=(*k)*kernel_pixels->red;
00960 pixel.green+=(*k)*kernel_pixels->green;
00961 pixel.blue+=(*k)*kernel_pixels->blue;
00962 k++;
00963 kernel_pixels++;
00964 }
00965 if ((channel & RedChannel) != 0)
00966 q->red=RoundToQuantum(pixel.red);
00967 if ((channel & GreenChannel) != 0)
00968 q->green=RoundToQuantum(pixel.green);
00969 if ((channel & BlueChannel) != 0)
00970 q->blue=RoundToQuantum(pixel.blue);
00971 if ((channel & OpacityChannel) != 0)
00972 {
00973 k=kernel;
00974 kernel_pixels=p;
00975 for (i=0; i < (long) width; i++)
00976 {
00977 pixel.opacity+=(*k)*kernel_pixels->opacity;
00978 k++;
00979 kernel_pixels++;
00980 }
00981 q->opacity=RoundToQuantum(pixel.opacity);
00982 }
00983 if (((channel & IndexChannel) != 0) &&
00984 (image->colorspace == CMYKColorspace))
00985 {
00986 register const IndexPacket
00987 *__restrict kernel_indexes;
00988
00989 k=kernel;
00990 kernel_indexes=indexes;
00991 for (i=0; i < (long) width; i++)
00992 {
00993 pixel.index+=(*k)*(*kernel_indexes);
00994 k++;
00995 kernel_indexes++;
00996 }
00997 blur_indexes[x]=RoundToQuantum(pixel.index);
00998 }
00999 }
01000 else
01001 {
01002 MagickRealType
01003 alpha,
01004 gamma;
01005
01006 gamma=0.0;
01007 for (i=0; i < (long) width; i++)
01008 {
01009 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01010 kernel_pixels->opacity));
01011 pixel.red+=(*k)*alpha*kernel_pixels->red;
01012 pixel.green+=(*k)*alpha*kernel_pixels->green;
01013 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
01014 gamma+=(*k)*alpha;
01015 k++;
01016 kernel_pixels++;
01017 }
01018 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01019 if ((channel & RedChannel) != 0)
01020 q->red=RoundToQuantum(gamma*pixel.red);
01021 if ((channel & GreenChannel) != 0)
01022 q->green=RoundToQuantum(gamma*pixel.green);
01023 if ((channel & BlueChannel) != 0)
01024 q->blue=RoundToQuantum(gamma*pixel.blue);
01025 if ((channel & OpacityChannel) != 0)
01026 {
01027 k=kernel;
01028 kernel_pixels=p;
01029 for (i=0; i < (long) width; i++)
01030 {
01031 pixel.opacity+=(*k)*kernel_pixels->opacity;
01032 k++;
01033 kernel_pixels++;
01034 }
01035 q->opacity=RoundToQuantum(pixel.opacity);
01036 }
01037 if (((channel & IndexChannel) != 0) &&
01038 (image->colorspace == CMYKColorspace))
01039 {
01040 register const IndexPacket
01041 *__restrict kernel_indexes;
01042
01043 k=kernel;
01044 kernel_pixels=p;
01045 kernel_indexes=indexes;
01046 for (i=0; i < (long) width; i++)
01047 {
01048 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01049 kernel_pixels->opacity));
01050 pixel.index+=(*k)*alpha*(*kernel_indexes);
01051 k++;
01052 kernel_pixels++;
01053 kernel_indexes++;
01054 }
01055 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
01056 }
01057 }
01058 p++;
01059 q++;
01060 }
01061 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
01062 status=MagickFalse;
01063 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01064 {
01065 MagickBooleanType
01066 proceed;
01067
01068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01069 #pragma omp critical (MagickCore_BlurImageChannel)
01070 #endif
01071 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01072 blur_image->columns);
01073 if (proceed == MagickFalse)
01074 status=MagickFalse;
01075 }
01076 }
01077 blur_view=DestroyCacheView(blur_view);
01078 image_view=DestroyCacheView(image_view);
01079
01080
01081
01082 image_view=AcquireCacheView(blur_image);
01083 blur_view=AcquireCacheView(blur_image);
01084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01085 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01086 #endif
01087 for (x=0; x < (long) blur_image->columns; x++)
01088 {
01089 register const IndexPacket
01090 *__restrict indexes;
01091
01092 register const PixelPacket
01093 *__restrict p;
01094
01095 register IndexPacket
01096 *__restrict blur_indexes;
01097
01098 register long
01099 y;
01100
01101 register PixelPacket
01102 *__restrict q;
01103
01104 if (status == MagickFalse)
01105 continue;
01106 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
01107 width,exception);
01108 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
01109 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01110 {
01111 status=MagickFalse;
01112 continue;
01113 }
01114 indexes=GetCacheViewVirtualIndexQueue(image_view);
01115 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
01116 for (y=0; y < (long) blur_image->rows; y++)
01117 {
01118 MagickPixelPacket
01119 pixel;
01120
01121 register const double
01122 *__restrict k;
01123
01124 register const PixelPacket
01125 *__restrict kernel_pixels;
01126
01127 register long
01128 i;
01129
01130 pixel=bias;
01131 k=kernel;
01132 kernel_pixels=p;
01133 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
01134 {
01135 for (i=0; i < (long) width; i++)
01136 {
01137 pixel.red+=(*k)*kernel_pixels->red;
01138 pixel.green+=(*k)*kernel_pixels->green;
01139 pixel.blue+=(*k)*kernel_pixels->blue;
01140 k++;
01141 kernel_pixels++;
01142 }
01143 if ((channel & RedChannel) != 0)
01144 q->red=RoundToQuantum(pixel.red);
01145 if ((channel & GreenChannel) != 0)
01146 q->green=RoundToQuantum(pixel.green);
01147 if ((channel & BlueChannel) != 0)
01148 q->blue=RoundToQuantum(pixel.blue);
01149 if ((channel & OpacityChannel) != 0)
01150 {
01151 k=kernel;
01152 kernel_pixels=p;
01153 for (i=0; i < (long) width; i++)
01154 {
01155 pixel.opacity+=(*k)*kernel_pixels->opacity;
01156 k++;
01157 kernel_pixels++;
01158 }
01159 q->opacity=RoundToQuantum(pixel.opacity);
01160 }
01161 if (((channel & IndexChannel) != 0) &&
01162 (image->colorspace == CMYKColorspace))
01163 {
01164 register const IndexPacket
01165 *__restrict kernel_indexes;
01166
01167 k=kernel;
01168 kernel_indexes=indexes;
01169 for (i=0; i < (long) width; i++)
01170 {
01171 pixel.index+=(*k)*(*kernel_indexes);
01172 k++;
01173 kernel_indexes++;
01174 }
01175 blur_indexes[y]=RoundToQuantum(pixel.index);
01176 }
01177 }
01178 else
01179 {
01180 MagickRealType
01181 alpha,
01182 gamma;
01183
01184 gamma=0.0;
01185 for (i=0; i < (long) width; i++)
01186 {
01187 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01188 kernel_pixels->opacity));
01189 pixel.red+=(*k)*alpha*kernel_pixels->red;
01190 pixel.green+=(*k)*alpha*kernel_pixels->green;
01191 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
01192 gamma+=(*k)*alpha;
01193 k++;
01194 kernel_pixels++;
01195 }
01196 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01197 if ((channel & RedChannel) != 0)
01198 q->red=RoundToQuantum(gamma*pixel.red);
01199 if ((channel & GreenChannel) != 0)
01200 q->green=RoundToQuantum(gamma*pixel.green);
01201 if ((channel & BlueChannel) != 0)
01202 q->blue=RoundToQuantum(gamma*pixel.blue);
01203 if ((channel & OpacityChannel) != 0)
01204 {
01205 k=kernel;
01206 kernel_pixels=p;
01207 for (i=0; i < (long) width; i++)
01208 {
01209 pixel.opacity+=(*k)*kernel_pixels->opacity;
01210 k++;
01211 kernel_pixels++;
01212 }
01213 q->opacity=RoundToQuantum(pixel.opacity);
01214 }
01215 if (((channel & IndexChannel) != 0) &&
01216 (image->colorspace == CMYKColorspace))
01217 {
01218 register const IndexPacket
01219 *__restrict kernel_indexes;
01220
01221 k=kernel;
01222 kernel_pixels=p;
01223 kernel_indexes=indexes;
01224 for (i=0; i < (long) width; i++)
01225 {
01226 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01227 kernel_pixels->opacity));
01228 pixel.index+=(*k)*alpha*(*kernel_indexes);
01229 k++;
01230 kernel_pixels++;
01231 kernel_indexes++;
01232 }
01233 blur_indexes[y]=RoundToQuantum(gamma*pixel.index);
01234 }
01235 }
01236 p++;
01237 q++;
01238 }
01239 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
01240 status=MagickFalse;
01241 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01242 {
01243 MagickBooleanType
01244 proceed;
01245
01246 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01247 #pragma omp critical (MagickCore_BlurImageChannel)
01248 #endif
01249 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01250 blur_image->columns);
01251 if (proceed == MagickFalse)
01252 status=MagickFalse;
01253 }
01254 }
01255 blur_view=DestroyCacheView(blur_view);
01256 image_view=DestroyCacheView(image_view);
01257 kernel=(double *) RelinquishMagickMemory(kernel);
01258 if (status == MagickFalse)
01259 blur_image=DestroyImage(blur_image);
01260 blur_image->type=image->type;
01261 return(blur_image);
01262 }
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
01291 {
01292 register long
01293 i;
01294
01295 assert(pixels != (Quantum **) NULL);
01296 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
01297 if (pixels[i] != (Quantum *) NULL)
01298 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
01299 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
01300 return(pixels);
01301 }
01302
01303 static Quantum **AcquirePixelThreadSet(const size_t count)
01304 {
01305 register long
01306 i;
01307
01308 Quantum
01309 **pixels;
01310
01311 unsigned long
01312 number_threads;
01313
01314 number_threads=GetOpenMPMaximumThreads();
01315 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
01316 if (pixels == (Quantum **) NULL)
01317 return((Quantum **) NULL);
01318 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
01319 for (i=0; i < (long) number_threads; i++)
01320 {
01321 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
01322 if (pixels[i] == (Quantum *) NULL)
01323 return(DestroyPixelThreadSet(pixels));
01324 }
01325 return(pixels);
01326 }
01327
01328 static void Hull(const long x_offset,const long y_offset,
01329 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
01330 const int polarity)
01331 {
01332 long
01333 y;
01334
01335 MagickRealType
01336 v;
01337
01338 register long
01339 x;
01340
01341 register Quantum
01342 *p,
01343 *q,
01344 *r,
01345 *s;
01346
01347 assert(f != (Quantum *) NULL);
01348 assert(g != (Quantum *) NULL);
01349 p=f+(columns+2);
01350 q=g+(columns+2);
01351 r=p+(y_offset*((long) columns+2)+x_offset);
01352 for (y=0; y < (long) rows; y++)
01353 {
01354 p++;
01355 q++;
01356 r++;
01357 if (polarity > 0)
01358 for (x=(long) columns; x != 0; x--)
01359 {
01360 v=(MagickRealType) (*p);
01361 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
01362 v+=ScaleCharToQuantum(1);
01363 *q=(Quantum) v;
01364 p++;
01365 q++;
01366 r++;
01367 }
01368 else
01369 for (x=(long) columns; x != 0; x--)
01370 {
01371 v=(MagickRealType) (*p);
01372 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
01373 v-=(long) ScaleCharToQuantum(1);
01374 *q=(Quantum) v;
01375 p++;
01376 q++;
01377 r++;
01378 }
01379 p++;
01380 q++;
01381 r++;
01382 }
01383 p=f+(columns+2);
01384 q=g+(columns+2);
01385 r=q+(y_offset*((long) columns+2)+x_offset);
01386 s=q-(y_offset*((long) columns+2)+x_offset);
01387 for (y=0; y < (long) rows; y++)
01388 {
01389 p++;
01390 q++;
01391 r++;
01392 s++;
01393 if (polarity > 0)
01394 for (x=(long) columns; x != 0; x--)
01395 {
01396 v=(MagickRealType) (*q);
01397 if (((MagickRealType) *s >=
01398 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
01399 ((MagickRealType) *r > v))
01400 v+=ScaleCharToQuantum(1);
01401 *p=(Quantum) v;
01402 p++;
01403 q++;
01404 r++;
01405 s++;
01406 }
01407 else
01408 for (x=(long) columns; x != 0; x--)
01409 {
01410 v=(MagickRealType) (*q);
01411 if (((MagickRealType) *s <=
01412 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
01413 ((MagickRealType) *r < v))
01414 v-=(MagickRealType) ScaleCharToQuantum(1);
01415 *p=(Quantum) v;
01416 p++;
01417 q++;
01418 r++;
01419 s++;
01420 }
01421 p++;
01422 q++;
01423 r++;
01424 s++;
01425 }
01426 }
01427
01428 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
01429 {
01430 #define DespeckleImageTag "Despeckle/Image"
01431
01432 CacheView
01433 *despeckle_view,
01434 *image_view;
01435
01436 Image
01437 *despeckle_image;
01438
01439 long
01440 channel;
01441
01442 MagickBooleanType
01443 status;
01444
01445 Quantum
01446 **buffers,
01447 **pixels;
01448
01449 size_t
01450 length;
01451
01452 static const int
01453 X[4] = {0, 1, 1,-1},
01454 Y[4] = {1, 0, 1, 1};
01455
01456
01457
01458
01459 assert(image != (const Image *) NULL);
01460 assert(image->signature == MagickSignature);
01461 if (image->debug != MagickFalse)
01462 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01463 assert(exception != (ExceptionInfo *) NULL);
01464 assert(exception->signature == MagickSignature);
01465 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01466 exception);
01467 if (despeckle_image == (Image *) NULL)
01468 return((Image *) NULL);
01469 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
01470 {
01471 InheritException(exception,&despeckle_image->exception);
01472 despeckle_image=DestroyImage(despeckle_image);
01473 return((Image *) NULL);
01474 }
01475
01476
01477
01478 length=(size_t) ((image->columns+2)*(image->rows+2));
01479 pixels=AcquirePixelThreadSet(length);
01480 buffers=AcquirePixelThreadSet(length);
01481 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
01482 {
01483 if (buffers != (Quantum **) NULL)
01484 buffers=DestroyPixelThreadSet(buffers);
01485 if (pixels != (Quantum **) NULL)
01486 pixels=DestroyPixelThreadSet(pixels);
01487 despeckle_image=DestroyImage(despeckle_image);
01488 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01489 }
01490
01491
01492
01493 status=MagickTrue;
01494 image_view=AcquireCacheView(image);
01495 despeckle_view=AcquireCacheView(despeckle_image);
01496 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01497 #pragma omp parallel for schedule(dynamic,4) shared(status)
01498 #endif
01499 for (channel=0; channel <= 3; channel++)
01500 {
01501 long
01502 j,
01503 y;
01504
01505 register long
01506 i,
01507 id,
01508 x;
01509
01510 register Quantum
01511 *buffer,
01512 *pixel;
01513
01514 if (status == MagickFalse)
01515 continue;
01516 id=GetOpenMPThreadId();
01517 pixel=pixels[id];
01518 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
01519 buffer=buffers[id];
01520 j=(long) image->columns+2;
01521 for (y=0; y < (long) image->rows; y++)
01522 {
01523 register const PixelPacket
01524 *__restrict p;
01525
01526 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
01527 if (p == (const PixelPacket *) NULL)
01528 break;
01529 j++;
01530 for (x=0; x < (long) image->columns; x++)
01531 {
01532 switch (channel)
01533 {
01534 case 0: pixel[j]=p->red; break;
01535 case 1: pixel[j]=p->green; break;
01536 case 2: pixel[j]=p->blue; break;
01537 case 3: pixel[j]=p->opacity; break;
01538 default: break;
01539 }
01540 p++;
01541 j++;
01542 }
01543 j++;
01544 }
01545 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
01546 for (i=0; i < 4; i++)
01547 {
01548 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
01549 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
01550 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
01551 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
01552 }
01553 j=(long) image->columns+2;
01554 for (y=0; y < (long) image->rows; y++)
01555 {
01556 MagickBooleanType
01557 sync;
01558
01559 register PixelPacket
01560 *__restrict q;
01561
01562 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
01563 1,exception);
01564 if (q == (PixelPacket *) NULL)
01565 break;
01566 j++;
01567 for (x=0; x < (long) image->columns; x++)
01568 {
01569 switch (channel)
01570 {
01571 case 0: q->red=pixel[j]; break;
01572 case 1: q->green=pixel[j]; break;
01573 case 2: q->blue=pixel[j]; break;
01574 case 3: q->opacity=pixel[j]; break;
01575 default: break;
01576 }
01577 q++;
01578 j++;
01579 }
01580 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
01581 if (sync == MagickFalse)
01582 {
01583 status=MagickFalse;
01584 break;
01585 }
01586 j++;
01587 }
01588 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01589 {
01590 MagickBooleanType
01591 proceed;
01592
01593 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01594 #pragma omp critical (MagickCore_DespeckleImage)
01595 #endif
01596 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
01597 if (proceed == MagickFalse)
01598 status=MagickFalse;
01599 }
01600 }
01601 despeckle_view=DestroyCacheView(despeckle_view);
01602 image_view=DestroyCacheView(image_view);
01603 buffers=DestroyPixelThreadSet(buffers);
01604 pixels=DestroyPixelThreadSet(pixels);
01605 despeckle_image->type=image->type;
01606 if (status == MagickFalse)
01607 despeckle_image=DestroyImage(despeckle_image);
01608 return(despeckle_image);
01609 }
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640 MagickExport Image *EdgeImage(const Image *image,const double radius,
01641 ExceptionInfo *exception)
01642 {
01643 Image
01644 *edge_image;
01645
01646 double
01647 *kernel;
01648
01649 register long
01650 i;
01651
01652 unsigned long
01653 width;
01654
01655 assert(image != (const Image *) NULL);
01656 assert(image->signature == MagickSignature);
01657 if (image->debug != MagickFalse)
01658 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01659 assert(exception != (ExceptionInfo *) NULL);
01660 assert(exception->signature == MagickSignature);
01661 width=GetOptimalKernelWidth1D(radius,0.5);
01662 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
01663 if (kernel == (double *) NULL)
01664 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01665 for (i=0; i < (long) (width*width); i++)
01666 kernel[i]=(-1.0);
01667 kernel[i/2]=(double) (width*width-1.0);
01668 edge_image=ConvolveImage(image,width,kernel,exception);
01669 kernel=(double *) RelinquishMagickMemory(kernel);
01670 return(edge_image);
01671 }
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690
01691
01692
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705
01706 MagickExport Image *EmbossImage(const Image *image,const double radius,
01707 const double sigma,ExceptionInfo *exception)
01708 {
01709 double
01710 *kernel;
01711
01712 Image
01713 *emboss_image;
01714
01715 long
01716 j;
01717
01718 MagickRealType
01719 alpha;
01720
01721 register long
01722 i,
01723 u,
01724 v;
01725
01726 unsigned long
01727 width;
01728
01729 assert(image != (Image *) NULL);
01730 assert(image->signature == MagickSignature);
01731 if (image->debug != MagickFalse)
01732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01733 assert(exception != (ExceptionInfo *) NULL);
01734 assert(exception->signature == MagickSignature);
01735 width=GetOptimalKernelWidth2D(radius,sigma);
01736 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
01737 if (kernel == (double *) NULL)
01738 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01739 i=0;
01740 j=(long) width/2;
01741 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
01742 {
01743 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
01744 {
01745 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
01746 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
01747 (2.0*MagickPI*MagickSigma*MagickSigma));
01748 if (u != j)
01749 kernel[i]=0.0;
01750 i++;
01751 }
01752 j--;
01753 }
01754 emboss_image=ConvolveImage(image,width,kernel,exception);
01755 if (emboss_image != (Image *) NULL)
01756 (void) EqualizeImage(emboss_image);
01757 kernel=(double *) RelinquishMagickMemory(kernel);
01758 return(emboss_image);
01759 }
01760
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
01801 const double sigma,ExceptionInfo *exception)
01802 {
01803 Image
01804 *blur_image;
01805
01806 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
01807 exception);
01808 return(blur_image);
01809 }
01810
01811 MagickExport Image *GaussianBlurImageChannel(const Image *image,
01812 const ChannelType channel,const double radius,const double sigma,
01813 ExceptionInfo *exception)
01814 {
01815 double
01816 *kernel;
01817
01818 Image
01819 *blur_image;
01820
01821 MagickRealType
01822 alpha;
01823
01824 register long
01825 i,
01826 u,
01827 v;
01828
01829 unsigned long
01830 width;
01831
01832 assert(image != (const Image *) NULL);
01833 assert(image->signature == MagickSignature);
01834 if (image->debug != MagickFalse)
01835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01836 assert(exception != (ExceptionInfo *) NULL);
01837 assert(exception->signature == MagickSignature);
01838 width=GetOptimalKernelWidth2D(radius,sigma);
01839 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
01840 if (kernel == (double *) NULL)
01841 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01842 i=0;
01843 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
01844 {
01845 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
01846 {
01847 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
01848 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
01849 i++;
01850 }
01851 }
01852 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
01853 kernel=(double *) RelinquishMagickMemory(kernel);
01854 return(blur_image);
01855 }
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881
01882
01883
01884
01885
01886
01887
01888
01889
01890
01891
01892 #define MedianListChannels 5
01893
01894 typedef struct _MedianListNode
01895 {
01896 unsigned long
01897 next[9],
01898 count,
01899 signature;
01900 } MedianListNode;
01901
01902 typedef struct _MedianSkipList
01903 {
01904 long
01905 level;
01906
01907 MedianListNode
01908 *nodes;
01909 } MedianSkipList;
01910
01911 typedef struct _MedianPixelList
01912 {
01913 unsigned long
01914 center,
01915 seed,
01916 signature;
01917
01918 MedianSkipList
01919 lists[MedianListChannels];
01920 } MedianPixelList;
01921
01922 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
01923 {
01924 register long
01925 i;
01926
01927 if (pixel_list == (MedianPixelList *) NULL)
01928 return((MedianPixelList *) NULL);
01929 for (i=0; i < MedianListChannels; i++)
01930 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
01931 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
01932 pixel_list->lists[i].nodes);
01933 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
01934 return(pixel_list);
01935 }
01936
01937 static MedianPixelList **DestroyMedianPixelListThreadSet(
01938 MedianPixelList **pixel_list)
01939 {
01940 register long
01941 i;
01942
01943 assert(pixel_list != (MedianPixelList **) NULL);
01944 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
01945 if (pixel_list[i] != (MedianPixelList *) NULL)
01946 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
01947 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
01948 return(pixel_list);
01949 }
01950
01951 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
01952 {
01953 MedianPixelList
01954 *pixel_list;
01955
01956 register long
01957 i;
01958
01959 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
01960 if (pixel_list == (MedianPixelList *) NULL)
01961 return(pixel_list);
01962 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
01963 pixel_list->center=width*width/2;
01964 for (i=0; i < MedianListChannels; i++)
01965 {
01966 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
01967 sizeof(*pixel_list->lists[i].nodes));
01968 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
01969 return(DestroyMedianPixelList(pixel_list));
01970 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
01971 sizeof(*pixel_list->lists[i].nodes));
01972 }
01973 pixel_list->signature=MagickSignature;
01974 return(pixel_list);
01975 }
01976
01977 static MedianPixelList **AcquireMedianPixelListThreadSet(
01978 const unsigned long width)
01979 {
01980 register long
01981 i;
01982
01983 MedianPixelList
01984 **pixel_list;
01985
01986 unsigned long
01987 number_threads;
01988
01989 number_threads=GetOpenMPMaximumThreads();
01990 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
01991 sizeof(*pixel_list));
01992 if (pixel_list == (MedianPixelList **) NULL)
01993 return((MedianPixelList **) NULL);
01994 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
01995 for (i=0; i < (long) number_threads; i++)
01996 {
01997 pixel_list[i]=AcquireMedianPixelList(width);
01998 if (pixel_list[i] == (MedianPixelList *) NULL)
01999 return(DestroyMedianPixelListThreadSet(pixel_list));
02000 }
02001 return(pixel_list);
02002 }
02003
02004 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
02005 const long channel,const unsigned long color)
02006 {
02007 register long
02008 level;
02009
02010 register MedianSkipList
02011 *list;
02012
02013 unsigned long
02014 search,
02015 update[9];
02016
02017
02018
02019
02020 list=pixel_list->lists+channel;
02021 list->nodes[color].signature=pixel_list->signature;
02022 list->nodes[color].count=1;
02023
02024
02025
02026 search=65536UL;
02027 for (level=list->level; level >= 0; level--)
02028 {
02029 while (list->nodes[search].next[level] < color)
02030 search=list->nodes[search].next[level];
02031 update[level]=search;
02032 }
02033
02034
02035
02036 for (level=0; ; level++)
02037 {
02038 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
02039 if ((pixel_list->seed & 0x300) != 0x300)
02040 break;
02041 }
02042 if (level > 8)
02043 level=8;
02044 if (level > (list->level+2))
02045 level=list->level+2;
02046
02047
02048
02049 while (level > list->level)
02050 {
02051 list->level++;
02052 update[list->level]=65536UL;
02053 }
02054
02055
02056
02057 do
02058 {
02059 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
02060 list->nodes[update[level]].next[level]=color;
02061 }
02062 while (level-- > 0);
02063 }
02064
02065 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
02066 {
02067 MagickPixelPacket
02068 pixel;
02069
02070 register long
02071 channel;
02072
02073 register MedianSkipList
02074 *list;
02075
02076 unsigned long
02077 center,
02078 color,
02079 count;
02080
02081 unsigned short
02082 channels[MedianListChannels];
02083
02084
02085
02086
02087 center=pixel_list->center;
02088 for (channel=0; channel < 5; channel++)
02089 {
02090 list=pixel_list->lists+channel;
02091 color=65536UL;
02092 count=0;
02093 do
02094 {
02095 color=list->nodes[color].next[0];
02096 count+=list->nodes[color].count;
02097 }
02098 while (count <= center);
02099 channels[channel]=(unsigned short) color;
02100 }
02101 GetMagickPixelPacket((const Image *) NULL,&pixel);
02102 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
02103 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
02104 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
02105 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
02106 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
02107 return(pixel);
02108 }
02109
02110 static inline void InsertMedianPixelList(const Image *image,
02111 const PixelPacket *pixel,const IndexPacket *indexes,
02112 MedianPixelList *pixel_list)
02113 {
02114 unsigned long
02115 signature;
02116
02117 unsigned short
02118 index;
02119
02120 index=ScaleQuantumToShort(pixel->red);
02121 signature=pixel_list->lists[0].nodes[index].signature;
02122 if (signature == pixel_list->signature)
02123 pixel_list->lists[0].nodes[index].count++;
02124 else
02125 AddNodeMedianPixelList(pixel_list,0,index);
02126 index=ScaleQuantumToShort(pixel->green);
02127 signature=pixel_list->lists[1].nodes[index].signature;
02128 if (signature == pixel_list->signature)
02129 pixel_list->lists[1].nodes[index].count++;
02130 else
02131 AddNodeMedianPixelList(pixel_list,1,index);
02132 index=ScaleQuantumToShort(pixel->blue);
02133 signature=pixel_list->lists[2].nodes[index].signature;
02134 if (signature == pixel_list->signature)
02135 pixel_list->lists[2].nodes[index].count++;
02136 else
02137 AddNodeMedianPixelList(pixel_list,2,index);
02138 index=ScaleQuantumToShort(pixel->opacity);
02139 signature=pixel_list->lists[3].nodes[index].signature;
02140 if (signature == pixel_list->signature)
02141 pixel_list->lists[3].nodes[index].count++;
02142 else
02143 AddNodeMedianPixelList(pixel_list,3,index);
02144 if (image->colorspace == CMYKColorspace)
02145 index=ScaleQuantumToShort(*indexes);
02146 signature=pixel_list->lists[4].nodes[index].signature;
02147 if (signature == pixel_list->signature)
02148 pixel_list->lists[4].nodes[index].count++;
02149 else
02150 AddNodeMedianPixelList(pixel_list,4,index);
02151 }
02152
02153 static void ResetMedianPixelList(MedianPixelList *pixel_list)
02154 {
02155 int
02156 level;
02157
02158 register long
02159 channel;
02160
02161 register MedianListNode
02162 *root;
02163
02164 register MedianSkipList
02165 *list;
02166
02167
02168
02169
02170 for (channel=0; channel < 5; channel++)
02171 {
02172 list=pixel_list->lists+channel;
02173 root=list->nodes+65536UL;
02174 list->level=0;
02175 for (level=0; level < 9; level++)
02176 root->next[level]=65536UL;
02177 }
02178 pixel_list->seed=pixel_list->signature++;
02179 }
02180
02181 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
02182 ExceptionInfo *exception)
02183 {
02184 #define MedianFilterImageTag "MedianFilter/Image"
02185
02186 Image
02187 *median_image;
02188
02189 long
02190 progress,
02191 y;
02192
02193 MagickBooleanType
02194 status;
02195
02196 MedianPixelList
02197 **pixel_list;
02198
02199 unsigned long
02200 width;
02201
02202 CacheView
02203 *image_view,
02204 *median_view;
02205
02206
02207
02208
02209 assert(image != (Image *) NULL);
02210 assert(image->signature == MagickSignature);
02211 if (image->debug != MagickFalse)
02212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02213 assert(exception != (ExceptionInfo *) NULL);
02214 assert(exception->signature == MagickSignature);
02215 width=GetOptimalKernelWidth2D(radius,0.5);
02216 if ((image->columns < width) || (image->rows < width))
02217 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
02218 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
02219 exception);
02220 if (median_image == (Image *) NULL)
02221 return((Image *) NULL);
02222 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
02223 {
02224 InheritException(exception,&median_image->exception);
02225 median_image=DestroyImage(median_image);
02226 return((Image *) NULL);
02227 }
02228 pixel_list=AcquireMedianPixelListThreadSet(width);
02229 if (pixel_list == (MedianPixelList **) NULL)
02230 {
02231 median_image=DestroyImage(median_image);
02232 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02233 }
02234
02235
02236
02237 status=MagickTrue;
02238 progress=0;
02239 image_view=AcquireCacheView(image);
02240 median_view=AcquireCacheView(median_image);
02241 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02242 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02243 #endif
02244 for (y=0; y < (long) median_image->rows; y++)
02245 {
02246 register const IndexPacket
02247 *__restrict indexes;
02248
02249 register const PixelPacket
02250 *__restrict p;
02251
02252 register IndexPacket
02253 *__restrict median_indexes;
02254
02255 register long
02256 id,
02257 x;
02258
02259 register PixelPacket
02260 *__restrict q;
02261
02262 if (status == MagickFalse)
02263 continue;
02264 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
02265 2L),image->columns+width,width,exception);
02266 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
02267 exception);
02268 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
02269 {
02270 status=MagickFalse;
02271 continue;
02272 }
02273 indexes=GetCacheViewVirtualIndexQueue(image_view);
02274 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
02275 id=GetOpenMPThreadId();
02276 for (x=0; x < (long) median_image->columns; x++)
02277 {
02278 MagickPixelPacket
02279 pixel;
02280
02281 register const PixelPacket
02282 *__restrict r;
02283
02284 register const IndexPacket
02285 *__restrict s;
02286
02287 register long
02288 u,
02289 v;
02290
02291 r=p;
02292 s=indexes+x;
02293 ResetMedianPixelList(pixel_list[id]);
02294 for (v=0; v < (long) width; v++)
02295 {
02296 for (u=0; u < (long) width; u++)
02297 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
02298 r+=image->columns+width;
02299 s+=image->columns+width;
02300 }
02301 pixel=GetMedianPixelList(pixel_list[id]);
02302 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
02303 p++;
02304 q++;
02305 }
02306 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
02307 status=MagickFalse;
02308 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02309 {
02310 MagickBooleanType
02311 proceed;
02312
02313 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02314 #pragma omp critical (MagickCore_MedianFilterImage)
02315 #endif
02316 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
02317 image->rows);
02318 if (proceed == MagickFalse)
02319 status=MagickFalse;
02320 }
02321 }
02322 median_view=DestroyCacheView(median_view);
02323 image_view=DestroyCacheView(image_view);
02324 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
02325 return(median_image);
02326 }
02327
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372
02373 static double *GetMotionBlurKernel(unsigned long width,
02374 const MagickRealType sigma)
02375 {
02376 #define KernelRank 3
02377
02378 double
02379 *kernel;
02380
02381 long
02382 bias;
02383
02384 MagickRealType
02385 alpha,
02386 normalize;
02387
02388 register long
02389 i;
02390
02391
02392
02393
02394 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02395 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
02396 if (kernel == (double *) NULL)
02397 return(kernel);
02398 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
02399 bias=(long) (KernelRank*width);
02400 for (i=0; i < (long) bias; i++)
02401 {
02402 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
02403 MagickSigma*MagickSigma)));
02404 kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
02405 }
02406 normalize=0.0;
02407 for (i=0; i < (long) width; i++)
02408 normalize+=kernel[i];
02409 for (i=0; i < (long) width; i++)
02410 kernel[i]/=normalize;
02411 return(kernel);
02412 }
02413
02414 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
02415 const double sigma,const double angle,ExceptionInfo *exception)
02416 {
02417 Image
02418 *motion_blur;
02419
02420 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
02421 exception);
02422 return(motion_blur);
02423 }
02424
02425 MagickExport Image *MotionBlurImageChannel(const Image *image,
02426 const ChannelType channel,const double radius,const double sigma,
02427 const double angle,ExceptionInfo *exception)
02428 {
02429 typedef struct _OffsetInfo
02430 {
02431 long
02432 x,
02433 y;
02434 } OffsetInfo;
02435
02436 double
02437 *kernel;
02438
02439 Image
02440 *blur_image;
02441
02442 long
02443 progress,
02444 y;
02445
02446 MagickBooleanType
02447 status;
02448
02449 MagickPixelPacket
02450 bias;
02451
02452 OffsetInfo
02453 *offset;
02454
02455 PointInfo
02456 point;
02457
02458 register long
02459 i;
02460
02461 unsigned long
02462 width;
02463
02464 CacheView
02465 *blur_view,
02466 *image_view;
02467
02468 assert(image != (Image *) NULL);
02469 assert(image->signature == MagickSignature);
02470 if (image->debug != MagickFalse)
02471 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02472 assert(exception != (ExceptionInfo *) NULL);
02473 width=GetOptimalKernelWidth1D(radius,sigma);
02474 kernel=GetMotionBlurKernel(width,sigma);
02475 if (kernel == (double *) NULL)
02476 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02477 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
02478 if (offset == (OffsetInfo *) NULL)
02479 {
02480 kernel=(double *) RelinquishMagickMemory(kernel);
02481 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02482 }
02483 blur_image=CloneImage(image,0,0,MagickTrue,exception);
02484 if (blur_image == (Image *) NULL)
02485 {
02486 kernel=(double *) RelinquishMagickMemory(kernel);
02487 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02488 return((Image *) NULL);
02489 }
02490 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
02491 {
02492 kernel=(double *) RelinquishMagickMemory(kernel);
02493 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02494 InheritException(exception,&blur_image->exception);
02495 blur_image=DestroyImage(blur_image);
02496 return((Image *) NULL);
02497 }
02498 point.x=(double) width*sin(DegreesToRadians(angle));
02499 point.y=(double) width*cos(DegreesToRadians(angle));
02500 for (i=0; i < (long) width; i++)
02501 {
02502 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
02503 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
02504 }
02505
02506
02507
02508 status=MagickTrue;
02509 progress=0;
02510 GetMagickPixelPacket(image,&bias);
02511 image_view=AcquireCacheView(image);
02512 blur_view=AcquireCacheView(blur_image);
02513 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02514 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02515 #endif
02516 for (y=0; y < (long) image->rows; y++)
02517 {
02518 register IndexPacket
02519 *__restrict blur_indexes;
02520
02521 register long
02522 x;
02523
02524 register PixelPacket
02525 *__restrict q;
02526
02527 if (status == MagickFalse)
02528 continue;
02529 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
02530 exception);
02531 if (q == (PixelPacket *) NULL)
02532 {
02533 status=MagickFalse;
02534 continue;
02535 }
02536 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
02537 for (x=0; x < (long) image->columns; x++)
02538 {
02539 MagickPixelPacket
02540 qixel;
02541
02542 PixelPacket
02543 pixel;
02544
02545 register double
02546 *__restrict k;
02547
02548 register long
02549 i;
02550
02551 register const IndexPacket
02552 *__restrict indexes;
02553
02554 k=kernel;
02555 qixel=bias;
02556 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
02557 {
02558 for (i=0; i < (long) width; i++)
02559 {
02560 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
02561 offset[i].y,&pixel,exception);
02562 qixel.red+=(*k)*pixel.red;
02563 qixel.green+=(*k)*pixel.green;
02564 qixel.blue+=(*k)*pixel.blue;
02565 qixel.opacity+=(*k)*pixel.opacity;
02566 if (image->colorspace == CMYKColorspace)
02567 {
02568 indexes=GetCacheViewVirtualIndexQueue(image_view);
02569 qixel.index+=(*k)*(*indexes);
02570 }
02571 k++;
02572 }
02573 if ((channel & RedChannel) != 0)
02574 q->red=RoundToQuantum(qixel.red);
02575 if ((channel & GreenChannel) != 0)
02576 q->green=RoundToQuantum(qixel.green);
02577 if ((channel & BlueChannel) != 0)
02578 q->blue=RoundToQuantum(qixel.blue);
02579 if ((channel & OpacityChannel) != 0)
02580 q->opacity=RoundToQuantum(qixel.opacity);
02581 if (((channel & IndexChannel) != 0) &&
02582 (image->colorspace == CMYKColorspace))
02583 blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
02584 }
02585 else
02586 {
02587 MagickRealType
02588 alpha,
02589 gamma;
02590
02591 alpha=0.0;
02592 gamma=0.0;
02593 for (i=0; i < (long) width; i++)
02594 {
02595 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
02596 offset[i].y,&pixel,exception);
02597 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
02598 qixel.red+=(*k)*alpha*pixel.red;
02599 qixel.green+=(*k)*alpha*pixel.green;
02600 qixel.blue+=(*k)*alpha*pixel.blue;
02601 qixel.opacity+=(*k)*pixel.opacity;
02602 if (image->colorspace == CMYKColorspace)
02603 {
02604 indexes=GetCacheViewVirtualIndexQueue(image_view);
02605 qixel.index+=(*k)*alpha*(*indexes);
02606 }
02607 gamma+=(*k)*alpha;
02608 k++;
02609 }
02610 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02611 if ((channel & RedChannel) != 0)
02612 q->red=RoundToQuantum(gamma*qixel.red);
02613 if ((channel & GreenChannel) != 0)
02614 q->green=RoundToQuantum(gamma*qixel.green);
02615 if ((channel & BlueChannel) != 0)
02616 q->blue=RoundToQuantum(gamma*qixel.blue);
02617 if ((channel & OpacityChannel) != 0)
02618 q->opacity=RoundToQuantum(qixel.opacity);
02619 if (((channel & IndexChannel) != 0) &&
02620 (image->colorspace == CMYKColorspace))
02621 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
02622 }
02623 q++;
02624 }
02625 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
02626 status=MagickFalse;
02627 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02628 {
02629 MagickBooleanType
02630 proceed;
02631
02632 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02633 #pragma omp critical (MagickCore_MotionBlurImageChannel)
02634 #endif
02635 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
02636 if (proceed == MagickFalse)
02637 status=MagickFalse;
02638 }
02639 }
02640 blur_view=DestroyCacheView(blur_view);
02641 image_view=DestroyCacheView(image_view);
02642 kernel=(double *) RelinquishMagickMemory(kernel);
02643 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02644 if (status == MagickFalse)
02645 blur_image=DestroyImage(blur_image);
02646 return(blur_image);
02647 }
02648
02649
02650
02651
02652
02653
02654
02655
02656
02657
02658
02659
02660
02661
02662
02663
02664
02665
02666
02667
02668
02669
02670
02671
02672
02673
02674
02675
02676
02677
02678
02679 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
02680 ExceptionInfo *exception)
02681 {
02682 #define NumberTiles 9
02683 #define PreviewImageTag "Preview/Image"
02684 #define DefaultPreviewGeometry "204x204+10+10"
02685
02686 char
02687 factor[MaxTextExtent],
02688 label[MaxTextExtent];
02689
02690 double
02691 degrees,
02692 gamma,
02693 percentage,
02694 radius,
02695 sigma,
02696 threshold;
02697
02698 Image
02699 *images,
02700 *montage_image,
02701 *preview_image,
02702 *thumbnail;
02703
02704 ImageInfo
02705 *preview_info;
02706
02707 long
02708 y;
02709
02710 MagickBooleanType
02711 proceed;
02712
02713 MontageInfo
02714 *montage_info;
02715
02716 QuantizeInfo
02717 quantize_info;
02718
02719 RectangleInfo
02720 geometry;
02721
02722 register long
02723 i,
02724 x;
02725
02726 unsigned long
02727 colors;
02728
02729
02730
02731
02732 assert(image != (Image *) NULL);
02733 assert(image->signature == MagickSignature);
02734 if (image->debug != MagickFalse)
02735 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02736 colors=2;
02737 degrees=0.0;
02738 gamma=(-0.2f);
02739 preview_info=AcquireImageInfo();
02740 SetGeometry(image,&geometry);
02741 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
02742 &geometry.width,&geometry.height);
02743 images=NewImageList();
02744 percentage=12.5;
02745 GetQuantizeInfo(&quantize_info);
02746 radius=0.0;
02747 sigma=1.0;
02748 threshold=0.0;
02749 x=0;
02750 y=0;
02751 for (i=0; i < NumberTiles; i++)
02752 {
02753 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
02754 if (thumbnail == (Image *) NULL)
02755 break;
02756 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
02757 (void *) NULL);
02758 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
02759 if (i == (NumberTiles/2))
02760 {
02761 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
02762 AppendImageToList(&images,thumbnail);
02763 continue;
02764 }
02765 switch (preview)
02766 {
02767 case RotatePreview:
02768 {
02769 degrees+=45.0;
02770 preview_image=RotateImage(thumbnail,degrees,exception);
02771 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
02772 break;
02773 }
02774 case ShearPreview:
02775 {
02776 degrees+=5.0;
02777 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
02778 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
02779 degrees,2.0*degrees);
02780 break;
02781 }
02782 case RollPreview:
02783 {
02784 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
02785 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
02786 preview_image=RollImage(thumbnail,x,y,exception);
02787 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
02788 break;
02789 }
02790 case HuePreview:
02791 {
02792 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02793 if (preview_image == (Image *) NULL)
02794 break;
02795 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
02796 2.0*percentage);
02797 (void) ModulateImage(preview_image,factor);
02798 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
02799 break;
02800 }
02801 case SaturationPreview:
02802 {
02803 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02804 if (preview_image == (Image *) NULL)
02805 break;
02806 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
02807 (void) ModulateImage(preview_image,factor);
02808 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
02809 break;
02810 }
02811 case BrightnessPreview:
02812 {
02813 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02814 if (preview_image == (Image *) NULL)
02815 break;
02816 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
02817 (void) ModulateImage(preview_image,factor);
02818 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
02819 break;
02820 }
02821 case GammaPreview:
02822 default:
02823 {
02824 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02825 if (preview_image == (Image *) NULL)
02826 break;
02827 gamma+=0.4f;
02828 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
02829 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
02830 break;
02831 }
02832 case SpiffPreview:
02833 {
02834 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02835 if (preview_image != (Image *) NULL)
02836 for (x=0; x < i; x++)
02837 (void) ContrastImage(preview_image,MagickTrue);
02838 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
02839 break;
02840 }
02841 case DullPreview:
02842 {
02843 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02844 if (preview_image == (Image *) NULL)
02845 break;
02846 for (x=0; x < i; x++)
02847 (void) ContrastImage(preview_image,MagickFalse);
02848 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
02849 break;
02850 }
02851 case GrayscalePreview:
02852 {
02853 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02854 if (preview_image == (Image *) NULL)
02855 break;
02856 colors<<=1;
02857 quantize_info.number_colors=colors;
02858 quantize_info.colorspace=GRAYColorspace;
02859 (void) QuantizeImage(&quantize_info,preview_image);
02860 (void) FormatMagickString(label,MaxTextExtent,
02861 "-colorspace gray -colors %ld",colors);
02862 break;
02863 }
02864 case QuantizePreview:
02865 {
02866 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02867 if (preview_image == (Image *) NULL)
02868 break;
02869 colors<<=1;
02870 quantize_info.number_colors=colors;
02871 (void) QuantizeImage(&quantize_info,preview_image);
02872 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
02873 break;
02874 }
02875 case DespecklePreview:
02876 {
02877 for (x=0; x < (i-1); x++)
02878 {
02879 preview_image=DespeckleImage(thumbnail,exception);
02880 if (preview_image == (Image *) NULL)
02881 break;
02882 thumbnail=DestroyImage(thumbnail);
02883 thumbnail=preview_image;
02884 }
02885 preview_image=DespeckleImage(thumbnail,exception);
02886 if (preview_image == (Image *) NULL)
02887 break;
02888 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
02889 break;
02890 }
02891 case ReduceNoisePreview:
02892 {
02893 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
02894 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
02895 break;
02896 }
02897 case AddNoisePreview:
02898 {
02899 switch ((int) i)
02900 {
02901 case 0:
02902 {
02903 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
02904 break;
02905 }
02906 case 1:
02907 {
02908 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
02909 break;
02910 }
02911 case 2:
02912 {
02913 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
02914 break;
02915 }
02916 case 3:
02917 {
02918 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
02919 break;
02920 }
02921 case 4:
02922 {
02923 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
02924 break;
02925 }
02926 case 5:
02927 {
02928 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
02929 break;
02930 }
02931 default:
02932 {
02933 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
02934 break;
02935 }
02936 }
02937 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
02938 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
02939 break;
02940 }
02941 case SharpenPreview:
02942 {
02943 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
02944 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
02945 sigma);
02946 break;
02947 }
02948 case BlurPreview:
02949 {
02950 preview_image=BlurImage(thumbnail,radius,sigma,exception);
02951 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
02952 sigma);
02953 break;
02954 }
02955 case ThresholdPreview:
02956 {
02957 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02958 if (preview_image == (Image *) NULL)
02959 break;
02960 (void) BilevelImage(thumbnail,
02961 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
02962 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
02963 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
02964 break;
02965 }
02966 case EdgeDetectPreview:
02967 {
02968 preview_image=EdgeImage(thumbnail,radius,exception);
02969 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
02970 break;
02971 }
02972 case SpreadPreview:
02973 {
02974 preview_image=SpreadImage(thumbnail,radius,exception);
02975 (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
02976 break;
02977 }
02978 case SolarizePreview:
02979 {
02980 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02981 if (preview_image == (Image *) NULL)
02982 break;
02983 (void) SolarizeImage(preview_image,(double) QuantumRange*
02984 percentage/100.0);
02985 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
02986 (QuantumRange*percentage)/100.0);
02987 break;
02988 }
02989 case ShadePreview:
02990 {
02991 degrees+=10.0;
02992 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
02993 exception);
02994 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
02995 degrees);
02996 break;
02997 }
02998 case RaisePreview:
02999 {
03000 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
03001 if (preview_image == (Image *) NULL)
03002 break;
03003 geometry.width=(unsigned long) (2*i+2);
03004 geometry.height=(unsigned long) (2*i+2);
03005 geometry.x=i/2;
03006 geometry.y=i/2;
03007 (void) RaiseImage(preview_image,&geometry,MagickTrue);
03008 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
03009 geometry.width,geometry.height,geometry.x,geometry.y);
03010 break;
03011 }
03012 case SegmentPreview:
03013 {
03014 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
03015 if (preview_image == (Image *) NULL)
03016 break;
03017 threshold+=0.4f;
03018 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
03019 threshold);
03020 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
03021 threshold,threshold);
03022 break;
03023 }
03024 case SwirlPreview:
03025 {
03026 preview_image=SwirlImage(thumbnail,degrees,exception);
03027 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
03028 degrees+=45.0;
03029 break;
03030 }
03031 case ImplodePreview:
03032 {
03033 degrees+=0.1f;
03034 preview_image=ImplodeImage(thumbnail,degrees,exception);
03035 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
03036 break;
03037 }
03038 case WavePreview:
03039 {
03040 degrees+=5.0f;
03041 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
03042 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
03043 2.0*degrees);
03044 break;
03045 }
03046 case OilPaintPreview:
03047 {
03048 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
03049 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
03050 break;
03051 }
03052 case CharcoalDrawingPreview:
03053 {
03054 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
03055 exception);
03056 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
03057 sigma);
03058 break;
03059 }
03060 case JPEGPreview:
03061 {
03062 char
03063 filename[MaxTextExtent];
03064
03065 int
03066 file;
03067
03068 MagickBooleanType
03069 status;
03070
03071 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
03072 if (preview_image == (Image *) NULL)
03073 break;
03074 preview_info->quality=(unsigned long) percentage;
03075 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
03076 preview_info->quality);
03077 file=AcquireUniqueFileResource(filename);
03078 if (file != -1)
03079 file=close(file)-1;
03080 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
03081 "jpeg:%s",filename);
03082 status=WriteImage(preview_info,preview_image);
03083 if (status != MagickFalse)
03084 {
03085 Image
03086 *quality_image;
03087
03088 (void) CopyMagickString(preview_info->filename,
03089 preview_image->filename,MaxTextExtent);
03090 quality_image=ReadImage(preview_info,exception);
03091 if (quality_image != (Image *) NULL)
03092 {
03093 preview_image=DestroyImage(preview_image);
03094 preview_image=quality_image;
03095 }
03096 }
03097 (void) RelinquishUniqueFileResource(preview_image->filename);
03098 if ((GetBlobSize(preview_image)/1024) >= 1024)
03099 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
03100 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
03101 1024.0/1024.0);
03102 else
03103 if (GetBlobSize(preview_image) >= 1024)
03104 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
03105 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
03106 1024.0);
03107 else
03108 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
03109 factor,(unsigned long) GetBlobSize(thumbnail));
03110 break;
03111 }
03112 }
03113 thumbnail=DestroyImage(thumbnail);
03114 percentage+=12.5;
03115 radius+=0.5;
03116 sigma+=0.25;
03117 if (preview_image == (Image *) NULL)
03118 break;
03119 (void) DeleteImageProperty(preview_image,"label");
03120 (void) SetImageProperty(preview_image,"label",label);
03121 AppendImageToList(&images,preview_image);
03122 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
03123 if (proceed == MagickFalse)
03124 break;
03125 }
03126 if (images == (Image *) NULL)
03127 {
03128 preview_info=DestroyImageInfo(preview_info);
03129 return((Image *) NULL);
03130 }
03131
03132
03133
03134 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
03135 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
03136 montage_info->shadow=MagickTrue;
03137 (void) CloneString(&montage_info->tile,"3x3");
03138 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
03139 (void) CloneString(&montage_info->frame,DefaultTileFrame);
03140 montage_image=MontageImages(images,montage_info,exception);
03141 montage_info=DestroyMontageInfo(montage_info);
03142 images=DestroyImageList(images);
03143 if (montage_image == (Image *) NULL)
03144 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03145 if (montage_image->montage != (char *) NULL)
03146 {
03147
03148
03149
03150 montage_image->montage=(char *) RelinquishMagickMemory(
03151 montage_image->montage);
03152 if (image->directory != (char *) NULL)
03153 montage_image->directory=(char *) RelinquishMagickMemory(
03154 montage_image->directory);
03155 }
03156 preview_info=DestroyImageInfo(preview_info);
03157 return(montage_image);
03158 }
03159
03160
03161
03162
03163
03164
03165
03166
03167
03168
03169
03170
03171
03172
03173
03174
03175
03176
03177
03178
03179
03180
03181
03182
03183
03184
03185
03186
03187
03188
03189
03190
03191
03192
03193
03194 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
03195 ExceptionInfo *exception)
03196 {
03197 Image
03198 *blur_image;
03199
03200 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
03201 return(blur_image);
03202 }
03203
03204 MagickExport Image *RadialBlurImageChannel(const Image *image,
03205 const ChannelType channel,const double angle,ExceptionInfo *exception)
03206 {
03207 Image
03208 *blur_image;
03209
03210 long
03211 progress,
03212 y;
03213
03214 MagickBooleanType
03215 status;
03216
03217 MagickPixelPacket
03218 bias;
03219
03220 MagickRealType
03221 blur_radius,
03222 *cos_theta,
03223 offset,
03224 *sin_theta,
03225 theta;
03226
03227 PointInfo
03228 blur_center;
03229
03230 register long
03231 i;
03232
03233 unsigned long
03234 n;
03235
03236 CacheView
03237 *blur_view,
03238 *image_view;
03239
03240
03241
03242
03243 assert(image != (Image *) NULL);
03244 assert(image->signature == MagickSignature);
03245 if (image->debug != MagickFalse)
03246 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03247 assert(exception != (ExceptionInfo *) NULL);
03248 assert(exception->signature == MagickSignature);
03249 blur_image=CloneImage(image,0,0,MagickTrue,exception);
03250 if (blur_image == (Image *) NULL)
03251 return((Image *) NULL);
03252 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
03253 {
03254 InheritException(exception,&blur_image->exception);
03255 blur_image=DestroyImage(blur_image);
03256 return((Image *) NULL);
03257 }
03258 blur_center.x=(double) image->columns/2.0;
03259 blur_center.y=(double) image->rows/2.0;
03260 blur_radius=hypot(blur_center.x,blur_center.y);
03261 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
03262 2UL);
03263 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
03264 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
03265 sizeof(*cos_theta));
03266 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
03267 sizeof(*sin_theta));
03268 if ((cos_theta == (MagickRealType *) NULL) ||
03269 (sin_theta == (MagickRealType *) NULL))
03270 {
03271 blur_image=DestroyImage(blur_image);
03272 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03273 }
03274 offset=theta*(MagickRealType) (n-1)/2.0;
03275 for (i=0; i < (long) n; i++)
03276 {
03277 cos_theta[i]=cos((double) (theta*i-offset));
03278 sin_theta[i]=sin((double) (theta*i-offset));
03279 }
03280
03281
03282
03283 status=MagickTrue;
03284 progress=0;
03285 GetMagickPixelPacket(image,&bias);
03286 image_view=AcquireCacheView(image);
03287 blur_view=AcquireCacheView(blur_image);
03288 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03289 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03290 #endif
03291 for (y=0; y < (long) blur_image->rows; y++)
03292 {
03293 register const IndexPacket
03294 *__restrict indexes;
03295
03296 register IndexPacket
03297 *__restrict blur_indexes;
03298
03299 register long
03300 x;
03301
03302 register PixelPacket
03303 *__restrict q;
03304
03305 if (status == MagickFalse)
03306 continue;
03307 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
03308 exception);
03309 if (q == (PixelPacket *) NULL)
03310 {
03311 status=MagickFalse;
03312 continue;
03313 }
03314 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
03315 for (x=0; x < (long) blur_image->columns; x++)
03316 {
03317 MagickPixelPacket
03318 qixel;
03319
03320 MagickRealType
03321 normalize,
03322 radius;
03323
03324 PixelPacket
03325 pixel;
03326
03327 PointInfo
03328 center;
03329
03330 register long
03331 i;
03332
03333 unsigned long
03334 step;
03335
03336 center.x=(double) x-blur_center.x;
03337 center.y=(double) y-blur_center.y;
03338 radius=hypot((double) center.x,center.y);
03339 if (radius == 0)
03340 step=1;
03341 else
03342 {
03343 step=(unsigned long) (blur_radius/radius);
03344 if (step == 0)
03345 step=1;
03346 else
03347 if (step >= n)
03348 step=n-1;
03349 }
03350 normalize=0.0;
03351 qixel=bias;
03352 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
03353 {
03354 for (i=0; i < (long) n; i+=step)
03355 {
03356 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
03357 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
03358 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
03359 &pixel,exception);
03360 qixel.red+=pixel.red;
03361 qixel.green+=pixel.green;
03362 qixel.blue+=pixel.blue;
03363 qixel.opacity+=pixel.opacity;
03364 if (image->colorspace == CMYKColorspace)
03365 {
03366 indexes=GetCacheViewVirtualIndexQueue(image_view);
03367 qixel.index+=(*indexes);
03368 }
03369 normalize+=1.0;
03370 }
03371 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
03372 normalize);
03373 if ((channel & RedChannel) != 0)
03374 q->red=RoundToQuantum(normalize*qixel.red);
03375 if ((channel & GreenChannel) != 0)
03376 q->green=RoundToQuantum(normalize*qixel.green);
03377 if ((channel & BlueChannel) != 0)
03378 q->blue=RoundToQuantum(normalize*qixel.blue);
03379 if ((channel & OpacityChannel) != 0)
03380 q->opacity=RoundToQuantum(normalize*qixel.opacity);
03381 if (((channel & IndexChannel) != 0) &&
03382 (image->colorspace == CMYKColorspace))
03383 blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
03384 }
03385 else
03386 {
03387 MagickRealType
03388 alpha,
03389 gamma;
03390
03391 alpha=1.0;
03392 gamma=0.0;
03393 for (i=0; i < (long) n; i+=step)
03394 {
03395 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
03396 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
03397 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
03398 &pixel,exception);
03399 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
03400 qixel.red+=alpha*pixel.red;
03401 qixel.green+=alpha*pixel.green;
03402 qixel.blue+=alpha*pixel.blue;
03403 qixel.opacity+=pixel.opacity;
03404 if (image->colorspace == CMYKColorspace)
03405 {
03406 indexes=GetCacheViewVirtualIndexQueue(image_view);
03407 qixel.index+=alpha*(*indexes);
03408 }
03409 gamma+=alpha;
03410 normalize+=1.0;
03411 }
03412 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03413 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
03414 normalize);
03415 if ((channel & RedChannel) != 0)
03416 q->red=RoundToQuantum(gamma*qixel.red);
03417 if ((channel & GreenChannel) != 0)
03418 q->green=RoundToQuantum(gamma*qixel.green);
03419 if ((channel & BlueChannel) != 0)
03420 q->blue=RoundToQuantum(gamma*qixel.blue);
03421 if ((channel & OpacityChannel) != 0)
03422 q->opacity=RoundToQuantum(normalize*qixel.opacity);
03423 if (((channel & IndexChannel) != 0) &&
03424 (image->colorspace == CMYKColorspace))
03425 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
03426 }
03427 q++;
03428 }
03429 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
03430 status=MagickFalse;
03431 if (image->progress_monitor != (MagickProgressMonitor) NULL)
03432 {
03433 MagickBooleanType
03434 proceed;
03435
03436 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03437 #pragma omp critical (MagickCore_RadialBlurImageChannel)
03438 #endif
03439 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
03440 if (proceed == MagickFalse)
03441 status=MagickFalse;
03442 }
03443 }
03444 blur_view=DestroyCacheView(blur_view);
03445 image_view=DestroyCacheView(image_view);
03446 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
03447 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
03448 if (status == MagickFalse)
03449 blur_image=DestroyImage(blur_image);
03450 return(blur_image);
03451 }
03452
03453
03454
03455
03456
03457
03458
03459
03460
03461
03462
03463
03464
03465
03466
03467
03468
03469
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481
03482
03483
03484 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
03485 {
03486 MagickPixelPacket
03487 pixel;
03488
03489 register long
03490 channel;
03491
03492 register MedianSkipList
03493 *list;
03494
03495 unsigned long
03496 center,
03497 color,
03498 count,
03499 previous,
03500 next;
03501
03502 unsigned short
03503 channels[5];
03504
03505
03506
03507
03508 center=pixel_list->center;
03509 for (channel=0; channel < 5; channel++)
03510 {
03511 list=pixel_list->lists+channel;
03512 color=65536UL;
03513 next=list->nodes[color].next[0];
03514 count=0;
03515 do
03516 {
03517 previous=color;
03518 color=next;
03519 next=list->nodes[color].next[0];
03520 count+=list->nodes[color].count;
03521 }
03522 while (count <= center);
03523 if ((previous == 65536UL) && (next != 65536UL))
03524 color=next;
03525 else
03526 if ((previous != 65536UL) && (next == 65536UL))
03527 color=previous;
03528 channels[channel]=(unsigned short) color;
03529 }
03530 GetMagickPixelPacket((const Image *) NULL,&pixel);
03531 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
03532 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
03533 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
03534 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
03535 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
03536 return(pixel);
03537 }
03538
03539 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
03540 ExceptionInfo *exception)
03541 {
03542 #define ReduceNoiseImageTag "ReduceNoise/Image"
03543
03544 Image
03545 *noise_image;
03546
03547 long
03548 progress,
03549 y;
03550
03551 MagickBooleanType
03552 status;
03553
03554 MedianPixelList
03555 **pixel_list;
03556
03557 unsigned long
03558 width;
03559
03560 CacheView
03561 *image_view,
03562 *noise_view;
03563
03564
03565
03566
03567 assert(image != (Image *) NULL);
03568 assert(image->signature == MagickSignature);
03569 if (image->debug != MagickFalse)
03570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03571 assert(exception != (ExceptionInfo *) NULL);
03572 assert(exception->signature == MagickSignature);
03573 width=GetOptimalKernelWidth2D(radius,0.5);
03574 if ((image->columns < width) || (image->rows < width))
03575 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
03576 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
03577 exception);
03578 if (noise_image == (Image *) NULL)
03579 return((Image *) NULL);
03580 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
03581 {
03582 InheritException(exception,&noise_image->exception);
03583 noise_image=DestroyImage(noise_image);
03584 return((Image *) NULL);
03585 }
03586 pixel_list=AcquireMedianPixelListThreadSet(width);
03587 if (pixel_list == (MedianPixelList **) NULL)
03588 {
03589 noise_image=DestroyImage(noise_image);
03590 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03591 }
03592
03593
03594
03595 status=MagickTrue;
03596 progress=0;
03597 image_view=AcquireCacheView(image);
03598 noise_view=AcquireCacheView(noise_image);
03599 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03600 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03601 #endif
03602 for (y=0; y < (long) noise_image->rows; y++)
03603 {
03604 register const IndexPacket
03605 *__restrict indexes;
03606
03607 register const PixelPacket
03608 *__restrict p;
03609
03610 register IndexPacket
03611 *__restrict noise_indexes;
03612
03613 register long
03614 id,
03615 x;
03616
03617 register PixelPacket
03618 *__restrict q;
03619
03620 if (status == MagickFalse)
03621 continue;
03622 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
03623 2L),image->columns+width,width,exception);
03624 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
03625 exception);
03626 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
03627 {
03628 status=MagickFalse;
03629 continue;
03630 }
03631 indexes=GetCacheViewVirtualIndexQueue(image_view);
03632 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
03633 id=GetOpenMPThreadId();
03634 for (x=0; x < (long) noise_image->columns; x++)
03635 {
03636 MagickPixelPacket
03637 pixel;
03638
03639 register const PixelPacket
03640 *__restrict r;
03641
03642 register const IndexPacket
03643 *__restrict s;
03644
03645 register long
03646 u,
03647 v;
03648
03649 r=p;
03650 s=indexes+x;
03651 ResetMedianPixelList(pixel_list[id]);
03652 for (v=0; v < (long) width; v++)
03653 {
03654 for (u=0; u < (long) width; u++)
03655 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
03656 r+=image->columns+width;
03657 s+=image->columns+width;
03658 }
03659 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
03660 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
03661 p++;
03662 q++;
03663 }
03664 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
03665 status=MagickFalse;
03666 if (image->progress_monitor != (MagickProgressMonitor) NULL)
03667 {
03668 MagickBooleanType
03669 proceed;
03670
03671 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03672 #pragma omp critical (MagickCore_ReduceNoiseImage)
03673 #endif
03674 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
03675 image->rows);
03676 if (proceed == MagickFalse)
03677 status=MagickFalse;
03678 }
03679 }
03680 noise_view=DestroyCacheView(noise_view);
03681 image_view=DestroyCacheView(image_view);
03682 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
03683 return(noise_image);
03684 }
03685
03686
03687
03688
03689
03690
03691
03692
03693
03694
03695
03696
03697
03698
03699
03700
03701
03702
03703
03704
03705
03706
03707
03708
03709
03710
03711
03712
03713
03714
03715
03716
03717
03718
03719
03720
03721
03722
03723
03724
03725
03726
03727 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
03728 const PixelPacket *q,const double threshold)
03729 {
03730 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
03731 return(MagickTrue);
03732 return(MagickFalse);
03733 }
03734
03735 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
03736 const double sigma,const double threshold,ExceptionInfo *exception)
03737 {
03738 Image
03739 *blur_image;
03740
03741 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
03742 threshold,exception);
03743 return(blur_image);
03744 }
03745
03746 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
03747 const ChannelType channel,const double radius,const double sigma,
03748 const double threshold,ExceptionInfo *exception)
03749 {
03750 #define SelectiveBlurImageTag "SelectiveBlur/Image"
03751
03752 double
03753 alpha,
03754 *kernel;
03755
03756 Image
03757 *blur_image;
03758
03759 long
03760 progress,
03761 v,
03762 y;
03763
03764 MagickBooleanType
03765 status;
03766
03767 MagickPixelPacket
03768 bias;
03769
03770 register long
03771 i,
03772 u;
03773
03774 unsigned long
03775 width;
03776
03777 CacheView
03778 *blur_view,
03779 *image_view;
03780
03781
03782
03783
03784 assert(image != (Image *) NULL);
03785 assert(image->signature == MagickSignature);
03786 if (image->debug != MagickFalse)
03787 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03788 assert(exception != (ExceptionInfo *) NULL);
03789 assert(exception->signature == MagickSignature);
03790 width=GetOptimalKernelWidth1D(radius,sigma);
03791 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
03792 if (kernel == (double *) NULL)
03793 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03794 i=0;
03795 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
03796 {
03797 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
03798 {
03799 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
03800 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
03801 i++;
03802 }
03803 }
03804 if (image->debug != MagickFalse)
03805 {
03806 char
03807 format[MaxTextExtent],
03808 *message;
03809
03810 long
03811 u,
03812 v;
03813
03814 register const double
03815 *k;
03816
03817 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
03818 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
03819 message=AcquireString("");
03820 k=kernel;
03821 for (v=0; v < (long) width; v++)
03822 {
03823 *message='\0';
03824 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
03825 (void) ConcatenateString(&message,format);
03826 for (u=0; u < (long) width; u++)
03827 {
03828 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
03829 (void) ConcatenateString(&message,format);
03830 }
03831 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
03832 }
03833 message=DestroyString(message);
03834 }
03835 blur_image=CloneImage(image,0,0,MagickTrue,exception);
03836 if (blur_image == (Image *) NULL)
03837 return((Image *) NULL);
03838 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
03839 {
03840 InheritException(exception,&blur_image->exception);
03841 blur_image=DestroyImage(blur_image);
03842 return((Image *) NULL);
03843 }
03844
03845
03846
03847 status=MagickTrue;
03848 progress=0;
03849 GetMagickPixelPacket(image,&bias);
03850 SetMagickPixelPacketBias(image,&bias);
03851 image_view=AcquireCacheView(image);
03852 blur_view=AcquireCacheView(blur_image);
03853 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03854 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03855 #endif
03856 for (y=0; y < (long) image->rows; y++)
03857 {
03858 MagickBooleanType
03859 sync;
03860
03861 MagickRealType
03862 gamma;
03863
03864 register const IndexPacket
03865 *__restrict indexes;
03866
03867 register const PixelPacket
03868 *__restrict p;
03869
03870 register IndexPacket
03871 *__restrict blur_indexes;
03872
03873 register long
03874 x;
03875
03876 register PixelPacket
03877 *__restrict q;
03878
03879 if (status == MagickFalse)
03880 continue;
03881 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
03882 2L),image->columns+width,width,exception);
03883 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
03884 exception);
03885 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
03886 {
03887 status=MagickFalse;
03888 continue;
03889 }
03890 indexes=GetCacheViewVirtualIndexQueue(image_view);
03891 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
03892 for (x=0; x < (long) image->columns; x++)
03893 {
03894 long
03895 j,
03896 v;
03897
03898 MagickPixelPacket
03899 pixel;
03900
03901 register const double
03902 *__restrict k;
03903
03904 register long
03905 u;
03906
03907 pixel=bias;
03908 k=kernel;
03909 gamma=0.0;
03910 j=0;
03911 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
03912 {
03913 for (v=0; v < (long) width; v++)
03914 {
03915 for (u=0; u < (long) width; u++)
03916 {
03917 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03918 {
03919 pixel.red+=(*k)*(p+u+j)->red;
03920 pixel.green+=(*k)*(p+u+j)->green;
03921 pixel.blue+=(*k)*(p+u+j)->blue;
03922 gamma+=(*k);
03923 k++;
03924 }
03925 }
03926 j+=image->columns+width;
03927 }
03928 if (gamma != 0.0)
03929 {
03930 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03931 if ((channel & RedChannel) != 0)
03932 q->red=RoundToQuantum(gamma*pixel.red);
03933 if ((channel & GreenChannel) != 0)
03934 q->green=RoundToQuantum(gamma*pixel.green);
03935 if ((channel & BlueChannel) != 0)
03936 q->blue=RoundToQuantum(gamma*pixel.blue);
03937 }
03938 if ((channel & OpacityChannel) != 0)
03939 {
03940 gamma=0.0;
03941 j=0;
03942 for (v=0; v < (long) width; v++)
03943 {
03944 for (u=0; u < (long) width; u++)
03945 {
03946 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03947 {
03948 pixel.opacity+=(*k)*(p+u+j)->opacity;
03949 gamma+=(*k);
03950 k++;
03951 }
03952 }
03953 j+=image->columns+width;
03954 }
03955 if (gamma != 0.0)
03956 {
03957 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
03958 gamma);
03959 q->opacity=RoundToQuantum(gamma*pixel.opacity);
03960 }
03961 }
03962 if (((channel & IndexChannel) != 0) &&
03963 (image->colorspace == CMYKColorspace))
03964 {
03965 gamma=0.0;
03966 j=0;
03967 for (v=0; v < (long) width; v++)
03968 {
03969 for (u=0; u < (long) width; u++)
03970 {
03971 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03972 {
03973 pixel.index+=(*k)*indexes[x+u+j];
03974 gamma+=(*k);
03975 k++;
03976 }
03977 }
03978 j+=image->columns+width;
03979 }
03980 if (gamma != 0.0)
03981 {
03982 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
03983 gamma);
03984 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
03985 }
03986 }
03987 }
03988 else
03989 {
03990 MagickRealType
03991 alpha;
03992
03993 for (v=0; v < (long) width; v++)
03994 {
03995 for (u=0; u < (long) width; u++)
03996 {
03997 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03998 {
03999 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
04000 (p+u+j)->opacity));
04001 pixel.red+=(*k)*alpha*(p+u+j)->red;
04002 pixel.green+=(*k)*alpha*(p+u+j)->green;
04003 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
04004 pixel.opacity+=(*k)*(p+u+j)->opacity;
04005 gamma+=(*k)*alpha;
04006 k++;
04007 }
04008 }
04009 j+=image->columns+width;
04010 }
04011 if (gamma != 0.0)
04012 {
04013 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
04014 if ((channel & RedChannel) != 0)
04015 q->red=RoundToQuantum(gamma*pixel.red);
04016 if ((channel & GreenChannel) != 0)
04017 q->green=RoundToQuantum(gamma*pixel.green);
04018 if ((channel & BlueChannel) != 0)
04019 q->blue=RoundToQuantum(gamma*pixel.blue);
04020 }
04021 if ((channel & OpacityChannel) != 0)
04022 {
04023 gamma=0.0;
04024 j=0;
04025 for (v=0; v < (long) width; v++)
04026 {
04027 for (u=0; u < (long) width; u++)
04028 {
04029 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
04030 {
04031 pixel.opacity+=(*k)*(p+u+j)->opacity;
04032 gamma+=(*k);
04033 k++;
04034 }
04035 }
04036 j+=image->columns+width;
04037 }
04038 if (gamma != 0.0)
04039 {
04040 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
04041 gamma);
04042 q->opacity=RoundToQuantum(pixel.opacity);
04043 }
04044 }
04045 if (((channel & IndexChannel) != 0) &&
04046 (image->colorspace == CMYKColorspace))
04047 {
04048 gamma=0.0;
04049 j=0;
04050 for (v=0; v < (long) width; v++)
04051 {
04052 for (u=0; u < (long) width; u++)
04053 {
04054 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
04055 {
04056 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
04057 (p+u+j)->opacity));
04058 pixel.index+=(*k)*alpha*indexes[x+u+j];
04059 gamma+=(*k);
04060 k++;
04061 }
04062 }
04063 j+=image->columns+width;
04064 }
04065 if (gamma != 0.0)
04066 {
04067 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
04068 gamma);
04069 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
04070 }
04071 }
04072 }
04073 p++;
04074 q++;
04075 }
04076 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
04077 if (sync == MagickFalse)
04078 status=MagickFalse;
04079 if (image->progress_monitor != (MagickProgressMonitor) NULL)
04080 {
04081 MagickBooleanType
04082 proceed;
04083
04084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04085 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
04086 #endif
04087 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
04088 image->rows);
04089 if (proceed == MagickFalse)
04090 status=MagickFalse;
04091 }
04092 }
04093 blur_image->type=image->type;
04094 blur_view=DestroyCacheView(blur_view);
04095 image_view=DestroyCacheView(image_view);
04096 kernel=(double *) RelinquishMagickMemory(kernel);
04097 if (status == MagickFalse)
04098 blur_image=DestroyImage(blur_image);
04099 return(blur_image);
04100 }
04101
04102
04103
04104
04105
04106
04107
04108
04109
04110
04111
04112
04113
04114
04115
04116
04117
04118
04119
04120
04121
04122
04123
04124
04125
04126
04127
04128
04129
04130
04131
04132
04133
04134 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
04135 const double azimuth,const double elevation,ExceptionInfo *exception)
04136 {
04137 #define ShadeImageTag "Shade/Image"
04138
04139 Image
04140 *shade_image;
04141
04142 long
04143 progress,
04144 y;
04145
04146 MagickBooleanType
04147 status;
04148
04149 PrimaryInfo
04150 light;
04151
04152 CacheView
04153 *image_view,
04154 *shade_view;
04155
04156
04157
04158
04159 assert(image != (const Image *) NULL);
04160 assert(image->signature == MagickSignature);
04161 if (image->debug != MagickFalse)
04162 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
04163 assert(exception != (ExceptionInfo *) NULL);
04164 assert(exception->signature == MagickSignature);
04165 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
04166 if (shade_image == (Image *) NULL)
04167 return((Image *) NULL);
04168 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
04169 {
04170 InheritException(exception,&shade_image->exception);
04171 shade_image=DestroyImage(shade_image);
04172 return((Image *) NULL);
04173 }
04174
04175
04176
04177 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
04178 cos(DegreesToRadians(elevation));
04179 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
04180 cos(DegreesToRadians(elevation));
04181 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
04182
04183
04184
04185 status=MagickTrue;
04186 progress=0;
04187 image_view=AcquireCacheView(image);
04188 shade_view=AcquireCacheView(shade_image);
04189 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04190 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
04191 #endif
04192 for (y=0; y < (long) image->rows; y++)
04193 {
04194 MagickRealType
04195 distance,
04196 normal_distance,
04197 shade;
04198
04199 PrimaryInfo
04200 normal;
04201
04202 register const PixelPacket
04203 *__restrict p,
04204 *__restrict s0,
04205 *__restrict s1,
04206 *__restrict s2;
04207
04208 register long
04209 x;
04210
04211 register PixelPacket
04212 *__restrict q;
04213
04214 if (status == MagickFalse)
04215 continue;
04216 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
04217 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
04218 exception);
04219 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
04220 {
04221 status=MagickFalse;
04222 continue;
04223 }
04224
04225
04226
04227 normal.z=2.0*(double) QuantumRange;
04228 s0=p+1;
04229 s1=s0+image->columns+2;
04230 s2=s1+image->columns+2;
04231 for (x=0; x < (long) image->columns; x++)
04232 {
04233
04234
04235
04236 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
04237 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
04238 PixelIntensity(s2+1));
04239 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
04240 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
04241 PixelIntensity(s0+1));
04242 if ((normal.x == 0.0) && (normal.y == 0.0))
04243 shade=light.z;
04244 else
04245 {
04246 shade=0.0;
04247 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04248 if (distance > MagickEpsilon)
04249 {
04250 normal_distance=
04251 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04252 if (normal_distance > (MagickEpsilon*MagickEpsilon))
04253 shade=distance/sqrt((double) normal_distance);
04254 }
04255 }
04256 if (gray != MagickFalse)
04257 {
04258 q->red=(Quantum) shade;
04259 q->green=(Quantum) shade;
04260 q->blue=(Quantum) shade;
04261 }
04262 else
04263 {
04264 q->red=RoundToQuantum(QuantumScale*shade*s1->red);
04265 q->green=RoundToQuantum(QuantumScale*shade*s1->green);
04266 q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
04267 }
04268 q->opacity=s1->opacity;
04269 s0++;
04270 s1++;
04271 s2++;
04272 q++;
04273 }
04274 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
04275 status=MagickFalse;
04276 if (image->progress_monitor != (MagickProgressMonitor) NULL)
04277 {
04278 MagickBooleanType
04279 proceed;
04280
04281 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04282 #pragma omp critical (MagickCore_ShadeImage)
04283 #endif
04284 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
04285 if (proceed == MagickFalse)
04286 status=MagickFalse;
04287 }
04288 }
04289 shade_view=DestroyCacheView(shade_view);
04290 image_view=DestroyCacheView(image_view);
04291 if (status == MagickFalse)
04292 shade_image=DestroyImage(shade_image);
04293 return(shade_image);
04294 }
04295
04296
04297
04298
04299
04300
04301
04302
04303
04304
04305
04306
04307
04308
04309
04310
04311
04312
04313
04314
04315
04316
04317
04318
04319
04320
04321
04322
04323
04324
04325
04326
04327
04328
04329
04330
04331
04332
04333
04334
04335
04336
04337
04338
04339 MagickExport Image *SharpenImage(const Image *image,const double radius,
04340 const double sigma,ExceptionInfo *exception)
04341 {
04342 Image
04343 *sharp_image;
04344
04345 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
04346 return(sharp_image);
04347 }
04348
04349 MagickExport Image *SharpenImageChannel(const Image *image,
04350 const ChannelType channel,const double radius,const double sigma,
04351 ExceptionInfo *exception)
04352 {
04353 double
04354 *kernel;
04355
04356 Image
04357 *sharp_image;
04358
04359 MagickRealType
04360 alpha,
04361 normalize;
04362
04363 register long
04364 i,
04365 u,
04366 v;
04367
04368 unsigned long
04369 width;
04370
04371 assert(image != (const Image *) NULL);
04372 assert(image->signature == MagickSignature);
04373 if (image->debug != MagickFalse)
04374 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
04375 assert(exception != (ExceptionInfo *) NULL);
04376 assert(exception->signature == MagickSignature);
04377 width=GetOptimalKernelWidth2D(radius,sigma);
04378 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
04379 if (kernel == (double *) NULL)
04380 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
04381 i=0;
04382 normalize=0.0;
04383 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
04384 {
04385 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
04386 {
04387 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
04388 kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
04389 normalize+=kernel[i];
04390 i++;
04391 }
04392 }
04393 kernel[i/2]=(double) ((-2.0)*normalize);
04394 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
04395 kernel=(double *) RelinquishMagickMemory(kernel);
04396 return(sharp_image);
04397 }
04398
04399
04400
04401
04402
04403