]> granicus.if.org Git - imagemagick/commitdiff
Added PerceptualHash to Magick++.
authordirk <dirk@git.imagemagick.org>
Sat, 6 Sep 2014 21:59:07 +0000 (21:59 +0000)
committerdirk <dirk@git.imagemagick.org>
Sat, 6 Sep 2014 21:59:07 +0000 (21:59 +0000)
Magick++/lib/Image.cpp
Magick++/lib/Magick++/Image.h
Magick++/lib/Magick++/Include.h
Magick++/lib/Magick++/Statistic.h
Magick++/lib/Statistic.cpp

index 67a633327bb8c9c8ca237166ab9fd6868fc8c641..22e5fbc8fbca2fae547644806c9c78dfcb6d6f1e 100644 (file)
@@ -3627,6 +3627,11 @@ void Magick::Image::perceptibleChannel(const ChannelType channel_,
   ThrowPPException;
 }
 
+ Magick::ImagePerceptualHash Magick::Image::perceptualHash()
+{
+  return(ImagePerceptualHash(constImage()));
+}
+
 void Magick::Image::ping(const std::string &imageSpec_)
 {
   MagickCore::Image
index 0d3c5a7c669686867b5e2da137a12c40a20e08f4..e81afd12bf0e63454721913c159cf950c252648a 100644 (file)
@@ -1070,6 +1070,9 @@ namespace Magick
     void perceptible(const double epsilon_);
     void perceptibleChannel(const ChannelType channel_,const double epsilon_);
 
+    // Returns the perceptual hash for this image.
+    Magick::ImagePerceptualHash perceptualHash();
+
     // Ping is similar to read except only enough of the image is read
     // to determine the image columns, rows, and filesize.  Access the
     // columns(), rows(), and fileSize() attributes after invoking
@@ -1290,6 +1293,7 @@ namespace Magick
     // Spread pixels randomly within image by specified ammount
     void spread(const size_t amount_=3);
 
+    // Returns the statistics for this image.
     Magick::ImageStatistics statistics();
 
     // Add a digital watermark to the image (based on second image)
index 0e35a6dc121e43aca3611eb617d8ec55d56d94cc..c8fbb792496d937eb5a593d6687edf8d24c8c510 100644 (file)
@@ -1247,6 +1247,7 @@ namespace Magick
   using MagickCore::GetImageInfo;
   using MagickCore::GetImageInfoFile;
   using MagickCore::GetImageOption;
+  using MagickCore::GetImagePerceptualHash;
   using MagickCore::GetAuthenticPixels;
   using MagickCore::GetImageProfile;
   using MagickCore::GetImageProperty;
index 3be0734c582189a3ec66d077152bb9dce0056c34..dad76dffbeaa68cf813d8aea2daaf7471a0abc27 100644 (file)
@@ -64,8 +64,8 @@ namespace Magick
       const MagickCore::ChannelMoments *channelMoments_);
 
   private:
-    std::vector<double> _huInvariants;
     PixelChannel _channel;
+    std::vector<double> _huInvariants;
     double _centroidX;
     double _centroidY;
     double _ellipseAxisX;
@@ -75,6 +75,55 @@ namespace Magick
     double _ellipseIntensity;
   };
 
+  class MagickPPExport ChannelPerceptualHash
+  {
+  public:
+
+    // Default constructor
+    ChannelPerceptualHash(void);
+
+    // Copy constructor
+    ChannelPerceptualHash(const ChannelPerceptualHash &channelPerceptualHash_);
+
+    // Constructor using the specified hash string
+    ChannelPerceptualHash(const PixelChannel channel_,
+      const std::string &hash_);
+
+    // Destroy channel perceptual hash
+    ~ChannelPerceptualHash(void);
+
+    // Return hash string
+    operator std::string() const;
+
+    // The channel
+    PixelChannel channel(void) const;
+
+    // Does object contain valid channel perceptual hash?
+    bool isValid() const;
+
+    // Returns the sum squared difference between this hash and the other hash
+    double sumSquaredDifferences(
+      const ChannelPerceptualHash &channelPerceptualHash_);
+
+    // SRGB hu preceptual hash (valid range for index is 0-6)
+    double srgbHuPhash(const size_t index_) const;
+
+    // HCLp hu preceptual hash (valid range for index is 0-6)
+    double hclpHuPhash(const size_t index_) const;
+
+    //
+    // Implemementation methods
+    //
+
+    ChannelPerceptualHash(const PixelChannel channel_,
+      const MagickCore::ChannelPerceptualHash *channelPerceptualHash_);
+
+  private:
+    PixelChannel _channel;
+    std::vector<double> _srgbHuPhash;
+    std::vector<double> _hclpHuPhash;
+  };
+
   // Obtain image statistics. Statistics are normalized to the range
   // of 0.0 to 1.0 and are output to the specified ImageStatistics
   // structure.
@@ -185,6 +234,44 @@ namespace Magick
     std::vector<ChannelMoments> _channels;
   };
 
+  class MagickPPExport ImagePerceptualHash
+  {
+  public:
+
+    // Default constructor
+    ImagePerceptualHash(void);
+
+    // Copy constructor
+    ImagePerceptualHash(const ImagePerceptualHash &imagePerceptualHash_);
+
+    // Constructor using the specified hash string
+    ImagePerceptualHash(const std::string &hash_);
+
+    // Destroy image perceptual hash
+    ~ImagePerceptualHash(void);
+
+    // Return hash string
+    operator std::string() const;
+
+    // Returns the perceptual hash for the specified channel
+    ChannelPerceptualHash channel(const PixelChannel channel_) const;
+
+    // Does object contain valid perceptual hash?
+    bool isValid() const;
+
+    // Returns the sum squared difference between this hash and the other hash
+    double sumSquaredDifferences(
+      const ImagePerceptualHash &channelPerceptualHash_);
+
+    //
+    // Implemementation methods
+    //
+    ImagePerceptualHash(const MagickCore::Image *image_);
+
+  private:
+    std::vector<ChannelPerceptualHash> _channels;
+  };
+
   class MagickPPExport ImageStatistics
   {
   public:
index e5d5dbb2bbbe2ed50cf05bb83a8a0381bd7a113e..8d76b7b8f9aef25eaca310b5565891523b3708c6 100644 (file)
@@ -109,13 +109,172 @@ Magick::ChannelMoments::ChannelMoments(const PixelChannel channel_,
     _ellipseEccentricity(channelMoments_->ellipse_eccentricity),
     _ellipseIntensity(channelMoments_->ellipse_intensity)
 {
-  size_t
+  register ssize_t
     i;
 
   for (i=0; i<8; i++)
     _huInvariants.push_back(channelMoments_->I[i]);
 }
 
+Magick::ChannelPerceptualHash::ChannelPerceptualHash(void)
+  : _channel(SyncPixelChannel),
+    _srgbHuPhash(7),
+    _hclpHuPhash(7)
+{
+}
+
+Magick::ChannelPerceptualHash::ChannelPerceptualHash(
+  const ChannelPerceptualHash &channelPerceptualHash_)
+  : _channel(channelPerceptualHash_._channel),
+    _srgbHuPhash(channelPerceptualHash_._srgbHuPhash),
+    _hclpHuPhash(channelPerceptualHash_._hclpHuPhash)
+{
+}
+
+Magick::ChannelPerceptualHash::ChannelPerceptualHash(
+  const PixelChannel channel_,const std::string &hash_)
+  : _channel(channel_),
+    _srgbHuPhash(7),
+    _hclpHuPhash(7)
+{
+  register ssize_t
+    i;
+
+  if (hash_.length() != 70)
+    throw ErrorOption("Invalid hash length");
+
+  for (i=0; i<14; i++)
+  {
+    unsigned long
+      hex;
+
+    double
+      value;
+
+    if (sscanf(hash_.substr(i*5,5).c_str(),"%05x",&hex) != 1)
+      throw ErrorOption("Invalid hash value");
+
+    value=((unsigned short)hex) / pow(10, (hex >> 17));
+    if (hex & (1 << 16))
+      value=-value;
+    if (i < 7)
+      _srgbHuPhash[i]=value;
+    else
+      _hclpHuPhash[i-7]=value;
+  }
+}
+
+Magick::ChannelPerceptualHash::~ChannelPerceptualHash(void)
+{
+}
+
+Magick::ChannelPerceptualHash::operator std::string() const
+{
+  std::string
+    hash;
+
+  register ssize_t
+    i;
+
+  if (!isValid())
+    return(std::string());
+
+  for (i=0; i<14; i++)
+  {
+    char
+      buffer[6];
+
+    double
+      value;
+
+    unsigned long
+      hex;
+
+    if (i < 7)
+      value=_srgbHuPhash[i];
+    else
+      value=_hclpHuPhash[i-7];
+
+    hex=0;
+    while(hex < 7 && fabs(value*10) < 65536)
+    {
+      value=value*10;
+      hex++;
+    }
+
+    hex=(hex<<1);
+    if (value < 0.0)
+      hex|=1;
+    hex=(hex<<16)+(unsigned long)(value < 0.0 ? -(value - 0.5) : value + 0.5);
+    (void) FormatLocaleString(buffer,6,"%05x",hex);
+    hash+=std::string(buffer);
+  }
+  return(hash);
+}
+
+Magick::PixelChannel Magick::ChannelPerceptualHash::channel() const
+{
+  return(_channel);
+}
+
+bool Magick::ChannelPerceptualHash::isValid() const
+{
+  return(_channel != SyncPixelChannel);
+}
+
+double Magick::ChannelPerceptualHash::sumSquaredDifferences(
+  const ChannelPerceptualHash &channelPerceptualHash_)
+{
+  double
+    ssd;
+
+  register ssize_t
+    i;
+
+  ssd=0.0;
+  for (i=0; i<7; i++)
+  {
+    ssd+=((_srgbHuPhash[i]-channelPerceptualHash_._srgbHuPhash[i])*
+      (_srgbHuPhash[i]-channelPerceptualHash_._srgbHuPhash[i]));
+    ssd+=((_hclpHuPhash[i]-channelPerceptualHash_._hclpHuPhash[i])*
+      (_hclpHuPhash[i]-channelPerceptualHash_._hclpHuPhash[i]));
+  }
+  return(ssd);
+}
+
+double Magick::ChannelPerceptualHash::srgbHuPhash(const size_t index_) const
+{
+  if (index_ > 6)
+    throw ErrorOption("Valid range for index is 0-6");
+
+  return(_srgbHuPhash.at(index_));
+}
+
+double Magick::ChannelPerceptualHash::hclpHuPhash(const size_t index_) const
+{
+  if (index_ > 6)
+    throw ErrorOption("Valid range for index is 0-6");
+
+  return(_hclpHuPhash.at(index_));
+}
+
+Magick::ChannelPerceptualHash::ChannelPerceptualHash(
+  const PixelChannel channel_,
+  const MagickCore::ChannelPerceptualHash *channelPerceptualHash_)
+  : _channel(channel_),
+    _srgbHuPhash(7),
+    _hclpHuPhash(7)
+{
+  register ssize_t
+    i;
+
+  for (i=0; i<7; i++)
+  {
+    _srgbHuPhash[i]=channelPerceptualHash_->srgb_hu_phash[i];
+    _hclpHuPhash[i]=channelPerceptualHash_->hclp_hu_phash[i];
+  }
+}
+
 Magick::ChannelStatistics::ChannelStatistics(void)
   : _channel(SyncPixelChannel),
     _area(0.0),
@@ -309,6 +468,128 @@ Magick::ImageMoments::ImageMoments(const MagickCore::Image *image)
   ThrowPPException;
 }
 
+Magick::ImagePerceptualHash::ImagePerceptualHash(void)
+  : _channels()
+{
+}
+
+Magick::ImagePerceptualHash::ImagePerceptualHash(
+  const ImagePerceptualHash &imagePerceptualHash_)
+  : _channels(imagePerceptualHash_._channels)
+{
+}
+
+Magick::ImagePerceptualHash::ImagePerceptualHash(const std::string &hash_)
+  : _channels()
+{
+  if (hash_.length() != 210)
+    throw ErrorOption("Invalid hash length");
+
+  _channels.push_back(Magick::ChannelPerceptualHash(RedPixelChannel,
+    hash_.substr(0, 70)));
+  _channels.push_back(Magick::ChannelPerceptualHash(GreenPixelChannel,
+    hash_.substr(70, 70)));
+  _channels.push_back(Magick::ChannelPerceptualHash(BluePixelChannel,
+    hash_.substr(140, 70)));
+}
+
+Magick::ImagePerceptualHash::~ImagePerceptualHash(void)
+{
+}
+
+Magick::ImagePerceptualHash::operator std::string() const
+{
+  if (!isValid())
+    return(std::string());
+
+  return static_cast<std::string>(_channels[0]) +
+    static_cast<std::string>(_channels[1]) + 
+    static_cast<std::string>(_channels[2]);
+}
+
+Magick::ChannelPerceptualHash Magick::ImagePerceptualHash::channel(
+  const PixelChannel channel_) const
+{
+  for (std::vector<ChannelPerceptualHash>::const_iterator it =
+       _channels.begin(); it != _channels.end(); ++it)
+  {
+    if (it->channel() == channel_)
+      return(*it);
+  }
+  return(ChannelPerceptualHash());
+}
+
+bool Magick::ImagePerceptualHash::isValid() const
+{
+  if (_channels.size() != 3)
+    return(false);
+
+  if (_channels[0].channel() != RedPixelChannel)
+    return(false);
+
+  if (_channels[1].channel() != GreenPixelChannel)
+    return(false);
+
+  if (_channels[2].channel() != BluePixelChannel)
+    return(false);
+
+  return(true);
+}
+
+double Magick::ImagePerceptualHash::sumSquaredDifferences(
+      const ImagePerceptualHash &channelPerceptualHash_)
+{
+  double
+    ssd;
+
+  register ssize_t
+    i;
+
+  if (!isValid())
+    throw ErrorOption("instance is not valid");
+  if (!channelPerceptualHash_.isValid())
+    throw ErrorOption("channelPerceptualHash_ is not valid");
+
+  ssd=0.0;
+  for (i=0; i<3; i++)
+  {
+    ssd+=_channels[i].sumSquaredDifferences(_channels[i]);
+  }
+  return(ssd);
+}
+
+Magick::ImagePerceptualHash::ImagePerceptualHash(
+  const MagickCore::Image *image)
+  : _channels()
+{
+  MagickCore::ChannelPerceptualHash*
+    channel_perceptual_hash;
+
+  PixelTrait
+    traits;
+
+  GetPPException;
+  channel_perceptual_hash=GetImagePerceptualHash(image,exceptionInfo);
+  if (channel_perceptual_hash != (MagickCore::ChannelPerceptualHash *) NULL)
+    {
+      traits=GetPixelChannelTraits(image,RedPixelChannel);
+      if ((traits & UpdatePixelTrait) != 0)
+        _channels.push_back(Magick::ChannelPerceptualHash(RedPixelChannel,
+          &channel_perceptual_hash[RedPixelChannel]));
+      traits=GetPixelChannelTraits(image,GreenPixelChannel);
+      if ((traits & UpdatePixelTrait) != 0)
+        _channels.push_back(Magick::ChannelPerceptualHash(GreenPixelChannel,
+          &channel_perceptual_hash[GreenPixelChannel]));
+      traits=GetPixelChannelTraits(image,BluePixelChannel);
+      if ((traits & UpdatePixelTrait) != 0)
+        _channels.push_back(Magick::ChannelPerceptualHash(BluePixelChannel,
+          &channel_perceptual_hash[BluePixelChannel]));
+      channel_perceptual_hash=(MagickCore::ChannelPerceptualHash *)
+        RelinquishMagickMemory(channel_perceptual_hash);
+    }
+  ThrowPPException;
+}
+
 Magick::ImageStatistics::ImageStatistics(void)
   : _channels()
 {