shear.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                      SSSSS  H   H  EEEEE   AAA    RRRR                      %
00007 %                      SS     H   H  E      A   A   R   R                     %
00008 %                       SSS   HHHHH  EEE    AAAAA   RRRR                      %
00009 %                         SS  H   H  E      A   A   R R                       %
00010 %                      SSSSS  H   H  EEEEE  A   A   R  R                      %
00011 %                                                                             %
00012 %                                                                             %
00013 %    MagickCore Methods to Shear or Rotate an Image by an Arbitrary Angle     %
00014 %                                                                             %
00015 %                               Software Design                               %
00016 %                                 John Cristy                                 %
00017 %                                  July 1992                                  %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %  The RotateImage, XShearImage, and YShearImage methods are based on the
00037 %  paper "A Fast Algorithm for General Raster Rotatation" by Alan W. Paeth,
00038 %  Graphics Interface '86 (Vancouver).  RotateImage is adapted from a similar
00039 %  method based on the Paeth paper written by Michael Halle of the Spatial
00040 %  Imaging Group, MIT Media Lab.
00041 %
00042 %
00043 */
00044 
00045 /*
00046   Include declarations.
00047 */
00048 #include "magick/studio.h"
00049 #include "magick/artifact.h"
00050 #include "magick/blob-private.h"
00051 #include "magick/cache-private.h"
00052 #include "magick/color-private.h"
00053 #include "magick/colorspace-private.h"
00054 #include "magick/composite.h"
00055 #include "magick/composite-private.h"
00056 #include "magick/decorate.h"
00057 #include "magick/distort.h"
00058 #include "magick/draw.h"
00059 #include "magick/exception.h"
00060 #include "magick/exception-private.h"
00061 #include "magick/gem.h"
00062 #include "magick/geometry.h"
00063 #include "magick/image.h"
00064 #include "magick/image-private.h"
00065 #include "magick/memory_.h"
00066 #include "magick/list.h"
00067 #include "magick/monitor.h"
00068 #include "magick/monitor-private.h"
00069 #include "magick/pixel-private.h"
00070 #include "magick/quantum.h"
00071 #include "magick/resource_.h"
00072 #include "magick/shear.h"
00073 #include "magick/statistic.h"
00074 #include "magick/threshold.h"
00075 #include "magick/transform.h"
00076 
00077 /*
00078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00079 %                                                                             %
00080 %                                                                             %
00081 %                                                                             %
00082 %     A f f i n e T r a n s f o r m I m a g e                                 %
00083 %                                                                             %
00084 %                                                                             %
00085 %                                                                             %
00086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00087 %
00088 %  AffineTransformImage() transforms an image as dictated by the affine matrix.
00089 %  It allocates the memory necessary for the new Image structure and returns
00090 %  a pointer to the new image.
00091 %
00092 %  The format of the AffineTransformImage method is:
00093 %
00094 %      Image *AffineTransformImage(const Image *image,
00095 %        AffineMatrix *affine_matrix,ExceptionInfo *exception)
00096 %
00097 %  A description of each parameter follows:
00098 %
00099 %    o image: the image.
00100 %
00101 %    o affine_matrix: the affine matrix.
00102 %
00103 %    o exception: return any errors or warnings in this structure.
00104 %
00105 */
00106 MagickExport Image *AffineTransformImage(const Image *image,
00107   const AffineMatrix *affine_matrix,ExceptionInfo *exception)
00108 {
00109   double
00110     distort[6];
00111 
00112   Image
00113     *affine_image;
00114 
00115   /*
00116     Affine transform image.
00117   */
00118   assert(image->signature == MagickSignature);
00119   if (image->debug != MagickFalse)
00120     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00121   assert(affine_matrix != (AffineMatrix *) NULL);
00122   assert(exception != (ExceptionInfo *) NULL);
00123   assert(exception->signature == MagickSignature);
00124   distort[0]=affine_matrix->sx;
00125   distort[1]=affine_matrix->rx;
00126   distort[2]=affine_matrix->ry;
00127   distort[3]=affine_matrix->sy;
00128   distort[4]=affine_matrix->tx;
00129   distort[5]=affine_matrix->ty;
00130   affine_image=DistortImage(image,AffineProjectionDistortion,6,distort,
00131     MagickTrue,exception);
00132   return(affine_image);
00133 }
00134 
00135 /*
00136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00137 %                                                                             %
00138 %                                                                             %
00139 %                                                                             %
00140 +   C r o p T o F i t I m a g e                                               %
00141 %                                                                             %
00142 %                                                                             %
00143 %                                                                             %
00144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00145 %
00146 %  CropToFitImage() crops the sheared image as determined by the bounding box
00147 %  as defined by width and height and shearing angles.
00148 %
00149 %  The format of the CropToFitImage method is:
00150 %
00151 %      Image *CropToFitImage(Image **image,const MagickRealType x_shear,
00152 %        const MagickRealType x_shear,const MagickRealType width,
00153 %        const MagickRealType height,const MagickBooleanType rotate,
00154 %        ExceptionInfo *exception)
00155 %
00156 %  A description of each parameter follows.
00157 %
00158 %    o image: the image.
00159 %
00160 %    o x_shear, y_shear, width, height: Defines a region of the image to crop.
00161 %
00162 %    o exception: return any errors or warnings in this structure.
00163 %
00164 */
00165 static inline void CropToFitImage(Image **image,const MagickRealType x_shear,
00166   const MagickRealType y_shear,const MagickRealType width,
00167   const MagickRealType height,const MagickBooleanType rotate,
00168   ExceptionInfo *exception)
00169 {
00170   Image
00171     *crop_image;
00172 
00173   PointInfo
00174     extent[4],
00175     min,
00176     max;
00177 
00178   RectangleInfo
00179     geometry,
00180     page;
00181 
00182   register long
00183     i;
00184 
00185   /*
00186     Calculate the rotated image size.
00187   */
00188   extent[0].x=(double) (-width/2.0);
00189   extent[0].y=(double) (-height/2.0);
00190   extent[1].x=(double) width/2.0;
00191   extent[1].y=(double) (-height/2.0);
00192   extent[2].x=(double) (-width/2.0);
00193   extent[2].y=(double) height/2.0;
00194   extent[3].x=(double) width/2.0;
00195   extent[3].y=(double) height/2.0;
00196   for (i=0; i < 4; i++)
00197   {
00198     extent[i].x+=x_shear*extent[i].y;
00199     extent[i].y+=y_shear*extent[i].x;
00200     if (rotate != MagickFalse)
00201       extent[i].x+=x_shear*extent[i].y;
00202     extent[i].x+=(double) (*image)->columns/2.0;
00203     extent[i].y+=(double) (*image)->rows/2.0;
00204   }
00205   min=extent[0];
00206   max=extent[0];
00207   for (i=1; i < 4; i++)
00208   {
00209     if (min.x > extent[i].x)
00210       min.x=extent[i].x;
00211     if (min.y > extent[i].y)
00212       min.y=extent[i].y;
00213     if (max.x < extent[i].x)
00214       max.x=extent[i].x;
00215     if (max.y < extent[i].y)
00216       max.y=extent[i].y;
00217   }
00218   geometry.x=(long) (min.x+0.5);
00219   geometry.y=(long) (min.y+0.5);
00220   geometry.width=(unsigned long) ((long) (max.x+0.5)-(long) (min.x+0.5));
00221   geometry.height=(unsigned long) ((long) (max.y+0.5)-(long) (min.y+0.5));
00222   page=(*image)->page;
00223   (void) ParseAbsoluteGeometry("0x0+0+0",&(*image)->page);
00224   crop_image=CropImage(*image,&geometry,exception);
00225   (*image)->page=page;
00226   if (crop_image != (Image *) NULL)
00227     {
00228       crop_image->page=page;
00229       *image=DestroyImage(*image);
00230       *image=crop_image;
00231     }
00232 }
00233 
00234 /*
00235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00236 %                                                                             %
00237 %                                                                             %
00238 %                                                                             %
00239 %     D e s k e w I m a g e                                                   %
00240 %                                                                             %
00241 %                                                                             %
00242 %                                                                             %
00243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00244 %
00245 %  DeskewImage() removes skew from the image.  Skew is an artifact that
00246 %  occurs in scanned images because of the camera being misaligned,
00247 %  imperfections in the scanning or surface, or simply because the paper was
00248 %  not placed completely flat when scanned.
00249 %
00250 %  The format of the DeskewImage method is:
00251 %
00252 %      Image *DeskewImage(const Image *image,const double threshold,
00253 %        ExceptionInfo *exception)
00254 %
00255 %  A description of each parameter follows:
00256 %
00257 %    o image: the image.
00258 %
00259 %    o threshold: separate background from foreground.
00260 %
00261 %    o exception: return any errors or warnings in this structure.
00262 %
00263 */
00264 
00265 typedef struct _RadonInfo
00266 {
00267   CacheType
00268     type;
00269 
00270   unsigned long
00271     width,
00272     height;
00273 
00274   MagickSizeType
00275     length;
00276 
00277   MagickBooleanType
00278     mapped;
00279 
00280   char
00281     path[MaxTextExtent];
00282 
00283   int
00284     file;
00285 
00286   unsigned short
00287     *cells;
00288 } RadonInfo;
00289 
00290 static RadonInfo *DestroyRadonInfo(RadonInfo *radon_info)
00291 {
00292   assert(radon_info != (RadonInfo *) NULL);
00293   switch (radon_info->type)
00294   {
00295     case MemoryCache:
00296     {
00297       if (radon_info->mapped == MagickFalse)
00298         radon_info->cells=(unsigned short *) RelinquishMagickMemory(
00299           radon_info->cells);
00300       else
00301         radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,
00302           (size_t) radon_info->length);
00303       RelinquishMagickResource(MemoryResource,radon_info->length);
00304       break;
00305     }
00306     case MapCache:
00307     {
00308       radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,(size_t)
00309         radon_info->length);
00310       RelinquishMagickResource(MapResource,radon_info->length);
00311     }
00312     case DiskCache:
00313     {
00314       if (radon_info->file != -1)
00315         (void) close(radon_info->file);
00316       (void) RelinquishUniqueFileResource(radon_info->path);
00317       RelinquishMagickResource(DiskResource,radon_info->length);
00318       break;
00319     }
00320     default:
00321       break;
00322   }
00323   return((RadonInfo *) RelinquishMagickMemory(radon_info));
00324 }
00325 
00326 static MagickBooleanType ResetRadonCells(RadonInfo *radon_info)
00327 {
00328   long
00329     y;
00330 
00331   register long
00332     x;
00333 
00334   ssize_t
00335     count;
00336 
00337   unsigned short
00338     value;
00339 
00340   if (radon_info->type != DiskCache)
00341     {
00342       (void) ResetMagickMemory(radon_info->cells,0,(size_t) radon_info->length);
00343       return(MagickTrue);
00344     }
00345   value=0;
00346   (void) MagickSeek(radon_info->file,0,SEEK_SET);
00347   for (y=0; y < (long) radon_info->height; y++)
00348   {
00349     for (x=0; x < (long) radon_info->width; x++)
00350     {
00351       count=write(radon_info->file,&value,sizeof(*radon_info->cells));
00352       if (count != (ssize_t) sizeof(*radon_info->cells))
00353         break;
00354     }
00355     if (x < (long) radon_info->width)
00356       break;
00357   }
00358   return(y < (long) radon_info->height ? MagickFalse : MagickTrue);
00359 }
00360 
00361 static RadonInfo *AcquireRadonInfo(const Image *image,const unsigned long width,
00362   const unsigned long height,ExceptionInfo *exception)
00363 {
00364   MagickBooleanType
00365     status;
00366 
00367   RadonInfo
00368     *radon_info;
00369 
00370   radon_info=(RadonInfo *) AcquireMagickMemory(sizeof(*radon_info));
00371   if (radon_info == (RadonInfo *) NULL)
00372     return((RadonInfo *) NULL);
00373   (void) ResetMagickMemory(radon_info,0,sizeof(*radon_info));
00374   radon_info->width=width;
00375   radon_info->height=height;
00376   radon_info->length=(MagickSizeType) width*height*sizeof(*radon_info->cells);
00377   radon_info->type=MemoryCache;
00378   status=AcquireMagickResource(AreaResource,radon_info->length);
00379   if ((status != MagickFalse) &&
00380       (radon_info->length == (MagickSizeType) ((size_t) radon_info->length)))
00381     {
00382       status=AcquireMagickResource(MemoryResource,radon_info->length);
00383       if (status != MagickFalse)
00384         {
00385           radon_info->mapped=MagickFalse;
00386           radon_info->cells=(unsigned short *) AcquireMagickMemory((size_t)
00387             radon_info->length);
00388           if (radon_info->cells == (unsigned short *) NULL)
00389             {
00390               radon_info->mapped=MagickTrue;
00391               radon_info->cells=(unsigned short *) MapBlob(-1,IOMode,0,(size_t)
00392                 radon_info->length);
00393             }
00394           if (radon_info->cells == (unsigned short *) NULL)
00395             RelinquishMagickResource(MemoryResource,radon_info->length);
00396         }
00397     }
00398   radon_info->file=(-1);
00399   if (radon_info->cells == (unsigned short *) NULL)
00400     {
00401       status=AcquireMagickResource(DiskResource,radon_info->length);
00402       if (status == MagickFalse)
00403         {
00404           (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
00405             "CacheResourcesExhausted","`%s'",image->filename);
00406           return(DestroyRadonInfo(radon_info));
00407         }
00408       radon_info->type=DiskCache;
00409       (void) AcquireMagickResource(MemoryResource,radon_info->length);
00410       radon_info->file=AcquireUniqueFileResource(radon_info->path);
00411       if (radon_info->file == -1)
00412         return(DestroyRadonInfo(radon_info));
00413       status=AcquireMagickResource(MapResource,radon_info->length);
00414       if (status != MagickFalse)
00415         {
00416           status=ResetRadonCells(radon_info);
00417           if (status != MagickFalse)
00418             {
00419               radon_info->cells=(unsigned short *) MapBlob(radon_info->file,
00420                 IOMode,0,(size_t) radon_info->length);
00421               if (radon_info->cells != (unsigned short *) NULL)
00422                 radon_info->type=MapCache;
00423               else
00424                 RelinquishMagickResource(MapResource,radon_info->length);
00425             }
00426         }
00427     }
00428   return(radon_info);
00429 }
00430 
00431 static inline size_t MagickMin(const size_t x,const size_t y)
00432 {
00433   if (x < y)
00434     return(x);
00435   return(y);
00436 }
00437 
00438 static inline ssize_t ReadRadonCell(const RadonInfo *radon_info,
00439   const off_t offset,const size_t length,unsigned char *buffer)
00440 {
00441   register ssize_t
00442     i;
00443 
00444   ssize_t
00445     count;
00446 
00447 #if !defined(MAGICKCORE_HAVE_PPREAD)
00448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00449   #pragma omp critical
00450 #endif
00451   {
00452     i=(-1);
00453     if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00454       {
00455 #endif
00456         count=0;
00457         for (i=0; i < (ssize_t) length; i+=count)
00458         {
00459 #if !defined(MAGICKCORE_HAVE_PPREAD)
00460           count=read(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00461             SSIZE_MAX));
00462 #else
00463           count=pread(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00464             SSIZE_MAX),(off_t) (offset+i));
00465 #endif
00466           if (count > 0)
00467             continue;
00468           count=0;
00469           if (errno != EINTR)
00470             {
00471               i=(-1);
00472               break;
00473             }
00474         }
00475 #if !defined(MAGICKCORE_HAVE_PPREAD)
00476       }
00477   }
00478 #endif
00479   return(i);
00480 }
00481 
00482 static inline ssize_t WriteRadonCell(const RadonInfo *radon_info,
00483   const off_t offset,const size_t length,const unsigned char *buffer)
00484 {
00485   register ssize_t
00486     i;
00487 
00488   ssize_t
00489     count;
00490 
00491 #if !defined(MAGICKCORE_HAVE_PPWRITE)
00492 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00493   #pragma omp critical
00494 #endif
00495   {
00496     if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00497       {
00498 #endif
00499         count=0;
00500         for (i=0; i < (ssize_t) length; i+=count)
00501         {
00502 #if !defined(MAGICKCORE_HAVE_PPWRITE)
00503           count=write(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00504             SSIZE_MAX));
00505 #else
00506           count=pwrite(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00507             SSIZE_MAX),(off_t) (offset+i));
00508 #endif
00509           if (count > 0)
00510             continue;
00511           count=0;
00512           if (errno != EINTR)
00513             {
00514               i=(-1);
00515               break;
00516             }
00517         }
00518 #if !defined(MAGICKCORE_HAVE_PPWRITE)
00519       }
00520   }
00521 #endif
00522   return(i);
00523 }
00524 
00525 static inline unsigned short GetRadonCell(const RadonInfo *radon_info,
00526   const long x,const long y)
00527 {
00528   off_t
00529     i;
00530 
00531   unsigned short
00532     value;
00533 
00534   i=(off_t) radon_info->height*x+y;
00535   if ((i < 0) ||
00536       ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00537     return(0);
00538   if (radon_info->type != DiskCache)
00539     return(radon_info->cells[i]);
00540   value=0;
00541   (void) ReadRadonCell(radon_info,i*sizeof(*radon_info->cells),
00542     sizeof(*radon_info->cells),(unsigned char *) &value);
00543   return(value);
00544 }
00545 
00546 static inline MagickBooleanType SetRadonCell(const RadonInfo *radon_info,
00547   const long x,const long y,const unsigned short value)
00548 {
00549   off_t
00550     i;
00551 
00552   ssize_t
00553     count;
00554 
00555   i=(off_t) radon_info->height*x+y;
00556   if ((i < 0) ||
00557       ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00558     return(MagickFalse);
00559   if (radon_info->type != DiskCache)
00560     {
00561       radon_info->cells[i]=value;
00562       return(MagickTrue);
00563     }
00564   count=WriteRadonCell(radon_info,i*sizeof(*radon_info->cells),
00565     sizeof(*radon_info->cells),(unsigned char *) &value);
00566   if (count != (ssize_t) sizeof(*radon_info->cells))
00567     return(MagickFalse);
00568   return(MagickTrue);
00569 }
00570 
00571 static void RadonProjection(RadonInfo *source_cells,
00572   RadonInfo *destination_cells,const long sign,unsigned long *projection)
00573 {
00574   RadonInfo
00575     *swap;
00576 
00577   register long
00578     x;
00579 
00580   register RadonInfo
00581     *p,
00582     *q;
00583 
00584   unsigned long
00585     step;
00586 
00587   p=source_cells;
00588   q=destination_cells;
00589   for (step=1; step < p->width; step*=2)
00590   {
00591     for (x=0; x < (long) p->width; x+=2*step)
00592     {
00593       long
00594         y;
00595 
00596       register long
00597         i;
00598 
00599       unsigned short
00600         cell;
00601 
00602       for (i=0; i < (long) step; i++)
00603       {
00604         for (y=0; y < (long) (p->height-i-1); y++)
00605         {
00606           cell=GetRadonCell(p,x+i,y);
00607           (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00608           (void) SetRadonCell(q,x+2*i+1,y,cell+GetRadonCell(p,x+i+step,y+i+1));
00609         }
00610         for ( ; y < (long) (p->height-i); y++)
00611         {
00612           cell=GetRadonCell(p,x+i,y);
00613           (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00614           (void) SetRadonCell(q,x+2*i+1,y,cell);
00615         }
00616         for ( ; y < (long) p->height; y++)
00617         {
00618           cell=GetRadonCell(p,x+i,y);
00619           (void) SetRadonCell(q,x+2*i,y,cell);
00620           (void) SetRadonCell(q,x+2*i+1,y,cell);
00621         }
00622       }
00623     }
00624     swap=p;
00625     p=q;
00626     q=swap;
00627   }
00628 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00629   #pragma omp parallel for
00630 #endif
00631   for (x=0; x < (long) p->width; x++)
00632   {
00633     register long
00634       y;
00635 
00636     unsigned long
00637       sum;
00638 
00639     sum=0;
00640     for (y=0; y < (long) (p->height-1); y++)
00641     {
00642       long
00643         delta;
00644 
00645       delta=GetRadonCell(p,x,y)-(long) GetRadonCell(p,x,y+1);
00646       sum+=delta*delta;
00647     }
00648     projection[p->width+sign*x-1]=sum;
00649   }
00650 }
00651 
00652 static MagickBooleanType RadonTransform(const Image *image,
00653   const double threshold,unsigned long *projection,ExceptionInfo *exception)
00654 {
00655   long
00656     y;
00657 
00658   MagickBooleanType
00659     status;
00660 
00661   RadonInfo
00662     *destination_cells,
00663     *source_cells;
00664 
00665   register long
00666     i;
00667 
00668   unsigned char
00669     byte;
00670 
00671   unsigned long
00672     count,
00673     width;
00674 
00675   unsigned short
00676     bits[256];
00677 
00678   ViewInfo
00679     *image_view;
00680 
00681   for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00682   source_cells=AcquireRadonInfo(image,width,image->rows,exception);
00683   destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
00684   if ((source_cells == (RadonInfo *) NULL) ||
00685       (destination_cells == (RadonInfo *) NULL))
00686     {
00687       if (destination_cells != (RadonInfo *) NULL)
00688         destination_cells=DestroyRadonInfo(destination_cells);
00689       if (source_cells != (RadonInfo *) NULL)
00690         source_cells=DestroyRadonInfo(source_cells);
00691       return(MagickFalse);
00692     }
00693   if (ResetRadonCells(source_cells) == MagickFalse)
00694     {
00695       destination_cells=DestroyRadonInfo(destination_cells);
00696       source_cells=DestroyRadonInfo(source_cells);
00697       return(MagickFalse);
00698     }
00699   for (i=0; i < 256; i++)
00700   {
00701     byte=(unsigned char) i;
00702     for (count=0; byte != 0; byte>>=1)
00703       count+=byte & 0x01;
00704     bits[i]=(unsigned short) count;
00705   }
00706   status=MagickTrue;
00707   image_view=AcquireCacheView(image);
00708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00709   #pragma omp parallel for schedule(dynamic,8) shared(status)
00710 #endif
00711   for (y=0; y < (long) image->rows; y++)
00712   {
00713     register const PixelPacket
00714       *p;
00715 
00716     register long
00717       i,
00718       x;
00719 
00720     unsigned long
00721       bit,
00722       byte;
00723 
00724     if (status == MagickFalse)
00725       continue;
00726     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00727     if (p == (const PixelPacket *) NULL)
00728       {
00729         status=MagickFalse;
00730         continue;
00731       }
00732     bit=0;
00733     byte=0;
00734     i=(long) (image->columns+7)/8;
00735     for (x=0; x < (long) image->columns; x++)
00736     {
00737       byte<<=1;
00738       if (((MagickRealType) p->red < threshold) ||
00739           ((MagickRealType) p->green < threshold) ||
00740           ((MagickRealType) p->blue < threshold))
00741         byte|=0x01;
00742       bit++;
00743       if (bit == 8)
00744         {
00745           (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00746           bit=0;
00747           byte=0;
00748         }
00749       p++;
00750     }
00751     if (bit != 0)
00752       {
00753         byte<<=(8-bit);
00754         (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00755       }
00756   }
00757   RadonProjection(source_cells,destination_cells,-1,projection);
00758   (void) ResetRadonCells(source_cells);
00759 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00760   #pragma omp parallel for schedule(dynamic,8) shared(status)
00761 #endif
00762   for (y=0; y < (long) image->rows; y++)
00763   {
00764     register const PixelPacket
00765       *p;
00766 
00767     register long
00768       i,
00769       x;
00770 
00771     unsigned long
00772       bit,
00773       byte;
00774 
00775     if (status == MagickFalse)
00776       continue;
00777     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00778     if (p == (const PixelPacket *) NULL)
00779       {
00780         status=MagickFalse;
00781         continue;
00782       }
00783     bit=0;
00784     byte=0;
00785     i=0;
00786     for (x=0; x < (long) image->columns; x++)
00787     {
00788       byte<<=1;
00789       if (((MagickRealType) p->red < threshold) ||
00790           ((MagickRealType) p->green < threshold) ||
00791           ((MagickRealType) p->blue < threshold))
00792         byte|=0x01;
00793       bit++;
00794       if (bit == 8)
00795         {
00796           (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00797           bit=0;
00798           byte=0;
00799         }
00800       p++;
00801     }
00802     if (bit != 0)
00803       {
00804         byte<<=(8-bit);
00805         (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00806       }
00807   }
00808   RadonProjection(source_cells,destination_cells,1,projection);
00809   image_view=DestroyCacheView(image_view);
00810   destination_cells=DestroyRadonInfo(destination_cells);
00811   source_cells=DestroyRadonInfo(source_cells);
00812   return(MagickTrue);
00813 }
00814 
00815 static void GetImageBackgroundColor(Image *image,const long offset,
00816   ExceptionInfo *exception)
00817 {
00818   long
00819     y;
00820 
00821   MagickPixelPacket
00822     background;
00823 
00824   MagickRealType
00825     count;
00826 
00827   ViewInfo
00828     *image_view;
00829 
00830   /*
00831     Compute average background color.
00832   */
00833   if (offset <= 0)
00834     return;
00835   GetMagickPixelPacket(image,&background);
00836   count=0.0;
00837   image_view=AcquireCacheView(image);
00838   for (y=0; y < (long) image->rows; y++)
00839   {
00840     register const PixelPacket
00841       *p;
00842 
00843     register long
00844       x;
00845 
00846     if ((y >= offset) && (y < ((long) image->rows-offset)))
00847       continue;
00848     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00849     if (p == (const PixelPacket *) NULL)
00850       continue;
00851     for (x=0; x < (long) image->columns; x++)
00852     {
00853       if ((x >= offset) && (x < ((long) image->columns-offset)))
00854         continue;
00855       background.red+=QuantumScale*p->red;
00856       background.green+=QuantumScale*p->green;
00857       background.blue+=QuantumScale*p->blue;
00858       background.opacity+=QuantumScale*p->opacity;
00859       count++;
00860       p++;
00861     }
00862   }
00863   image_view=DestroyCacheView(image_view);
00864   image->background_color.red=RoundToQuantum((MagickRealType) QuantumRange*
00865     background.red/count);
00866   image->background_color.green=RoundToQuantum((MagickRealType) QuantumRange*
00867     background.green/count);
00868   image->background_color.blue=RoundToQuantum((MagickRealType) QuantumRange*
00869     background.blue/count);
00870   image->background_color.opacity=RoundToQuantum((MagickRealType) QuantumRange*
00871     background.opacity/count);
00872 }
00873 
00874 MagickExport Image *DeskewImage(const Image *image,const double threshold,
00875   ExceptionInfo *exception)
00876 {
00877   const char
00878     *artifact;
00879 
00880   double
00881     degrees,
00882     sum;
00883 
00884   Image
00885     *clone_image,
00886     *crop_image,
00887     *median_image,
00888     *rotate_image;
00889 
00890   long
00891     skew;
00892 
00893   MagickBooleanType
00894     status;
00895 
00896   RectangleInfo
00897     geometry;
00898 
00899   register long
00900     i;
00901 
00902   unsigned long
00903     max_projection,
00904     *projection,
00905     width;
00906 
00907   /*
00908     Compute deskew angle.
00909   */
00910   for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00911   projection=(unsigned long *) AcquireQuantumMemory((size_t) (2*width-1),
00912     sizeof(*projection));
00913   if (projection == (unsigned long *) NULL)
00914     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00915   status=RadonTransform(image,threshold,projection,exception);
00916   if (status == MagickFalse)
00917     {
00918       projection=(unsigned long *) RelinquishMagickMemory(projection);
00919       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00920     }
00921   max_projection=0;
00922   sum=0.0;
00923   skew=0;
00924   for (i=0; i < (long) (2*width-1); i++)
00925   {
00926     if (projection[i] > max_projection)
00927       {
00928         skew=i-(long) width+1;
00929         max_projection=projection[i];
00930       }
00931     sum+=projection[i];
00932   }
00933   projection=(unsigned long *) RelinquishMagickMemory(projection);
00934   /*
00935     Deskew image.
00936   */
00937   degrees=RadiansToDegrees(-atan((double) skew/width/8));
00938   if (image->debug != MagickFalse)
00939     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew angle: %g",
00940       degrees);
00941   artifact=GetImageArtifact(image,"deskew:auto-crop");
00942   if (artifact == (const char *) NULL)
00943     return(RotateImage(image,degrees,exception));
00944   /*
00945     Auto-crop image.
00946   */
00947   clone_image=CloneImage(image,0,0,MagickTrue,exception);
00948   if (clone_image == (Image *) NULL)
00949     return((Image *) NULL);
00950   GetImageBackgroundColor(clone_image,atol(artifact),exception);
00951   rotate_image=RotateImage(clone_image,degrees,exception);
00952   clone_image=DestroyImage(clone_image);
00953   if (rotate_image == (Image *) NULL)
00954     return((Image *) NULL);
00955   median_image=MedianFilterImage(rotate_image,0.0,exception);
00956   if (median_image == (Image *) NULL)
00957     {
00958       rotate_image=DestroyImage(rotate_image);
00959       return((Image *) NULL);
00960     }
00961   geometry=GetImageBoundingBox(median_image,exception);
00962   median_image=DestroyImage(median_image);
00963   if (image->debug != MagickFalse)
00964     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew geometry: "
00965       "%lux%lu%+ld%+ld",geometry.width,geometry.height,geometry.x,geometry.y);
00966   crop_image=CropImage(rotate_image,&geometry,exception);
00967   rotate_image=DestroyImage(rotate_image);
00968   return(crop_image);
00969 }
00970 
00971 /*
00972 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00973 %                                                                             %
00974 %                                                                             %
00975 %                                                                             %
00976 +   I n t e g r a l R o t a t e I m a g e                                     %
00977 %                                                                             %
00978 %                                                                             %
00979 %                                                                             %
00980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00981 %
00982 %  IntegralRotateImage()  rotates the image an integral of 90 degrees.  It
00983 %  allocates the memory necessary for the new Image structure and returns a
00984 %  pointer to the rotated image.
00985 %
00986 %  The format of the IntegralRotateImage method is:
00987 %
00988 %      Image *IntegralRotateImage(const Image *image,unsigned long rotations,
00989 %        ExceptionInfo *exception)
00990 %
00991 %  A description of each parameter follows.
00992 %
00993 %    o image: the image.
00994 %
00995 %    o rotations: Specifies the number of 90 degree rotations.
00996 %
00997 */
00998 static Image *IntegralRotateImage(const Image *image,unsigned long rotations,
00999   ExceptionInfo *exception)
01000 {
01001 #define TileHeight  128
01002 #define TileWidth  128
01003 #define RotateImageTag  "Rotate/Image"
01004 
01005   Image
01006     *rotate_image;
01007 
01008   long
01009     progress,
01010     y;
01011 
01012   MagickBooleanType
01013     status;
01014 
01015   RectangleInfo
01016     page;
01017 
01018   ViewInfo
01019     *image_view,
01020     *rotate_view;
01021 
01022   /*
01023     Initialize rotated image attributes.
01024   */
01025   assert(image != (Image *) NULL);
01026   page=image->page;
01027   rotations%=4;
01028   if ((rotations == 1) || (rotations == 3))
01029     rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
01030       exception);
01031   else
01032     rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01033       exception);
01034   if (rotate_image == (Image *) NULL)
01035     return((Image *) NULL);
01036   /*
01037     Integral rotate the image.
01038   */
01039   status=MagickTrue;
01040   progress=0;
01041   image_view=AcquireCacheView(image);
01042   rotate_view=AcquireCacheView(rotate_image);
01043   switch (rotations)
01044   {
01045     case 0:
01046     {
01047       /*
01048         Rotate 0 degrees.
01049       */
01050 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01051   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
01052 #endif
01053       for (y=0; y < (long) image->rows; y++)
01054       {
01055         MagickBooleanType
01056           sync;
01057 
01058         register const IndexPacket
01059           *indexes;
01060 
01061         register const PixelPacket
01062           *p;
01063 
01064         register IndexPacket
01065           *rotate_indexes;
01066 
01067         register PixelPacket
01068           *q;
01069 
01070         if (status == MagickFalse)
01071           continue;
01072         p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
01073           exception);
01074         q=QueueCacheViewAuthenticPixels(rotate_view,0,y,
01075           rotate_image->columns,1,exception);
01076         if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01077           {
01078             status=MagickFalse;
01079             continue;
01080           }
01081         indexes=GetCacheViewVirtualIndexQueue(image_view);
01082         (void) CopyMagickMemory(q,p,image->columns*sizeof(*p));
01083         if (indexes != (IndexPacket *) NULL)
01084           {
01085             rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01086             (void) CopyMagickMemory(rotate_indexes,indexes,image->columns*
01087               sizeof(*indexes));
01088           }
01089         sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01090         if (sync ==