Ejemplo n.º 1
0
static MagickRealType GetAbsoluteError(const Image *image,
  const Image *reconstruct_image,ExceptionInfo *exception)
{
  long
    y;

  MagickPixelPacket
    image_pixel,
    reconstruct_pixel;

  MagickRealType
    distortion;

  register const IndexPacket
    *indexes,
    *reconstruct_indexes;

  register const PixelPacket
    *p,
    *q;

  register long
    x;

  ViewInfo
    *image_view,
    *reconstruct_view;

  /*
    Compute the absolute difference in pixels between two images.
  */
  GetMagickPixelPacket(image,&image_pixel);
  GetMagickPixelPacket(reconstruct_image,&reconstruct_pixel);
  distortion=0.0;
  image_view=OpenCacheView(image);
  reconstruct_view=OpenCacheView(reconstruct_image);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
      exception);
    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
      break;
    indexes=AcquireCacheViewIndexes(image_view);
    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
    for (x=0; x < (long) image->columns; x++)
    {
      SetMagickPixelPacket(image,p,indexes+x,&image_pixel);
      SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,
        &reconstruct_pixel);
      if (IsMagickColorSimilar(&image_pixel,&reconstruct_pixel) == MagickFalse)
        distortion++;
      p++;
      q++;
    }
  }
  reconstruct_view=CloseCacheView(reconstruct_view);
  image_view=CloseCacheView(image_view);
  return(distortion);
}
Ejemplo n.º 2
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   D e s t r o y P i x e l I t e r a t o r                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  DestroyPixelIterator() deallocates resources associated with a PixelIterator.
%
%  The format of the DestroyPixelIterator method is:
%
%      PixelIterator *DestroyPixelIterator(PixelIterator *iterator)
%
%  A description of each parameter follows:
%
%    o iterator: the pixel iterator.
%
*/
WandExport PixelIterator *DestroyPixelIterator(PixelIterator *iterator)
{
  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == WandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->view=CloseCacheView(iterator->view);
  iterator->pixel_wands=DestroyPixelWands(iterator->pixel_wands,
    iterator->region.width);
  iterator->exception=DestroyExceptionInfo(iterator->exception);
  iterator->signature=(~WandSignature);
  RelinquishWandId(iterator->id);
  iterator=(PixelIterator *) RelinquishMagickMemory(iterator);
  return(iterator);
}
Ejemplo n.º 3
0
MagickExport Image *DistortImage(Image *image,const DistortImageMethod method,
  const unsigned long number_arguments,const double *arguments,
  MagickBooleanType bestfit, ExceptionInfo *exception)
{
#define DistortImageTag  "Distort/Image"

  double
    coefficients[9],
    validity;

  Image
    *distort_image;

  register long
    i,
    x;

  long
    j,
    y;

  PointInfo
    point;          /* point to sample (center of filtered resample of area) */

  MagickPixelPacket
    pixel,          /* pixel to assign to distorted image */
    invalid;  /* the color to assign when distort result is invalid */

  register IndexPacket
    *indexes;

  register PixelPacket
    *q;

  ResampleFilter
    *resample_filter;

  ViewInfo
    *distort_view;

  MagickBooleanType
    status;

  RectangleInfo
    geometry;

  const char
     *property;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickSignature);
  (void) ResetMagickMemory(coefficients,0,sizeof(coefficients));

  /*
    Convert input arguments into mapping coefficents for distortion
  */
  switch (method)
  {
    case AffineDistortion:
    {
      double
        **matrix;

      PointInfo
        *points;

      /* Affine Distortion

            u =   c0*x + c2*y + c4

            v =   c1*x + c3*y + c5

         Input Arguments are pairs of distorted coodinates (minimum 3 sets)
         Which will be used to generate the coefficients of the above.
            u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3, ...
      */
      if (number_arguments != 12) {  /* to be removed */
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Affine",
                     "Needs 12 numbers");
        return((Image *) NULL);
      }
      if (number_arguments%4 != 0) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Affine",
                     "Requires pairs of coords (4 numbers U,V,X,Y)");
        return((Image *) NULL);
      }
      if (number_arguments < 12) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Affine",
                     "Minimum of 3 pairs of coords needed (12 numbers)");
        return((Image *) NULL);
      }
      points=(PointInfo *) AcquireQuantumMemory((number_arguments)/2UL,
        sizeof(*points));
      if (points == (PointInfo *) NULL)
        ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
      for (i=0; i < (long) number_arguments; i++)
        if ((i % 2 ) == 0)
          points[i/2].x=arguments[i];
        else
          points[i/2].y=arguments[i];
      matrix = AcquireMagickMatrix(6UL,6UL);
      if (matrix == (double **) NULL)
      {
        points=(PointInfo *) RelinquishMagickMemory(points);
        ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
      }
      status=SolveAffineDistortion(6UL,points,matrix,coefficients);
      matrix = RelinquishMagickMatrix(matrix, 6UL);
      points = (PointInfo *) RelinquishMagickMemory(points);
      if ( status == MagickFalse ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Affine",
                     "Degenerate Result");
        return((Image *) NULL);
      }
      break;
    }
    case AffineProjectionDistortion:
    {
      /*
        Arguments: Affine Matrix (forward mapping)
           sx, rx, ry, sy, tx, ty
      */
      if (number_arguments != 6) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort AffineProjection",
                     "Needs 6 numbers");
        return((Image *) NULL);
      }
      InvertAffineCoefficients(arguments, coefficients);
      break;
    }
    case BilinearDistortion:
    {
      double
        **matrix;

      PointInfo
        *points;

      /* Bilinear Distortion (reversed)

            u = c0*x + c1*y + c2*x*y + c3;

            v = c4*x + c5*y + c6*x*y + c7;

         Input Arguments are pairs of distorted coodinates (minimum 4 pairs)
         Which will be used to generate the coefficients of the above.
            u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3,  u4,v4, x4,y4 ...
      */
      if (number_arguments != 16) {  /* to be removed */
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Needs 16 numbers");
        return((Image *) NULL);
      }
      if (number_arguments%4 != 0) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Requires pairs of coords (4 numbers U,V,X,Y)");
        return((Image *) NULL);
      }
      if (number_arguments < 16) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Minimum of 4 pairs of coords needed (16 numbers)");
        return((Image *) NULL);
      }
      points=(PointInfo *) AcquireQuantumMemory((number_arguments+1UL)/2UL,
        sizeof(*points));
      if (points == (PointInfo *) NULL)
        ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
      for (i=0; i < (long) number_arguments; i++)
        if ((i % 2 ) == 0)
          points[i/2].x=arguments[i];
        else
          points[i/2].y=arguments[i];
      matrix = AcquireMagickMatrix(8UL,8UL);
      if (matrix == (double **) NULL)
      {
        points=(PointInfo *) RelinquishMagickMemory(points);
        ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
      }
      status=SolveBilinearDistortion(8UL,points,matrix,coefficients);
      matrix = RelinquishMagickMatrix(matrix, 8UL);
      points = (PointInfo *) RelinquishMagickMemory(points);
      if ( status == MagickFalse ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Degenerate Result");
        return((Image *) NULL);
      }
      break;
    }
    case PerspectiveDistortion:
    {
      double
        **matrix;

      PointInfo
        *points;

      /* Perspective Distortion (a ratio of affine distortions)

                 p     c0*x + c1*y + c2
            u = --- = ------------------
                 r     c6*x + c7*y + 1

                 q     c3*x + c4*y + c5
            v = --- = ------------------
                 r     c6*x + c7*y + 1

            c8 = Sign of 'r', or the denominator affine, for the actual image.
                 This determines what part of the distorted image is 'ground'
                 side of the horizon, the other part is 'sky' or invalid.

         Input Arguments are pairs of distorted coodinates (minimum 4 pairs)
         Which will be used to generate the coefficients of the above.
            u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3,  u4,v4, x4,y4 ...
      */
      if (number_arguments != 16) {  /* to be removed */
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Needs 16 numbers");
        return((Image *) NULL);
      }
      if (number_arguments%4 != 0) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Requires pairs of coords (4 numbers U,V,X,Y)");
        return((Image *) NULL);
      }
      if (number_arguments < 16) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Bilinear",
                     "Minimum of 4 pairs of coords needed (16 numbers)");
        return((Image *) NULL);
      }
      points=(PointInfo *) AcquireQuantumMemory((number_arguments+1UL)/2UL,
        sizeof(*points));
      if (points == (PointInfo *) NULL)
        ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
      for (i=0; i < (long) number_arguments; i++)
        if ((i % 2 ) == 0)
          points[i/2].x=arguments[i];
        else
          points[i/2].y=arguments[i];
      matrix = AcquireMagickMatrix(8UL,8UL);
      if (matrix == (double **) NULL)
      {
        points=(PointInfo *) RelinquishMagickMemory(points);
        ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
      }
      status=SolvePerspectiveDistortion(8UL,points,matrix,coefficients);
      matrix = RelinquishMagickMatrix(matrix, 8UL);
      points = (PointInfo *) RelinquishMagickMemory(points);
      if ( status == MagickFalse ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'","distort Prespective",
                     "Degenerate Result");
        return((Image *) NULL);
      }
      /*
        What is the 'ground' of the perspective distortion.
        Just find the sign of denomitor affine for any image coordinate.
        This is a 9'th coefficient!
      */
      coefficients[8] = coefficients[6]*arguments[number_arguments-2]
                  + coefficients[7]*arguments[number_arguments-1] + 1.0;
      coefficients[8] = (coefficients[8] < 0.0) ? -1.0 : +1.0;
      break;
    }
    case PerspectiveProjectionDistortion:
    {
      /*
        Arguments: Perspective Coefficents (forward mapping)
      */
      if (number_arguments != 8) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'",
                     "distort PerspectiveProjection", "Needs 8 numbers");
        return((Image *) NULL);
      }
      InvertPerspectiveCoefficients(arguments, coefficients);
      /*
        What is the 'ground' of the perspective distortion?  For a forward
        mapped perspective the images 0,0 coord will map to c2,c5  in the
        distorted image, so get the sign of denominator of that.
        This is a 9'th coefficient!
      */
      coefficients[8] = coefficients[6]*arguments[2]
                  + coefficients[7]*arguments[5] + 1.0;
      coefficients[8] = (coefficients[8] < 0.0) ? -1.0 : +1.0;
      break;
    }
    case ScaleRotateTranslateDistortion:
    {
      double
        cosine, sine,
        x,y,sx,sy,a,nx,ny;

      /*
         Argument options, by number of arguments given:
           7: x,y, sx,sy, a, nx,ny
           6: x,y,   s,   a, nx,ny
           5: x,y, sx,sy, a
           4: x,y,   s,   a
           3: x,y,        a
           2:        s,   a
           1:             a
         Where actions are (in order of application)
            x,y     'center' of transforms     (default = image center)
            sx,sy   scale image by this amount (default = 1)
            a       angle of rotation          (argument required)
            nx,ny   move 'center' here         (default = no movement)
         And convert to affine mapping coefficients
      */
      x = nx = (double)image->columns/2.0;
      y = ny = (double)image->rows/2.0;
      if ( bestfit ) {
        x = nx += (double)image->page.x;
        y = ny += (double)image->page.y;
      }
      sx = sy = 1.0;
      switch ( number_arguments ) {
      case 0:
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'",
                     "distort ScaleTranslateRotate",
                     "Needs at least 1 argument");
        return((Image *) NULL);
      case 1:
        a = arguments[0];
        break;
      case 2:
        sx = sy = arguments[0];
        a = arguments[1];
        break;
      default:
        x = nx = arguments[0];
        y = ny = arguments[1];
        switch ( number_arguments ) {
        case 3:
          a = arguments[2];
          break;
        case 4:
          sx = sy = arguments[2];
          a = arguments[3];
          break;
        case 5:
          sx = arguments[2];
          sy = arguments[3];
          a = arguments[4];
          break;
        case 6:
          sx = sy = arguments[2];
          a = arguments[3];
          nx = arguments[4];
          ny = arguments[5];
          break;
        case 7:
          sx = arguments[2];
          sy = arguments[3];
          a = arguments[4];
          nx = arguments[5];
          ny = arguments[6];
          break;
        default:
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'",
                     "distort ScaleTranslateRotate",
                     "Too Many Arguments (7 or less)");
          return((Image *) NULL);
        }
        break;
      }
      a=DegreesToRadians(a);
      cosine=cos(a);
      sine=sin(a);
      coefficients[0]=cosine/sx;
      coefficients[1]=(-sine)/sy;
      coefficients[2]=sine/sx;
      coefficients[3]=cosine/sy;
      coefficients[4]=x-nx*coefficients[0]-ny*coefficients[2];
      coefficients[5]=y-nx*coefficients[1]-ny*coefficients[3];
      break;
    }
    case ArcDistortion:
    {
      /* Arc Distortion
         Args: arc_width  rotate  top_edge_radius  bottom_edge_radius
         All but first argument are optional
            arc_width      The angle over which to arc the image side-to-side
            rotate         Angle to rotate image from vertical center
            top_radius     Set top edge of source image at this radius
            bottom_radius  Set bootom edge to this radius (radial scaling)

         By default, if the radii arguments are nor provided the image radius
         is calculated so the horizontal center-line is fits the given arc
         without scaling.

         The output image size is ALWAYS adjusted to contain the whole image,
         and an offset is given to position image relative to the 0,0 point of
         the origin, allowing users to use relative positioning onto larger
         background (via -flatten).

         The arguments are converted to these coefficents
            c0: angle for center of source image
            c1: angle scale for mapping to source image
            c2: radius for top of source image
            c3: radius scale for mapping source image
            c4: centerline of arc within source image

         Note the coefficents use a center angle, so asymptotic join is
         furthest from both sides of the source image. This also means that
         for arc angles greater than 360 the sides of the image will be
         trimmed equally.
      */
      if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'", "distort Arc",
                     "Arc Angle Too Small");
        return((Image *) NULL);
      }
      if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                     "InvalidArgument","%s : '%s'", "distort Arc",
                     "Outer Radius Too Small");
        return((Image *) NULL);
      }
      coefficients[0] = -MagickPI/2.0;
      if ( number_arguments >= 1 )
        coefficients[1] = DegreesToRadians(arguments[0]);
      else
        coefficients[1] = MagickPI/2.0;
      if ( number_arguments >= 2 )
        coefficients[0] += DegreesToRadians(arguments[1]);
      coefficients[0] -= MagickRound(coefficients[0]/(2.0*MagickPI))
                             *2.0*MagickPI;
      coefficients[3] = 1.0*image->rows-1;
      coefficients[2] = 1.0*image->columns/coefficients[1] + coefficients[3]/2.0;
      if ( number_arguments >= 3 ) {
        if ( number_arguments >= 4 )
          coefficients[3] = arguments[2] - arguments[3];
        else
          coefficients[3] *= arguments[2]/coefficients[2];
        coefficients[2] = arguments[2];
      }
      coefficients[4] = (1.0*image->columns-1.0)/2.0;
      /* Always work out the 'best fit' for Arc Distort (required) */
      bestfit = MagickTrue;
      break;
    }
    default:
      break;
  }

  /*
    Determine the size and offset for a 'bestfit' destination.
    Usally the four corners of the source image is enough.
  */

  /* default output image bounds, when no 'bestfit' is requested */
  geometry.width=image->columns;
  geometry.height=image->rows;
  geometry.x=0;
  geometry.y=0;
  point.x=0.0;
  point.y=0.0;

  if ( bestfit ) {
    double
      min_x,max_x,min_y,max_y;

/* defines to figure out the bounds of the distorted image */
#define InitalBounds(px,py) \
{ \
  min_x = max_x = (px); \
  min_y = max_y = (py); \
}
#define ExpandBounds(px,py) \
{ \
  if ( (px) < min_x )  min_x = (px); \
  if ( (px) > max_x )  max_x = (px); \
  if ( (py) < min_y )  min_y = (py); \
  if ( (py) > max_y )  max_y = (py); \
}

    switch (method)
    {
      case AffineDistortion:
      case AffineProjectionDistortion:
      case ScaleRotateTranslateDistortion:
      { double inverse[6];
        InvertAffineCoefficients(coefficients, inverse);
        x = image->page.x;  y = image->page.y;
        point.x=inverse[0]*x+inverse[2]*y+inverse[4];
        point.y=inverse[1]*x+inverse[3]*y+inverse[5];
        InitalBounds( point.x, point.y );
        x = image->page.x+image->columns-1;  y = image->page.y;
        point.x=inverse[0]*x+inverse[2]*y+inverse[4];
        point.y=inverse[1]*x+inverse[3]*y+inverse[5];
        ExpandBounds( point.x, point.y );
        x = image->page.x;  y = image->page.y+image->rows-1;
        point.x=inverse[0]*x+inverse[2]*y+inverse[4];
        point.y=inverse[1]*x+inverse[3]*y+inverse[5];
        ExpandBounds( point.x, point.y );
        x = image->page.x+image->columns-1;
        y = image->page.y+image->rows-1;
        point.x=inverse[0]*x+inverse[2]*y+inverse[4];
        point.y=inverse[1]*x+inverse[3]*y+inverse[5];
        ExpandBounds( point.x, point.y );
        break;
      }
      case PerspectiveDistortion:
      case PerspectiveProjectionDistortion:
      { double inverse[8], scale;
        InvertPerspectiveCoefficients(coefficients, inverse);
        x = image->page.x;  y = image->page.y;
        scale=inverse[6]*x+inverse[7]*y+1.0;
        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
        point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]);
        point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]);
        InitalBounds( point.x, point.y );
        x = image->page.x+image->columns-1;  y = image->page.y;
        scale=inverse[6]*x+inverse[7]*y+1.0;
        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
        point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]);
        point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]);
        ExpandBounds( point.x, point.y );
        x = image->page.x;  y = image->page.y+image->rows-1;
        scale=inverse[6]*x+inverse[7]*y+1.0;
        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
        point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]);
        point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]);
        ExpandBounds( point.x, point.y );
        x = image->page.x+image->columns-1;
        y = image->page.y+image->rows-1;
        scale=inverse[6]*x+inverse[7]*y+1.0;
        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
        point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]);
        point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]);
        ExpandBounds( point.x, point.y );
        break;
      }
      case ArcDistortion:
      { double a, ca, sa;
        /* Forward Map Corners */
        a = coefficients[0]-coefficients[1]/2; ca = cos(a); sa = sin(a);
        point.x = coefficients[2]*ca;
        point.y = coefficients[2]*sa;
        InitalBounds( point.x, point.y );
        point.x = (coefficients[2]-coefficients[3])*ca;
        point.y = (coefficients[2]-coefficients[3])*sa;
        ExpandBounds( point.x, point.y );
        a = coefficients[0]+coefficients[1]/2; ca = cos(a); sa = sin(a);
        point.x = coefficients[2]*ca;
        point.y = coefficients[2]*sa;
        ExpandBounds( point.x, point.y );
        point.x = (coefficients[2]-coefficients[3])*ca;
        point.y = (coefficients[2]-coefficients[3])*sa;
        ExpandBounds( point.x, point.y );
        /* orthogonal points along top of arc */
        for( a=ceil((coefficients[0]-coefficients[1]/2.0)*2.0/MagickPI)
                              *MagickPI/2.0;
               a<(coefficients[0]+coefficients[1]/2.0); a+=MagickPI/2.0 ) {
          ca = cos(a); sa = sin(a);
          point.x = coefficients[2]*ca;
          point.y = coefficients[2]*sa;
          ExpandBounds( point.x, point.y );
        }
        /*
          Convert the angle_to_width and radius_to_height
          to appropriate scaling factors, to allow faster processing
          in the mapping function.
        */
        coefficients[1] = 2.0*MagickPI*image->columns/coefficients[1];
        coefficients[3] = 1.0*image->rows/coefficients[3];
        break;
      }
      case BilinearDistortion:
      default:
        /* no bestfit available for this distortion YET */
        bestfit = MagickFalse;
    }
    /* Set the output image geometry to the 'bestfit' of the image */
    if ( bestfit ) {
      geometry.x=(long) floor(min_x-MagickEpsilon);
      geometry.y=(long) floor(min_y-MagickEpsilon);
      geometry.width=(unsigned long) ceil(max_x-geometry.x+1+MagickEpsilon);
      geometry.height=(unsigned long) ceil(max_y-geometry.y+1+MagickEpsilon);
    }
  }

  /* User provided override of the output geometry */
  property=GetImageArtifact(image,"distort:viewport");
  if (property != (const char *) NULL)
    (void) ParseAbsoluteGeometry(property, &geometry);

  /*
    Initialize the distort image attributes.
  */
  distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
    exception);
  if (distort_image == (Image *) NULL)
    return((Image *) NULL);
  if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
    {
      InheritException(exception,&distort_image->exception);
      distort_image=DestroyImage(distort_image);
      return((Image *) NULL);
    }
  distort_image->page.x=geometry.x;
  distort_image->page.y=geometry.y;
  if (distort_image->background_color.opacity != OpaqueOpacity)
    distort_image->matte=MagickTrue;

  /* Open Image views as needed. */
  resample_filter=AcquireResampleFilter(image,exception);
  GetMagickPixelPacket(distort_image,&pixel);
  distort_view=OpenCacheView(distort_image);

  /* Define constant scaling vectors for Affine Distortions */
  switch (method)
  {
    case AffineDistortion:
    case AffineProjectionDistortion:
    case ScaleRotateTranslateDistortion:
      ScaleResampleFilter( resample_filter,
        coefficients[0], coefficients[2],
        coefficients[1], coefficients[3] );
      break;
    default:
      break;
  }

  /* Initialize default pixel validity
   *    negative:         pixel is invalid  output 'matte_color'
   *    0.0 to 1.0:       antialiased, mix with resample output
   *    1.0 or greater:   use resampled output.
   */
  validity = 1.0;
  GetMagickPixelPacket(distort_image,&invalid);
  SetMagickPixelPacket(distort_image, &distort_image->matte_color,
             (IndexPacket *) NULL, &invalid);
  if (distort_image->colorspace == CMYKColorspace)
        ConvertRGBToCMYK(&invalid);   /* what about other color spaces? */

  /* Sample the source image to each pixel in the distort image.  */
  for (j=0; j < (long) distort_image->rows; j++)
  {
    q=SetCacheView(distort_view,0,j,distort_image->columns,1);
    if (q == (PixelPacket *) NULL)
      break;
    indexes=GetCacheViewIndexes(distort_view);
    y = j+geometry.y;
    for (i=0; i < (long) distort_image->columns; i++)
    {
      x = i+geometry.x;
      switch (method)
      {
        case AffineDistortion:
        case AffineProjectionDistortion:
        case ScaleRotateTranslateDistortion:
        {
          point.x=coefficients[0]*x+coefficients[2]*y+coefficients[4];
          point.y=coefficients[1]*x+coefficients[3]*y+coefficients[5];
          /* Affine partical derivitives are constant -- set above */
          break;
        }
        case BilinearDistortion:
        {
          point.x=coefficients[0]*x+coefficients[1]*y+coefficients[2]*x*y+
            coefficients[3];
          point.y=coefficients[4]*x+coefficients[5]*y+coefficients[6]*x*y+
            coefficients[7];
          /* Bilinear partical derivitives of scaling vectors */
          ScaleResampleFilter( resample_filter,
              coefficients[0] + coefficients[2]*y,
              coefficients[1] + coefficients[2]*x,
              coefficients[4] + coefficients[6]*y,
              coefficients[5] + coefficients[6]*x );
          break;
        }
        case PerspectiveDistortion:
        case PerspectiveProjectionDistortion:
        {
          double
            p,q,r,abs_r,abs_c6,abs_c7,scale;
          /* perspective is a ratio of affines */
          p=coefficients[0]*x+coefficients[1]*y+coefficients[2];
          q=coefficients[3]*x+coefficients[4]*y+coefficients[5];
          r=coefficients[6]*x+coefficients[7]*y+1.0;
          /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
          validity = (r*coefficients[8] < 0.0) ? 0.0 : 1.0;
          /* Determine horizon anti-aliase blending */
          abs_r = fabs(r)*2;
          abs_c6 = fabs(coefficients[6]);
          abs_c7 = fabs(coefficients[7]);
          if ( abs_c6 > abs_c7 ) {
            if ( abs_r < abs_c6 )
              validity = 0.5 - coefficients[8]*r/coefficients[6];
          }
          else if ( abs_r < abs_c7 )
            validity = .5 - coefficients[8]*r/coefficients[7];
          /* Perspective Sampling Point (if valid) */
          if ( validity > 0.0 ) {
            scale = 1.0/r;
            point.x = p*scale;
            point.y = q*scale;
            /* Perspective Partial Derivatives or Scaling Vectors */
            scale *= scale;
            ScaleResampleFilter( resample_filter,
              (r*coefficients[0] - p*coefficients[6])*scale,
              (r*coefficients[1] - p*coefficients[7])*scale,
              (r*coefficients[3] - q*coefficients[6])*scale,
              (r*coefficients[4] - q*coefficients[7])*scale );
          }
          break;
        }
        case ArcDistortion:
        {
          double radius = sqrt((double) x*x+y*y);
          point.x = (atan2((double)y,(double)x) - coefficients[0])/(2*MagickPI);
          point.x -= MagickRound(point.x);
          point.x = point.x*coefficients[1] + coefficients[4];
          point.y = (coefficients[2] - radius) * coefficients[3];
          /* Polar Distortion Partial Derivatives or Scaling Vectors
             We give the deritives of  du/dr, dv/dr  and du/da, dv/da
             rather than provide the complex  du/dx,dv/dx and du/dy,dv/dy.
             The result will be the same, but it is simplier to calculate.
          */
          if ( radius > MagickEpsilon )
            ScaleResampleFilter( resample_filter,
                coefficients[1]/(2*MagickPI) / radius, 0, 0, coefficients[3] );
          else
            ScaleResampleFilter( resample_filter,
                 MagickHuge, 0, 0, coefficients[3] );
          break;
        }
        default:
        {
          /*
            Noop distortion (failsafe).
          */
          point.x=(double) i;
          point.y=(double) j;
          break;
        }
      }
      if ( bestfit && method != ArcDistortion ) {
        point.x -= image->page.x;
        point.y -= image->page.y;
      }

      if ( validity <= 0.0 ) {
        /* result of distortion is an invalid pixel - don't resample */
        SetPixelPacket(distort_image,&invalid,q,indexes);
      }
      else {
        /* resample the source image to find its correct color */
        pixel=ResamplePixelColor(resample_filter,point.x,point.y);
        /* if validity between 0.0 and 1.0 mix result with invalid pixel */
        if ( validity < 1.0 ) {
          /* Do a blend of sample color and invalid pixel */
          /* should this be a 'Blend', or an 'Over' compose */
          MagickPixelCompositeBlend(&pixel, validity,
               &invalid, (1.0-validity), &pixel);
        }
        SetPixelPacket(distort_image,&pixel,q,indexes);
      }
      q++;
      indexes++;
    }
    if (SyncImagePixels(distort_image) == MagickFalse)
      break;
    if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
        (QuantumTick(y,image->rows) != MagickFalse))
      {
        status=image->progress_monitor(DistortImageTag,y,image->rows,
          image->client_data);
        if (status == MagickFalse)
          break;
      }
  }
  distort_view=CloseCacheView(distort_view);
  resample_filter=DestroyResampleFilter(resample_filter);

  /* Arc does not return an offset unless 'bestfit' is in effect */
  if ( method == ArcDistortion && !bestfit &&
       property==(const char *)NULL ) {
    distort_image->page.x = 0;
    distort_image->page.y = 0;
  }
  return(distort_image);
}
Ejemplo n.º 4
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%     A v e r a g e I m a g e s                                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  The Average() method takes a set of images and averages them together.
%  Each image in the set must have the same width and height.  Average()
%  returns a single image with each corresponding pixel component of
%  each image averaged.   On failure, a NULL image is returned and
%  exception describes the reason for the failure.
%
%  The format of the AverageImage method is:
%
%      Image *AverageImages(Image *image,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o image: The image sequence.
%
%    o exception: Return any errors or warnings in this structure.
%
%
*/
MagickExport Image *AverageImages(const Image *image,ExceptionInfo *exception)
{
  ThreadViewDataSet
    *pixels_sums;

  Image
    *average_image;

  const Image
    *last_image;

  long
    y;

  unsigned long
    row_count=0;

  double
    number_scenes;
    
  unsigned long
    number_pixels;

  MagickPassFail
    status=MagickPass;

  /*
    Ensure the image are the same size.
  */
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickSignature);
  if (image->next == (Image *) NULL)
    ThrowImageException3(ImageError,ImageSequenceIsRequired,
                         UnableToAverageImage);
  {
    const Image
      *next;
      
    for (next=image; next != (Image *) NULL; next=next->next)
      {
        if ((next->columns != image->columns) || (next->rows != image->rows))
          ThrowImageException3(OptionError,UnableToAverageImageSequence,
                               ImageWidthsOrHeightsDiffer);
      }
  }
  /*
    Allocate sum accumulation buffer.
  */
  number_pixels=image->columns;
  pixels_sums=AllocateThreadViewDataArray(image,exception,number_pixels,
                                          sizeof(DoublePixelPacket));
  if (pixels_sums == (ThreadViewDataSet *) NULL)
    ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
                         UnableToAverageImageSequence);
  /*
    Initialize average next attributes.
  */
  average_image=CloneImage(image,image->columns,image->rows,True,exception);
  if (average_image == (Image *) NULL)
    {
      DestroyThreadViewDataSet(pixels_sums);
      return((Image *) NULL);
    }
  average_image->storage_class=DirectClass;

  number_scenes=(double) GetImageListLength(image);
  last_image=GetLastImageInList(image);
#if defined(HAVE_OPENMP)
#  pragma omp parallel for schedule(dynamic) shared(row_count, status)
#endif
  for (y=0; y < (long) image->rows; y++)
    {
      register DoublePixelPacket
        *pixels_sum;

      const Image
        *next;

      register const PixelPacket
        *p;

      register long
        x;

      MagickBool
        thread_status;

      thread_status=status;
      if (thread_status == MagickFail)
        continue;

      pixels_sum=AccessThreadViewData(pixels_sums);

      /*
        Compute sum over each pixel color component.
      */
      for (next=image; next != (Image *) NULL; next=next->next)
        {
          ViewInfo
            *next_view;

          next_view=OpenCacheView((Image *) next);
          if (next_view == (ViewInfo *) NULL)
            thread_status=MagickFail;
          if (next_view != (ViewInfo *) NULL)
            {
              p=AcquireCacheViewPixels(next_view,0,y,next->columns,1,exception);
              if (p == (const PixelPacket *) NULL)
                thread_status=MagickFail;
              if (p != (const PixelPacket *) NULL)
                {
                  if (next == image)
                    {
                      for (x=0; x < (long) next->columns; x++)
                        {
                          pixels_sum[x].red=p[x].red;
                          pixels_sum[x].green=p[x].green;
                          pixels_sum[x].blue=p[x].blue;
                          pixels_sum[x].opacity=p[x].opacity;
                        }
                    }
                  else
                    {
                      for (x=0; x < (long) next->columns; x++)
                        {
                          pixels_sum[x].red+=p[x].red;
                          pixels_sum[x].green+=p[x].green;
                          pixels_sum[x].blue+=p[x].blue;
                          pixels_sum[x].opacity+=p[x].opacity;
                        }
                    }
                }
              CloseCacheView(next_view);
            }
        }
      /*
        Average next pixels.
      */
      if (thread_status != MagickFail)
        {
          register PixelPacket
            *q;

          q=SetImagePixelsEx(average_image,0,y,average_image->columns,1,exception);
          if (q == (PixelPacket *) NULL)
            thread_status=MagickFail;
          if (q != (PixelPacket *) NULL)
            {
              for (x=0; x < (long) average_image->columns; x++)
                {
                  q[x].red=(Quantum) (pixels_sum[x].red/number_scenes+0.5);
                  q[x].green=(Quantum) (pixels_sum[x].green/number_scenes+0.5);
                  q[x].blue=(Quantum) (pixels_sum[x].blue/number_scenes+0.5);
                  q[x].opacity=(Quantum) (pixels_sum[x].opacity/number_scenes+0.5);
                }
              if (!SyncImagePixelsEx(average_image,exception))
                thread_status=MagickFail;
            }
        }

#if defined(HAVE_OPENMP)
#  pragma omp critical (GM_AverageImages)
#endif
      {
        row_count++;
        if (QuantumTick(row_count,average_image->rows))
          if (!MagickMonitorFormatted(row_count,average_image->rows,exception,
                                      "[%s,...,%s] Average image sequence...",
                                      image->filename,last_image->filename))
            thread_status=MagickFail;
      
        if (thread_status == MagickFail)
          status=MagickFail;
      }
    }

  DestroyThreadViewDataSet(pixels_sums);

  if (status == MagickFail)
    {
      DestroyImage(average_image);
      average_image=(Image *) NULL;
    }

  return(average_image);
}
Ejemplo n.º 5
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  I s I m a g e s E q u a l                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  IsImagesEqual() measures the difference between colors at each pixel
%  location of two images.  A value other than 0 means the colors match
%  exactly.  Otherwise an error measure is computed by summing over all
%  pixels in an image the distance squared in RGB space between each image
%  pixel and its corresponding pixel in the reconstruct image.  The error
%  measure is assigned to these image members:
%
%    o mean_error_per_pixel:  The mean error for any single pixel in
%      the image.
%
%    o normalized_mean_error:  The normalized mean quantization error for
%      any single pixel in the image.  This distance measure is normalized to
%      a range between 0 and 1.  It is independent of the range of red, green,
%      and blue values in the image.
%
%    o normalized_maximum_error:  The normalized maximum quantization
%      error for any single pixel in the image.  This distance measure is
%      normalized to a range between 0 and 1.  It is independent of the range
%      of red, green, and blue values in your image.
%
%  A small normalized mean square error, accessed as
%  image->normalized_mean_error, suggests the images are very similar in
%  spatial layout and color.
%
%  The format of the IsImagesEqual method is:
%
%      MagickBooleanType IsImagesEqual(Image *image,
%        const Image *reconstruct_image)
%
%  A description of each parameter follows.
%
%    o image: The image.
%
%    o reconstruct_image: The reconstruct image.
%
*/
MagickExport MagickBooleanType IsImagesEqual(Image *image,
  const Image *reconstruct_image)
{
  long
    y;

  MagickBooleanType
    status;

  MagickRealType
    area,
    distance,
    maximum_error,
    mean_error,
    mean_error_per_pixel;

  register const IndexPacket
    *indexes,
    *reconstruct_indexes;

  register const PixelPacket
    *p,
    *q;

  register long
    x;

  ViewInfo
    *image_view,
    *reconstruct_view;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  assert(reconstruct_image != (const Image *) NULL);
  assert(reconstruct_image->signature == MagickSignature);
  if ((reconstruct_image->columns != image->columns) ||
      (reconstruct_image->rows != image->rows))
    ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
  area=0.0;
  maximum_error=0.0;
  mean_error_per_pixel=0.0;
  mean_error=0.0;
  image_view=OpenCacheView(image);
  reconstruct_view=OpenCacheView(reconstruct_image);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,&image->exception);
    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
      &image->exception);
    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
      break;
    indexes=AcquireCacheViewIndexes(image_view);
    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
    for (x=0; x < (long) image->columns; x++)
    {
      distance=fabs(p->red-(double) q->red);
      mean_error_per_pixel+=distance;
      mean_error+=distance*distance;
      if (distance > maximum_error)
        maximum_error=distance;
      area++;
      distance=fabs(p->green-(double) q->green);
      mean_error_per_pixel+=distance;
      mean_error+=distance*distance;
      if (distance > maximum_error)
        maximum_error=distance;
      area++;
      distance=fabs(p->blue-(double) q->blue);
      mean_error_per_pixel+=distance;
      mean_error+=distance*distance;
      if (distance > maximum_error)
        maximum_error=distance;
      area++;
      distance=fabs(p->opacity-(double) q->opacity);
      mean_error_per_pixel+=distance;
      mean_error+=distance*distance;
      if (distance > maximum_error)
        maximum_error=distance;
      area++;
      if ((image->colorspace == CMYKColorspace) &&
          (reconstruct_image->colorspace == CMYKColorspace))
        {
          distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
          mean_error_per_pixel+=distance;
          mean_error+=distance*distance;
          if (distance > maximum_error)
            maximum_error=distance;
          area++;
        }
      p++;
      q++;
    }
  }
  reconstruct_view=CloseCacheView(reconstruct_view);
  image_view=CloseCacheView(image_view);
  image->error.mean_error_per_pixel=(double) (mean_error_per_pixel/area);
  image->error.normalized_mean_error=(double) (QuantumScale*QuantumScale*
    mean_error/area);
  image->error.normalized_maximum_error=(double) (QuantumScale*maximum_error);
  status=image->error.mean_error_per_pixel == 0.0 ? MagickTrue : MagickFalse;
  return(status);
}
Ejemplo n.º 6
0
static MagickRealType GetPeakAbsoluteError(const Image *image,
  const Image *reconstruct_image,const ChannelType channel,
  ExceptionInfo *exception)
{
  long
    y;

  MagickRealType
    distance,
    distortion;

  register const IndexPacket
    *indexes,
    *reconstruct_indexes;

  register const PixelPacket
    *p,
    *q;

  register long
    x;

  ViewInfo
    *image_view,
    *reconstruct_view;

  distortion=0.0;
  image_view=OpenCacheView(image);
  reconstruct_view=OpenCacheView(reconstruct_image);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
      exception);
    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
      break;
    indexes=AcquireCacheViewIndexes(image_view);
    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
    for (x=0; x < (long) image->columns; x++)
    {
      if ((channel & RedChannel) != 0)
        {
          distance=fabs(p->red-(double) q->red);
          if (distance > distortion)
            distortion=distance;
        }
      if ((channel & GreenChannel) != 0)
        {
          distance=fabs(p->green-(double) q->green);
          if (distance > distortion)
            distortion=distance;
        }
      if ((channel & BlueChannel) != 0)
        {
          distance=fabs(p->blue-(double) q->blue);
          if (distance > distortion)
            distortion=distance;
        }
      if ((channel & OpacityChannel) != 0)
        {
          distance=fabs(p->opacity-(double) q->opacity);
          if (distance > distortion)
            distortion=distance;
        }
      if (((channel & IndexChannel) != 0) &&
          (image->colorspace == CMYKColorspace) &&
          (reconstruct_image->colorspace == CMYKColorspace))
        {
          distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
          if (distance > distortion)
            distortion=distance;
        }
      p++;
      q++;
    }
  }
  reconstruct_view=CloseCacheView(reconstruct_view);
  image_view=CloseCacheView(image_view);
  return(distortion);
}
Ejemplo n.º 7
0
static MagickRealType GetMeanErrorPerPixel(Image *image,
  const Image *reconstruct_image,const ChannelType channel,
  ExceptionInfo *exception)
{
  long
    y;

  MagickRealType
    alpha,
    area,
    beta,
    distance,
    maximum_error,
    mean_error,
    mean_error_per_pixel;

  register const IndexPacket
    *indexes,
    *reconstruct_indexes;

  register const PixelPacket
    *p,
    *q;

  register long
    x;

  ViewInfo
    *image_view,
    *reconstruct_view;

  alpha=1.0;
  beta=1.0;
  area=0.0;
  maximum_error=0.0;
  mean_error_per_pixel=0.0;
  mean_error=0.0;
  image_view=OpenCacheView(image);
  reconstruct_view=OpenCacheView(reconstruct_image);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
      exception);
    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
      break;
    indexes=AcquireCacheViewIndexes(image_view);
    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
    for (x=0; x < (long) image->columns; x++)
    {
      if ((channel & OpacityChannel) != 0)
        {
          if (image->matte != MagickFalse)
            alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
          if (reconstruct_image->matte != MagickFalse)
            beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
        }
      if ((channel & RedChannel) != 0)
        {
          distance=fabs(alpha*p->red-beta*q->red);
          mean_error_per_pixel+=distance;
          mean_error+=distance*distance;
          if (distance > maximum_error)
            maximum_error=distance;
          area++;
        }
      if ((channel & GreenChannel) != 0)
        {
          distance=fabs(alpha*p->green-beta*q->green);
          mean_error_per_pixel+=distance;
          mean_error+=distance*distance;
          if (distance > maximum_error)
            maximum_error=distance;
          area++;
        }
      if ((channel & BlueChannel) != 0)
        {
          distance=fabs(alpha*p->blue-beta*q->blue);
          mean_error_per_pixel+=distance;
          mean_error+=distance*distance;
          if (distance > maximum_error)
            maximum_error=distance;
          area++;
        }
      if ((channel & OpacityChannel) != 0)
        {
          distance=fabs(alpha*p->opacity-beta*q->opacity);
          mean_error_per_pixel+=distance;
          mean_error+=distance*distance;
          if (distance > maximum_error)
            maximum_error=distance;
          area++;
        }
      if (((channel & IndexChannel) != 0) &&
          (image->colorspace == CMYKColorspace) &&
          (reconstruct_image->colorspace == CMYKColorspace))
        {
          distance=fabs(alpha*indexes[x]-beta*reconstruct_indexes[x]);
          mean_error_per_pixel+=distance;
          mean_error+=distance*distance;
          if (distance > maximum_error)
            maximum_error=distance;
          area++;
        }
      p++;
      q++;
    }
  }
  reconstruct_view=CloseCacheView(reconstruct_view);
  image_view=CloseCacheView(image_view);
  image->error.mean_error_per_pixel=mean_error_per_pixel/area;
  image->error.normalized_mean_error=QuantumScale*QuantumScale*mean_error/area;
  image->error.normalized_maximum_error=QuantumScale*maximum_error;
  return((MagickRealType) image->error.mean_error_per_pixel);
}
Ejemplo n.º 8
0
MagickExport Image *CompareImageChannels(Image *image,
  const Image *reconstruct_image,const ChannelType channel,
  const MetricType metric,double *distortion,ExceptionInfo *exception)
{
  Image
    *difference_image;

  long
    y;

  MagickPixelPacket
    composite,
    red,
    source,
    white;

  MagickStatusType
    difference;

  register const IndexPacket
    *indexes,
    *reconstruct_indexes;

  register const PixelPacket
    *p,
    *q;

  register IndexPacket
    *difference_indexes;

  register long
    x;

  register PixelPacket
    *r;

  ViewInfo
    *difference_view,
    *image_view,
    *reconstruct_view;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(reconstruct_image != (const Image *) NULL);
  assert(reconstruct_image->signature == MagickSignature);
  assert(distortion != (double *) NULL);
  *distortion=0.0;
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  if ((reconstruct_image->columns != image->columns) ||
      (reconstruct_image->rows != image->rows))
    ThrowImageException(ImageError,"ImageSizeDiffers");
  difference_image=CloneImage(image,image->columns,image->rows,MagickTrue,
    exception);
  if (difference_image == (Image *) NULL)
    return((Image *) NULL);
  if (SetImageStorageClass(difference_image,DirectClass) == MagickFalse)
    {
      InheritException(exception,&difference_image->exception);
      difference_image=DestroyImage(difference_image);
      return((Image *) NULL);
    }
  (void) QueryMagickColor("#f1001e",&red,exception);
  (void) QueryMagickColor("#ffffff",&white,exception);
  if (difference_image->colorspace == CMYKColorspace)
    {
      ConvertRGBToCMYK(&red);
      ConvertRGBToCMYK(&white);
    }
  /*
    Generate difference image.
  */
  GetMagickPixelPacket(reconstruct_image,&source);
  GetMagickPixelPacket(difference_image,&composite);
  image_view=OpenCacheView(image);
  reconstruct_view=OpenCacheView(reconstruct_image);
  difference_view=OpenCacheView(difference_image);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
      exception);
    r=SetCacheView(difference_view,0,y,difference_image->columns,1);
    if ((p == (const PixelPacket *) NULL) ||
        (q == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
      break;
    indexes=AcquireCacheViewIndexes(image_view);
    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
    difference_indexes=GetCacheViewIndexes(difference_view);
    for (x=0; x < (long) image->columns; x++)
    {
      difference=MagickFalse;
      if ((channel & RedChannel) != 0)
        if (p->red != q->red)
          difference=MagickTrue;
      if ((channel & GreenChannel) != 0)
        if (p->green != q->green)
          difference=MagickTrue;
      if ((channel & BlueChannel) != 0)
        if (p->blue != q->blue)
          difference=MagickTrue;
      if ((channel & OpacityChannel) != 0)
        if (p->opacity != q->opacity)
          difference=MagickTrue;
      if (((channel & IndexChannel) != 0) &&
          (image->colorspace == CMYKColorspace) &&
          (reconstruct_image->colorspace == CMYKColorspace))
        if (indexes[x] != reconstruct_indexes[x])
          difference=MagickTrue;
      SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,&source);
      if (difference != MagickFalse)
        MagickPixelCompositeOver(&source,7.5*QuantumRange/10.0,&red,
          (MagickRealType) red.opacity,&composite);
      else
        MagickPixelCompositeOver(&source,7.5*QuantumRange/10.0,&white,
          (MagickRealType) white.opacity,&composite);
      SetPixelPacket(difference_image,&composite,r,difference_indexes+x);
      p++;
      q++;
      r++;
    }
    if (SyncCacheView(difference_view) == MagickFalse)
      break;
  }
  difference_view=CloseCacheView(difference_view);
  reconstruct_view=CloseCacheView(reconstruct_view);
  image_view=CloseCacheView(image_view);
  (void) GetImageChannelDistortion(image,reconstruct_image,channel,metric,
    distortion,exception);
  return(difference_image);
}