]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/identify.c
Added decorators for unused arguments.
[imagemagick] / MagickCore / identify.c
index b733a9df6e78a25d398316aff5285cb571295b10..0b0a79c02f3357bed2e715032e2535ad2868d724 100644 (file)
 %               Identify an Image Format and Characteristics.                 %
 %                                                                             %
 %                           Software Design                                   %
-%                             John Cristy                                     %
+%                                Cristy                                       %
 %                            September 1994                                   %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
 %  obtain a copy of the License at                                            %
 %                                                                             %
-%    http://www.imagemagick.org/script/license.php                            %
+%    https://www.imagemagick.org/script/license.php                           %
 %                                                                             %
 %  Unless required by applicable law or agreed to in writing, software        %
 %  distributed under the License is distributed on an "AS IS" BASIS,          %
 #include "MagickCore/utility.h"
 #include "MagickCore/utility-private.h"
 #include "MagickCore/version.h"
-#if defined(MAGICKCORE_LCMS_DELEGATE)
-#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
-#include <lcms/lcms2.h>
-#elif defined(MAGICKCORE_HAVE_LCMS2_H)
-#include "lcms2.h"
-#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
-#include <lcms/lcms.h>
-#else
-#include "lcms.h"
-#endif
-#endif
-\f
-/*
-  Define declarations.
-*/
-#if defined(MAGICKCORE_LCMS_DELEGATE)
-#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
-#define cmsUInt32Number  DWORD
-#endif
-#endif
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 */
 
+static ChannelStatistics *GetLocationStatistics(const Image *image,
+  const StatisticType type,ExceptionInfo *exception)
+{
+  ChannelStatistics
+    *channel_statistics;
+
+  register ssize_t
+    i;
+
+  ssize_t
+    y;
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickCoreSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
+    MaxPixelChannels+1,sizeof(*channel_statistics));
+  if (channel_statistics == (ChannelStatistics *) NULL)
+    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+  (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
+    sizeof(*channel_statistics));
+  for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
+  {
+    switch (type)
+    {
+      case MaximumStatistic:
+      default:
+      {
+        channel_statistics[i].maxima=(-MagickMaximumValue);
+        break;
+      }
+      case MinimumStatistic:
+      {
+        channel_statistics[i].minima=MagickMaximumValue;
+        break;
+      }
+    }
+  }
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register const Quantum
+      *magick_restrict p;
+
+    register ssize_t
+      x;
+
+    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+    if (p == (const Quantum *) NULL)
+      break;
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      register ssize_t
+        i;
+
+      if (GetPixelReadMask(image,p) == 0)
+        {
+          p+=GetPixelChannels(image);
+          continue;
+        }
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        switch (type)
+        {
+          case MaximumStatistic:
+          default:
+          {
+            if ((double) p[i] > channel_statistics[channel].maxima)
+              channel_statistics[channel].maxima=(double) p[i];
+            break;
+          }
+          case MinimumStatistic:
+          {
+            if ((double) p[i] < channel_statistics[channel].minima)
+              channel_statistics[channel].minima=(double) p[i];
+            break;
+          }
+        }
+      }
+      p+=GetPixelChannels(image);
+    }
+  }
+  return(channel_statistics);
+}
+
 static ssize_t PrintChannelFeatures(FILE *file,const PixelChannel channel,
   const char *name,const ChannelFeatures *channel_features)
 {
@@ -165,7 +234,7 @@ static ssize_t PrintChannelFeatures(FILE *file,const PixelChannel channel,
   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
   "      Correlation:\n" \
   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
-  "      Sum of Squares: Variance:\n" \
+  "      Sum of Squares Variance:\n" \
   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
   "      Inverse Difference Moment:\n" \
   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
@@ -209,6 +278,167 @@ static ssize_t PrintChannelFeatures(FILE *file,const PixelChannel channel,
   return(n);
 }
 
+static ssize_t PrintChannelLocations(FILE *file,const Image *image,
+  const PixelChannel channel,const char *name,const StatisticType type,
+  const size_t max_locations,const ChannelStatistics *channel_statistics)
+{
+  double
+    target;
+
+  ExceptionInfo
+    *exception;
+
+  ssize_t
+    n,
+    y;
+
+  switch (type)
+  {
+    case MaximumStatistic:
+    default:
+    {
+      target=channel_statistics[channel].maxima;
+      break;
+    }
+    case MinimumStatistic:
+    {
+      target=channel_statistics[channel].minima;
+      break;
+    }
+  }
+  (void) FormatLocaleFile(file,"  %s: %.*g (%.*g)",name,GetMagickPrecision(),
+    target,GetMagickPrecision(),QuantumScale*target);
+  exception=AcquireExceptionInfo();
+  n=0;
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register const Quantum
+      *p;
+
+    ssize_t
+      offset,
+      x;
+
+    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+    if (p == (const Quantum *) NULL)
+      break;
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      MagickBooleanType
+        match;
+
+      PixelTrait traits=GetPixelChannelTraits(image,channel);
+      if (traits == UndefinedPixelTrait)
+        continue;
+      offset=GetPixelChannelOffset(image,channel);
+      match=fabs((double) (p[offset]-target)) < 0.5 ? MagickTrue : MagickFalse;
+      if (match != MagickFalse)
+        {
+          if ((max_locations != 0) && (n >= (ssize_t) max_locations))
+            break;
+          (void) FormatLocaleFile(file," %.20g,%.20g",(double) x,(double) y);
+          n++;
+        }
+      p+=GetPixelChannels(image);
+    }
+    if (x < (ssize_t) image->columns)
+      break;
+  }
+  (void) FormatLocaleFile(file,"\n");
+  return(n);
+}
+
+static ssize_t PrintChannelMoments(FILE *file,const PixelChannel channel,
+  const char *name,const double scale,const ChannelMoments *channel_moments)
+{
+  double
+    powers[MaximumNumberOfImageMoments] =
+      { 1.0, 2.0, 3.0, 3.0, 6.0, 4.0, 6.0, 4.0 };
+
+  register ssize_t
+    i;
+
+  ssize_t
+    n;
+
+  n=FormatLocaleFile(file,"    %s:\n",name);
+  n+=FormatLocaleFile(file,"      Centroid: %.*g,%.*g\n",
+    GetMagickPrecision(),channel_moments[channel].centroid.x,
+    GetMagickPrecision(),channel_moments[channel].centroid.y);
+  n+=FormatLocaleFile(file,"      Ellipse Semi-Major/Minor axis: %.*g,%.*g\n",
+    GetMagickPrecision(),channel_moments[channel].ellipse_axis.x,
+    GetMagickPrecision(),channel_moments[channel].ellipse_axis.y);
+  n+=FormatLocaleFile(file,"      Ellipse angle: %.*g\n",
+    GetMagickPrecision(),channel_moments[channel].ellipse_angle);
+  n+=FormatLocaleFile(file,"      Ellipse eccentricity: %.*g\n",
+    GetMagickPrecision(),channel_moments[channel].ellipse_eccentricity);
+  n+=FormatLocaleFile(file,"      Ellipse intensity: %.*g (%.*g)\n",
+    GetMagickPrecision(),pow(scale,powers[0])*
+    channel_moments[channel].ellipse_intensity,GetMagickPrecision(),
+    channel_moments[channel].ellipse_intensity);
+  for (i=0; i < MaximumNumberOfImageMoments; i++)
+    n+=FormatLocaleFile(file,"      I%.20g: %.*g (%.*g)\n",i+1.0,
+      GetMagickPrecision(),channel_moments[channel].invariant[i]/pow(scale,
+      powers[i]),GetMagickPrecision(),channel_moments[channel].invariant[i]);
+  return(n);
+}
+
+static ssize_t PrintChannelPerceptualHash(Image *image,FILE *file,
+  const ChannelPerceptualHash *channel_phash)
+{
+  register ssize_t
+    i;
+
+  ssize_t
+    n;
+
+  (void) FormatLocaleFile(file,"  Channel perceptual hash: ");
+  for (i=0; i < (ssize_t) channel_phash[0].number_colorspaces; i++)
+  {
+    (void) FormatLocaleFile(file,"%s",CommandOptionToMnemonic(
+      MagickColorspaceOptions,(ssize_t) channel_phash[0].colorspace[i]));
+    if (i < (ssize_t) (channel_phash[0].number_colorspaces-1))
+      (void) FormatLocaleFile(file,", ");
+  }
+  (void) FormatLocaleFile(file,"\n");
+  n=0;
+  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+  {
+    PixelChannel
+      channel;
+
+    PixelTrait
+      traits;
+
+    register ssize_t
+      j;
+
+    channel=GetPixelChannelChannel(image,i);
+    if (channel == IndexPixelChannel)
+      continue;
+    traits=GetPixelChannelTraits(image,channel);
+    if (traits == UndefinedPixelTrait)
+      continue;
+    n=FormatLocaleFile(file,"    Channel %.20g:\n",(double) channel);
+    for (j=0; j < MaximumNumberOfPerceptualHashes; j++)
+    {
+      register ssize_t
+        k;
+
+      n+=FormatLocaleFile(file,"      PH%.20g: ",(double) j+1);
+      for (k=0; k < (ssize_t) channel_phash[0].number_colorspaces; k++)
+      {
+        n+=FormatLocaleFile(file,"%.*g",GetMagickPrecision(),
+          channel_phash[channel].phash[k][j]);
+        if (k < (ssize_t) (channel_phash[0].number_colorspaces-1))
+          n+=FormatLocaleFile(file,", ");
+      }
+      n+=FormatLocaleFile(file,"\n");
+    }
+  }
+  return(n);
+}
+
 static ssize_t PrintChannelStatistics(FILE *file,const PixelChannel channel,
   const char *name,const double scale,
   const ChannelStatistics *channel_statistics)
@@ -216,20 +446,21 @@ static ssize_t PrintChannelStatistics(FILE *file,const PixelChannel channel,
 #define StatisticsFormat "    %s:\n      min: " QuantumFormat  \
   " (%g)\n      max: " QuantumFormat " (%g)\n"  \
   "      mean: %g (%g)\n      standard deviation: %g (%g)\n"  \
-  "      kurtosis: %g\n      skewness: %g\n"
+  "      kurtosis: %g\n      skewness: %g\n      entropy: %g\n"
 
   ssize_t
     n;
 
-  n=FormatLocaleFile(file,StatisticsFormat,name,ClampToQuantum(scale*
-    channel_statistics[channel].minima),channel_statistics[channel].minima/
-    (double) QuantumRange,ClampToQuantum(scale*
-    channel_statistics[channel].maxima),channel_statistics[channel].maxima/
-    (double) QuantumRange,scale*channel_statistics[channel].mean,
-    channel_statistics[channel].mean/(double) QuantumRange,scale*
-    channel_statistics[channel].standard_deviation,
+  n=FormatLocaleFile(file,StatisticsFormat,name,ClampToQuantum((MagickRealType)
+    (scale*channel_statistics[channel].minima)),
+    channel_statistics[channel].minima/(double) QuantumRange,ClampToQuantum(
+    (MagickRealType) (scale*channel_statistics[channel].maxima)),
+    channel_statistics[channel].maxima/(double) QuantumRange,scale*
+    channel_statistics[channel].mean,channel_statistics[channel].mean/(double)
+    QuantumRange,scale*channel_statistics[channel].standard_deviation,
     channel_statistics[channel].standard_deviation/(double) QuantumRange,
-    channel_statistics[channel].kurtosis,channel_statistics[channel].skewness);
+    channel_statistics[channel].kurtosis,channel_statistics[channel].skewness,
+    channel_statistics[channel].entropy);
   return(n);
 }
 
@@ -237,13 +468,19 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
   const MagickBooleanType verbose,ExceptionInfo *exception)
 {
   char
-    color[MaxTextExtent],
-    format[MaxTextExtent],
-    key[MaxTextExtent];
+    color[MagickPathExtent],
+    format[MagickPathExtent],
+    key[MagickPathExtent];
 
   ChannelFeatures
     *channel_features;
 
+  ChannelMoments
+    *channel_moments;
+
+  ChannelPerceptualHash
+    *channel_phash;
+
   ChannelStatistics
     *channel_statistics;
 
@@ -252,6 +489,7 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
 
   const char
     *artifact,
+    *locate,
     *name,
     *property,
     *registry,
@@ -262,9 +500,11 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
 
   double
     elapsed_time,
+    scale,
     user_time;
 
   ImageType
+    base_type,
     type;
 
   MagickBooleanType
@@ -278,18 +518,93 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
     x;
 
   size_t
-    distance,
-    scale;
+    distance;
 
   ssize_t
     y;
 
   assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
+  assert(image->signature == MagickCoreSignature);
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   if (file == (FILE *) NULL)
     file=stdout;
+  locate=GetImageArtifact(image,"identify:locate");
+  if (locate != (const char *) NULL)
+    {
+      const char
+        *limit;
+
+      size_t
+        max_locations;
+
+      StatisticType
+        statistic_type;
+
+      /*
+        Display minimum, maximum, or mean pixel locations.
+      */
+      statistic_type=(StatisticType) ParseCommandOption(MagickStatisticOptions,
+        MagickFalse,locate);
+      limit=GetImageArtifact(image,"identify:limit");
+      max_locations=0;
+      if (limit != (const char *) NULL)
+        max_locations=StringToUnsignedLong(limit);
+      channel_statistics=GetLocationStatistics(image,statistic_type,exception);
+      if (channel_statistics == (ChannelStatistics *) NULL)
+        return(MagickFalse);
+      (void) FormatLocaleFile(file,"Channel %s locations:\n",locate);
+      colorspace=image->colorspace;
+      type=IdentifyImageType(image,exception);
+      if ((type == BilevelType) || (type == GrayscaleType) ||
+          (type == GrayscaleAlphaType))
+        colorspace=GRAYColorspace;
+      switch (colorspace)
+      {
+        case RGBColorspace:
+        case sRGBColorspace:
+        {
+          (void) PrintChannelLocations(file,image,RedPixelChannel,"Red",
+            statistic_type,max_locations,channel_statistics);
+          (void) PrintChannelLocations(file,image,GreenPixelChannel,"Green",
+            statistic_type,max_locations,channel_statistics);
+          (void) PrintChannelLocations(file,image,BluePixelChannel,"Blue",
+            statistic_type,max_locations,channel_statistics);
+          break;
+        }
+        case CMYKColorspace:
+        {
+          (void) PrintChannelLocations(file,image,CyanPixelChannel,"Cyan",
+            statistic_type,max_locations,channel_statistics);
+          (void) PrintChannelLocations(file,image,MagentaPixelChannel,"Magenta",
+            statistic_type,max_locations,channel_statistics);
+          (void) PrintChannelLocations(file,image,YellowPixelChannel,"Yellow",
+            statistic_type,max_locations,channel_statistics);
+          (void) PrintChannelLocations(file,image,BlackPixelChannel,"Black",
+            statistic_type,max_locations,channel_statistics);
+          break;
+        }
+        case GRAYColorspace:
+        {
+          (void) PrintChannelLocations(file,image,GrayPixelChannel,"Gray",
+            statistic_type,max_locations,channel_statistics);
+          break;
+        }
+        default:
+        {
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+            (void) PrintChannelLocations(file,image,(PixelChannel) i,"Gray",
+              statistic_type,max_locations,channel_statistics);
+          break;
+        }
+      }
+      if (image->alpha_trait != UndefinedPixelTrait)
+        (void) PrintChannelLocations(file,image,AlphaPixelChannel,"Alpha",
+          statistic_type,max_locations,channel_statistics);
+      channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
+        channel_statistics);
+      return(ferror(file) != 0 ? MagickFalse : MagickTrue);
+    }
   *format='\0';
   elapsed_time=GetElapsedTime(&image->timer);
   user_time=GetUserTime(&image->timer);
@@ -326,21 +641,24 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
       if (image->type != UndefinedType)
         (void) FormatLocaleFile(file,"%s ",CommandOptionToMnemonic(
           MagickTypeOptions,(ssize_t) image->type));
+      if (image->colorspace != UndefinedColorspace)
+        (void) FormatLocaleFile(file,"%s ",CommandOptionToMnemonic(
+          MagickColorspaceOptions,(ssize_t) image->colorspace));
       if (image->storage_class == DirectClass)
         {
-          (void) FormatLocaleFile(file,"DirectClass ");
           if (image->total_colors != 0)
             {
-              (void) FormatMagickSize(image->total_colors,MagickFalse,format);
+              (void) FormatMagickSize(image->total_colors,MagickFalse,"B",
+                MagickPathExtent,format);
               (void) FormatLocaleFile(file,"%s ",format);
             }
         }
       else
         if (image->total_colors <= image->colors)
-          (void) FormatLocaleFile(file,"PseudoClass %.20gc ",(double)
+          (void) FormatLocaleFile(file,"%.20gc ",(double)
             image->colors);
         else
-          (void) FormatLocaleFile(file,"PseudoClass %.20g=>%.20gc ",(double)
+          (void) FormatLocaleFile(file,"%.20g=>%.20gc ",(double)
             image->total_colors,(double) image->colors);
       if (image->error.mean_error_per_pixel != 0.0)
         (void) FormatLocaleFile(file,"%.20g/%f/%fdb ",(double)
@@ -349,7 +667,8 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
           image->error.normalized_maximum_error);
       if (GetBlobSize(image) != 0)
         {
-          (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
+          (void) FormatMagickSize(GetBlobSize(image),MagickTrue,"B",
+            MagickPathExtent,format);
           (void) FormatLocaleFile(file,"%s ",format);
         }
       (void) FormatLocaleFile(file,"%0.3fu %lu:%02lu.%03lu",user_time,
@@ -363,27 +682,35 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
   /*
     Display verbose info about the image.
   */
+  colorspace=image->colorspace;
+  type=IdentifyImageType(image,exception);
+  if ((type == BilevelType) || (type == GrayscaleType) ||
+      (type == GrayscaleAlphaType))
+    colorspace=GRAYColorspace;
   p=GetVirtualPixels(image,0,0,1,1,exception);
   ping=p == (const Quantum *) NULL ? MagickTrue : MagickFalse;
-  type=GetImageType(image,exception);
   (void) SignatureImage(image,exception);
   (void) FormatLocaleFile(file,"Image: %s\n",image->filename);
   if (*image->magick_filename != '\0')
     if (LocaleCompare(image->magick_filename,image->filename) != 0)
       {
         char
-          filename[MaxTextExtent];
+          filename[MagickPathExtent];
 
         GetPathComponent(image->magick_filename,TailPath,filename);
         (void) FormatLocaleFile(file,"  Base filename: %s\n",filename);
       }
   magick_info=GetMagickInfo(image->magick,exception);
   if ((magick_info == (const MagickInfo *) NULL) ||
-      (*GetMagickDescription(magick_info) == '\0'))
+      (GetMagickDescription(magick_info) == (const char *) NULL))
     (void) FormatLocaleFile(file,"  Format: %s\n",image->magick);
   else
     (void) FormatLocaleFile(file,"  Format: %s (%s)\n",image->magick,
       GetMagickDescription(magick_info));
+  if ((magick_info != (const MagickInfo *) NULL) &&
+      (GetMagickMimeType(magick_info) != (const char *) NULL))
+    (void) FormatLocaleFile(file,"  Mime type: %s\n",GetMagickMimeType(
+      magick_info));
   (void) FormatLocaleFile(file,"  Class: %s\n",CommandOptionToMnemonic(
     MagickClassOptions,(ssize_t) image->storage_class));
   (void) FormatLocaleFile(file,"  Geometry: %.20gx%.20g%+.20g%+.20g\n",(double)
@@ -406,9 +733,10 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
     MagickResolutionOptions,(ssize_t) image->units));
   (void) FormatLocaleFile(file,"  Type: %s\n",CommandOptionToMnemonic(
     MagickTypeOptions,(ssize_t) type));
-  if (image->type != UndefinedType)
+  base_type=GetImageType(image);
+  if (type != base_type)
     (void) FormatLocaleFile(file,"  Base type: %s\n",CommandOptionToMnemonic(
-      MagickTypeOptions,(ssize_t) image->type));
+      MagickTypeOptions,(ssize_t) base_type));
   (void) FormatLocaleFile(file,"  Endianess: %s\n",CommandOptionToMnemonic(
     MagickEndianOptions,(ssize_t) image->endian));
   /*
@@ -417,15 +745,24 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
   (void) FormatLocaleFile(file,"  Colorspace: %s\n",CommandOptionToMnemonic(
     MagickColorspaceOptions,(ssize_t) image->colorspace));
   channel_statistics=(ChannelStatistics *) NULL;
+  channel_moments=(ChannelMoments *) NULL;
+  channel_phash=(ChannelPerceptualHash *) NULL;
   channel_features=(ChannelFeatures *) NULL;
-  colorspace=image->colorspace;
-  scale=1;
+  scale=1.0;
   if (ping == MagickFalse)
     {
       size_t
         depth;
 
       channel_statistics=GetImageStatistics(image,exception);
+      if (channel_statistics == (ChannelStatistics *) NULL)
+        return(MagickFalse);
+      artifact=GetImageArtifact(image,"identify:moments");
+      if (artifact != (const char *) NULL)
+        {
+          channel_moments=GetImageMoments(image,exception);
+          channel_phash=GetImagePerceptualHash(image,exception);
+        }
       artifact=GetImageArtifact(image,"identify:features");
       if (artifact != (const char *) NULL)
         {
@@ -440,55 +777,62 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
         (void) FormatLocaleFile(file,"  Depth: %.20g/%.20g-bit\n",(double)
           image->depth,(double) depth);
       (void) FormatLocaleFile(file,"  Channel depth:\n");
-      if (IsImageGray(image,exception) != MagickFalse)
-        colorspace=GRAYColorspace;
       switch (colorspace)
       {
         case RGBColorspace:
-        default:
+        case sRGBColorspace:
         {
-          (void) FormatLocaleFile(file,"    red: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Red: %.20g-bit\n",(double)
             channel_statistics[RedPixelChannel].depth);
-          (void) FormatLocaleFile(file,"    green: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Green: %.20g-bit\n",(double)
             channel_statistics[GreenPixelChannel].depth);
-          (void) FormatLocaleFile(file,"    blue: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Blue: %.20g-bit\n",(double)
             channel_statistics[BluePixelChannel].depth);
           break;
         }
         case CMYKColorspace:
         {
-          (void) FormatLocaleFile(file,"    cyan: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Cyan: %.20g-bit\n",(double)
             channel_statistics[CyanPixelChannel].depth);
-          (void) FormatLocaleFile(file,"    magenta: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Magenta: %.20g-bit\n",(double)
             channel_statistics[MagentaPixelChannel].depth);
-          (void) FormatLocaleFile(file,"    yellow: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Yellow: %.20g-bit\n",(double)
             channel_statistics[YellowPixelChannel].depth);
-          (void) FormatLocaleFile(file,"    black: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Black: %.20g-bit\n",(double)
             channel_statistics[BlackPixelChannel].depth);
           break;
         }
         case GRAYColorspace:
         {
-          (void) FormatLocaleFile(file,"    gray: %.20g-bit\n",(double)
+          (void) FormatLocaleFile(file,"    Gray: %.20g-bit\n",(double)
             channel_statistics[GrayPixelChannel].depth);
           break;
         }
+        default:
+        {
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+            (void) FormatLocaleFile(file,"    Channel %.20g: %.20g-bit\n",
+              (double) i,(double) channel_statistics[i].depth);
+          break;
+        }
       }
-      if (image->matte != MagickFalse)
-        (void) FormatLocaleFile(file,"    alpha: %.20g-bit\n",(double)
+      if (image->alpha_trait != UndefinedPixelTrait)
+        (void) FormatLocaleFile(file,"    Alpha: %.20g-bit\n",(double)
           channel_statistics[AlphaPixelChannel].depth);
-      scale=1;
+      scale=1.0;
       if (image->depth <= MAGICKCORE_QUANTUM_DEPTH)
-        scale=QuantumRange/((size_t) QuantumRange >> ((size_t)
-          MAGICKCORE_QUANTUM_DEPTH-image->depth));
+        scale=(double) (QuantumRange/((size_t) QuantumRange >> ((size_t)
+          MAGICKCORE_QUANTUM_DEPTH-image->depth)));
     }
   if (channel_statistics != (ChannelStatistics *) NULL)
     {
       (void) FormatLocaleFile(file,"  Channel statistics:\n");
+      (void) FormatLocaleFile(file,"    Pixels: %.20g\n",(double)
+        image->columns*image->rows);
       switch (colorspace)
       {
         case RGBColorspace:
-        default:
+        case sRGBColorspace:
         {
           (void) PrintChannelStatistics(file,RedPixelChannel,"Red",1.0/
             scale,channel_statistics);
@@ -516,19 +860,101 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
             scale,channel_statistics);
           break;
         }
+        default:
+        {
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+          {
+            char
+              channel[MagickPathExtent];
+
+            (void) FormatLocaleString(channel,MagickPathExtent,"Channel %.20g",
+              (double) i);
+            (void) PrintChannelStatistics(file,(PixelChannel) i,channel,1.0/
+              scale,channel_statistics);
+          }
+          break;
+        }
       }
-      if (image->matte != MagickFalse)
+      if (image->alpha_trait != UndefinedPixelTrait)
         (void) PrintChannelStatistics(file,AlphaPixelChannel,"Alpha",1.0/
           scale,channel_statistics);
       if (colorspace != GRAYColorspace)
         {
           (void) FormatLocaleFile(file,"  Image statistics:\n");
-          (void) PrintChannelStatistics(file,(PixelChannel) MaxPixelChannels,
-            "Overall",1.0/scale,channel_statistics);
+          (void) PrintChannelStatistics(file,CompositePixelChannel,"Overall",
+            1.0/scale,channel_statistics);
         }
       channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
         channel_statistics);
     }
+  if (channel_moments != (ChannelMoments *) NULL)
+    {
+      scale=(double) ((1UL << image->depth)-1);
+      (void) FormatLocaleFile(file,"  Channel moments:\n");
+      switch (colorspace)
+      {
+        case RGBColorspace:
+        case sRGBColorspace:
+        {
+          (void) PrintChannelMoments(file,RedPixelChannel,"Red",scale,
+            channel_moments);
+          (void) PrintChannelMoments(file,GreenPixelChannel,"Green",scale,
+            channel_moments);
+          (void) PrintChannelMoments(file,BluePixelChannel,"Blue",scale,
+            channel_moments);
+          break;
+        }
+        case CMYKColorspace:
+        {
+          (void) PrintChannelMoments(file,CyanPixelChannel,"Cyan",scale,
+            channel_moments);
+          (void) PrintChannelMoments(file,MagentaPixelChannel,"Magenta",scale,
+            channel_moments);
+          (void) PrintChannelMoments(file,YellowPixelChannel,"Yellow",scale,
+            channel_moments);
+          (void) PrintChannelMoments(file,BlackPixelChannel,"Black",scale,
+            channel_moments);
+          break;
+        }
+        case GRAYColorspace:
+        {
+          (void) PrintChannelMoments(file,GrayPixelChannel,"Gray",scale,
+            channel_moments);
+          break;
+        }
+        default:
+        {
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+          {
+            char
+              channel[MagickPathExtent];
+
+            (void) FormatLocaleString(channel,MagickPathExtent,"Channel %.20g",
+              (double) i);
+            (void) PrintChannelMoments(file,(PixelChannel) i,"channel",scale,
+              channel_moments);
+          }
+          break;
+        }
+      }
+      if (image->alpha_trait != UndefinedPixelTrait)
+        (void) PrintChannelMoments(file,AlphaPixelChannel,"Alpha",scale,
+          channel_moments);
+      if (colorspace != GRAYColorspace)
+        {
+          (void) FormatLocaleFile(file,"  Image moments:\n");
+          (void) PrintChannelMoments(file,CompositePixelChannel,"Overall",scale,
+            channel_moments);
+        }
+      channel_moments=(ChannelMoments *) RelinquishMagickMemory(
+        channel_moments);
+    }
+  if (channel_phash != (ChannelPerceptualHash *) NULL)
+    {
+      (void) PrintChannelPerceptualHash(image,file,channel_phash);
+      channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
+        channel_phash);
+    }
   if (channel_features != (ChannelFeatures *) NULL)
     {
       (void) FormatLocaleFile(file,"  Channel features (horizontal, vertical, "
@@ -536,7 +962,7 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
       switch (colorspace)
       {
         case RGBColorspace:
-        default:
+        case sRGBColorspace:
         {
           (void) PrintChannelFeatures(file,RedPixelChannel,"Red",
             channel_features);
@@ -564,8 +990,22 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
             channel_features);
           break;
         }
+        default:
+        {
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+          {
+            char
+              channel[MagickPathExtent];
+
+            (void) FormatLocaleString(channel,MagickPathExtent,"Channel %.20g",
+              (double) i);
+            (void) PrintChannelFeatures(file,(PixelChannel) i,channel,
+              channel_features);
+          }
+          break;
+        }
       }
-      if (image->matte != MagickFalse)
+      if (image->alpha_trait != UndefinedPixelTrait)
         (void) PrintChannelFeatures(file,AlphaPixelChannel,"Alpha",
           channel_features);
       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
@@ -574,10 +1014,11 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
   if (ping == MagickFalse)
     {
       if (image->colorspace == CMYKColorspace)
-        (void) FormatLocaleFile(file,"  Total ink density: %.0f%%\n",100.0*
-          GetImageTotalInkDensity(image,exception)/(double) QuantumRange);
+        (void) FormatLocaleFile(file,"  Total ink density: %*g%%\n",
+          GetMagickPrecision(),100.0*GetImageTotalInkDensity(image,exception)/
+          (double) QuantumRange);
       x=0;
-      if (image->matte != MagickFalse)
+      if (image->alpha_trait != UndefinedPixelTrait)
         {
           register const Quantum
             *p;
@@ -600,7 +1041,7 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
           if ((x < (ssize_t) image->columns) || (y < (ssize_t) image->rows))
             {
               char
-                tuple[MaxTextExtent];
+                tuple[MagickPathExtent];
 
               PixelInfo
                 pixel;
@@ -614,7 +1055,6 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
               (void) FormatLocaleFile(file,"  %s\n",tuple);
             }
         }
-      artifact=GetImageArtifact(image,"identify:unique-colors");
       if (IsHistogramImage(image,exception) != MagickFalse)
         {
           (void) FormatLocaleFile(file,"  Colors: %.20g\n",(double)
@@ -623,55 +1063,58 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
           (void) GetNumberColors(image,file,exception);
         }
       else
-        if ((artifact != (const char *) NULL) &&
-            (IsMagickTrue(artifact) != MagickFalse))
-          (void) FormatLocaleFile(file,"  Colors: %.20g\n",(double)
-            GetNumberColors(image,(FILE *) NULL,exception));
+        {
+          artifact=GetImageArtifact(image,"identify:unique-colors");
+          if (IsStringTrue(artifact) != MagickFalse)
+            (void) FormatLocaleFile(file,"  Colors: %.20g\n",(double)
+              GetNumberColors(image,(FILE *) NULL,exception));
+        }
     }
   if (image->storage_class == PseudoClass)
     {
-      (void) FormatLocaleFile(file,"  Colormap: %.20g\n",(double)
+      (void) FormatLocaleFile(file,"  Colormap entries: %.20g\n",(double)
         image->colors);
+      (void) FormatLocaleFile(file,"  Colormap:\n");
       if (image->colors <= 1024)
         {
           char
-            color[MaxTextExtent],
-            hex[MaxTextExtent],
-            tuple[MaxTextExtent];
+            color[MagickPathExtent],
+            hex[MagickPathExtent],
+            tuple[MagickPathExtent];
 
           PixelInfo
             pixel;
 
           register PixelInfo
-            *restrict p;
+            *magick_restrict p;
 
           GetPixelInfo(image,&pixel);
           p=image->colormap;
           for (i=0; i < (ssize_t) image->colors; i++)
           {
             pixel=(*p);
-            (void) CopyMagickString(tuple,"(",MaxTextExtent);
+            (void) CopyMagickString(tuple,"(",MagickPathExtent);
             ConcatenateColorComponent(&pixel,RedPixelChannel,X11Compliance,
               tuple);
-            (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+            (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
             ConcatenateColorComponent(&pixel,GreenPixelChannel,X11Compliance,
               tuple);
-            (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+            (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
             ConcatenateColorComponent(&pixel,BluePixelChannel,X11Compliance,
               tuple);
             if (pixel.colorspace == CMYKColorspace)
               {
-                (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+                (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
                 ConcatenateColorComponent(&pixel,BlackPixelChannel,
                   X11Compliance,tuple);
               }
-            if (pixel.matte != MagickFalse)
+            if (pixel.alpha_trait != UndefinedPixelTrait)
               {
-                (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+                (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
                 ConcatenateColorComponent(&pixel,AlphaPixelChannel,
                   X11Compliance,tuple);
               }
-            (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
+            (void) ConcatenateMagickString(tuple,")",MagickPathExtent);
             (void) QueryColorname(image,&pixel,SVGCompliance,color,
               exception);
             GetColorTuple(&pixel,MagickTrue,hex);
@@ -718,20 +1161,22 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
     (void) FormatLocaleFile(file,"  Tile geometry: %.20gx%.20g%+.20g%+.20g\n",
       (double) image->extract_info.width,(double) image->extract_info.height,
       (double) image->extract_info.x,(double) image->extract_info.y);
-  (void) FormatLocaleFile(file,"  Interlace: %s\n",CommandOptionToMnemonic(
-    MagickInterlaceOptions,(ssize_t) image->interlace));
+  (void) QueryColorname(image,&image->matte_color,SVGCompliance,color,
+    exception);
+  (void) FormatLocaleFile(file,"  Matte color: %s\n",color);
   (void) QueryColorname(image,&image->background_color,SVGCompliance,color,
     exception);
   (void) FormatLocaleFile(file,"  Background color: %s\n",color);
   (void) QueryColorname(image,&image->border_color,SVGCompliance,color,
     exception);
   (void) FormatLocaleFile(file,"  Border color: %s\n",color);
-  (void) QueryColorname(image,&image->matte_color,SVGCompliance,color,
-    exception);
-  (void) FormatLocaleFile(file,"  Matte color: %s\n",color);
   (void) QueryColorname(image,&image->transparent_color,SVGCompliance,color,
     exception);
   (void) FormatLocaleFile(file,"  Transparent color: %s\n",color);
+  (void) FormatLocaleFile(file,"  Interlace: %s\n",CommandOptionToMnemonic(
+    MagickInterlaceOptions,(ssize_t) image->interlace));
+  (void) FormatLocaleFile(file,"  Intensity: %s\n",CommandOptionToMnemonic(
+    MagickPixelIntensityOptions,(ssize_t) image->intensity));
   (void) FormatLocaleFile(file,"  Compose: %s\n",CommandOptionToMnemonic(
     MagickComposeOptions,(ssize_t) image->compose));
   if ((image->page.width != 0) || (image->page.height != 0) ||
@@ -750,6 +1195,9 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
   if (image->iterations != 1)
     (void) FormatLocaleFile(file,"  Iterations: %.20g\n",(double)
       image->iterations);
+  if (image->duration != 0)
+    (void) FormatLocaleFile(file,"  Duration: %.20g\n",(double)
+      image->duration);
   if ((image->next != (Image *) NULL) || (image->previous != (Image *) NULL))
     (void) FormatLocaleFile(file,"  Scene: %.20g of %.20g\n",(double)
       image->scene,(double) GetImageListLength(image));
@@ -819,6 +1267,9 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
       image_info=DestroyImageInfo(image_info);
     }
   (void) GetImageProperty(image,"exif:*",exception);
+  (void) GetImageProperty(image,"icc:*",exception);
+  (void) GetImageProperty(image,"iptc:*",exception);
+  (void) GetImageProperty(image,"xmp:*",exception);
   ResetImagePropertyIterator(image);
   property=GetNextImageProperty(image);
   if (property != (const char *) NULL)
@@ -836,7 +1287,7 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
         property=GetNextImageProperty(image);
       }
     }
-  (void) FormatLocaleString(key,MaxTextExtent,"8BIM:1999,2998:#1");
+  (void) FormatLocaleString(key,MagickPathExtent,"8BIM:1999,2998:#1");
   value=GetImageProperty(image,key,exception);
   if (value != (const char *) NULL)
     {
@@ -866,45 +1317,6 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
           continue;
         (void) FormatLocaleFile(file,"    Profile-%s: %.20g bytes\n",name,
           (double) GetStringInfoLength(profile));
-#if defined(MAGICKCORE_LCMS_DELEGATE)
-        if ((LocaleCompare(name,"icc") == 0) ||
-            (LocaleCompare(name,"icm") == 0))
-          {
-            cmsHPROFILE
-              icc_profile;
-
-            icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
-              (cmsUInt32Number) GetStringInfoLength(profile));
-            if (icc_profile != (cmsHPROFILE *) NULL)
-              {
-#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
-                const char
-                  *name;
-
-                name=cmsTakeProductName(icc_profile);
-                if (name != (const char *) NULL)
-                  (void) FormatLocaleFile(file,"      %s\n",name);
-#else
-                char
-                  info[MaxTextExtent];
-
-                (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
-                  "en","US",info,MaxTextExtent);
-                (void) FormatLocaleFile(file,"      Description: %s\n",info);
-                (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,
-                  "en","US",info,MaxTextExtent);
-                (void) FormatLocaleFile(file,"      Manufacturer: %s\n",info);
-                (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en",
-                  "US",info,MaxTextExtent);
-                (void) FormatLocaleFile(file,"      Model: %s\n",info);
-                (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,
-                  "en","US",info,MaxTextExtent);
-                (void) FormatLocaleFile(file,"      Copyright: %s\n",info);
-#endif
-                (void) cmsCloseProfile(icc_profile);
-              }
-          }
-#endif
         if (LocaleCompare(name,"iptc") == 0)
           {
             char
@@ -958,6 +1370,7 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
                 case 80: tag="Byline"; break;
                 case 85: tag="Byline Title"; break;
                 case 90: tag="City"; break;
+                case 92: tag="Sub-Location"; break;
                 case 95: tag="Province State"; break;
                 case 100: tag="Country Code"; break;
                 case 101: tag="Country"; break;
@@ -996,8 +1409,8 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
               length=(size_t) (GetStringInfoDatum(profile)[i++] << 8);
               length|=GetStringInfoDatum(profile)[i++];
               attribute=(char *) NULL;
-              if (~length >= (MaxTextExtent-1))
-                attribute=(char *) AcquireQuantumMemory(length+MaxTextExtent,
+              if (~length >= (MagickPathExtent-1))
+                attribute=(char *) AcquireQuantumMemory(length+MagickPathExtent,
                   sizeof(*attribute));
               if (attribute != (char *) NULL)
                 {
@@ -1062,16 +1475,20 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
     }
   (void) FormatLocaleFile(file,"  Tainted: %s\n",CommandOptionToMnemonic(
     MagickBooleanOptions,(ssize_t) image->taint));
-  (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
+  (void) FormatMagickSize(GetBlobSize(image),MagickTrue,"B",MagickPathExtent,
+    format);
   (void) FormatLocaleFile(file,"  Filesize: %s\n",format);
   (void) FormatMagickSize((MagickSizeType) image->columns*image->rows,
-    MagickFalse,format);
+    MagickFalse,"P",MagickPathExtent,format);
   if (strlen(format) > 1)
     format[strlen(format)-1]='\0';
   (void) FormatLocaleFile(file,"  Number pixels: %s\n",format);
-  (void) FormatMagickSize((MagickSizeType) ((double) image->columns*image->rows/
-    elapsed_time+0.5),MagickFalse,format);
-  (void) FormatLocaleFile(file,"  Pixels per second: %s\n",format);
+  if (elapsed_time > MagickEpsilon)
+    {
+      (void) FormatMagickSize((MagickSizeType) ((double) image->columns*
+        image->rows/elapsed_time+0.5),MagickFalse,"B",MagickPathExtent,format);
+      (void) FormatLocaleFile(file,"  Pixels per second: %s\n",format);
+    }
   (void) FormatLocaleFile(file,"  User time: %0.3fu\n",user_time);
   (void) FormatLocaleFile(file,"  Elapsed time: %lu:%02lu.%03lu\n",
     (unsigned long) (elapsed_time/60.0),(unsigned long) ceil(fmod(elapsed_time,