1// This may look like C code, but it is really -*- C++ -*-
2//
3// Copyright Dirk Lemstra 2014-2015
4//
5// Implementation of channel moments.
6//
7
8#define MAGICKCORE_IMPLEMENTATION  1
9#define MAGICK_PLUSPLUS_IMPLEMENTATION  1
10
11#include "Magick++/Include.h"
12#include "Magick++/Exception.h"
13#include "Magick++/Statistic.h"
14#include "Magick++/Image.h"
15
16using namespace std;
17
18Magick::ChannelMoments::ChannelMoments(void)
19  : _channel(SyncPixelChannel),
20    _huInvariants(8),
21    _centroidX(0.0),
22    _centroidY(0.0),
23    _ellipseAxisX(0.0),
24    _ellipseAxisY(0.0),
25    _ellipseAngle(0.0),
26    _ellipseEccentricity(0.0),
27    _ellipseIntensity(0.0)
28{
29}
30
31Magick::ChannelMoments::ChannelMoments(const ChannelMoments &channelMoments_)
32  : _channel(channelMoments_._channel),
33    _huInvariants(channelMoments_._huInvariants),
34    _centroidX(channelMoments_._centroidX),
35    _centroidY(channelMoments_._centroidY),
36    _ellipseAxisX(channelMoments_._ellipseAxisX),
37    _ellipseAxisY(channelMoments_._ellipseAxisY),
38    _ellipseAngle(channelMoments_._ellipseAngle),
39    _ellipseEccentricity(channelMoments_._ellipseEccentricity),
40    _ellipseIntensity(channelMoments_._ellipseIntensity)
41{
42}
43
44Magick::ChannelMoments::~ChannelMoments(void)
45{
46}
47
48double Magick::ChannelMoments::centroidX(void) const
49{
50  return(_centroidX);
51}
52
53double Magick::ChannelMoments::centroidY(void) const
54{
55  return(_centroidY);
56}
57
58Magick::PixelChannel Magick::ChannelMoments::channel(void) const
59{
60  return(_channel);
61}
62
63double Magick::ChannelMoments::ellipseAxisX(void) const
64{
65  return(_ellipseAxisX);
66}
67
68double Magick::ChannelMoments::ellipseAxisY(void) const
69{
70  return(_ellipseAxisY);
71}
72
73double Magick::ChannelMoments::ellipseAngle(void) const
74{
75  return(_ellipseAngle);
76}
77
78double Magick::ChannelMoments::ellipseEccentricity(void) const
79{
80  return(_ellipseEccentricity);
81}
82
83double Magick::ChannelMoments::ellipseIntensity(void) const
84{
85  return(_ellipseIntensity);
86}
87
88double Magick::ChannelMoments::huInvariants(const size_t index_) const
89{
90  if (index_ > 7)
91    throw ErrorOption("Valid range for index is 0-7");
92
93  return(_huInvariants.at(index_));
94}
95
96bool Magick::ChannelMoments::isValid() const
97{
98  return(_channel != SyncPixelChannel);
99}
100
101Magick::ChannelMoments::ChannelMoments(const PixelChannel channel_,
102  const MagickCore::ChannelMoments *channelMoments_)
103  : _channel(channel_),
104    _huInvariants(),
105    _centroidX(channelMoments_->centroid.x),
106    _centroidY(channelMoments_->centroid.y),
107    _ellipseAxisX(channelMoments_->ellipse_axis.x),
108    _ellipseAxisY(channelMoments_->ellipse_axis.y),
109    _ellipseAngle(channelMoments_->ellipse_angle),
110    _ellipseEccentricity(channelMoments_->ellipse_eccentricity),
111    _ellipseIntensity(channelMoments_->ellipse_intensity)
112{
113  register ssize_t
114    i;
115
116  for (i=0; i<8; i++)
117    _huInvariants.push_back(channelMoments_->invariant[i]);
118}
119
120Magick::ChannelPerceptualHash::ChannelPerceptualHash(void)
121  : _channel(SyncPixelChannel),
122    _srgbHuPhash(7),
123    _hclpHuPhash(7)
124{
125}
126
127Magick::ChannelPerceptualHash::ChannelPerceptualHash(
128  const ChannelPerceptualHash &channelPerceptualHash_)
129  : _channel(channelPerceptualHash_._channel),
130    _srgbHuPhash(channelPerceptualHash_._srgbHuPhash),
131    _hclpHuPhash(channelPerceptualHash_._hclpHuPhash)
132{
133}
134
135Magick::ChannelPerceptualHash::ChannelPerceptualHash(
136  const PixelChannel channel_,const std::string &hash_)
137  : _channel(channel_),
138    _srgbHuPhash(7),
139    _hclpHuPhash(7)
140{
141  register ssize_t
142    i;
143
144  if (hash_.length() != 70)
145    throw ErrorOption("Invalid hash length");
146
147  for (i=0; i<14; i++)
148  {
149    unsigned int
150      hex;
151
152    double
153      value;
154
155    if (sscanf(hash_.substr(i*5,5).c_str(),"%05x",&hex) != 1)
156      throw ErrorOption("Invalid hash value");
157
158    value=((unsigned short)hex) / pow(10.0, (double)(hex >> 17));
159    if (hex & (1 << 16))
160      value=-value;
161    if (i < 7)
162      _srgbHuPhash[i]=value;
163    else
164      _hclpHuPhash[i-7]=value;
165  }
166}
167
168Magick::ChannelPerceptualHash::~ChannelPerceptualHash(void)
169{
170}
171
172Magick::ChannelPerceptualHash::operator std::string() const
173{
174  std::string
175    hash;
176
177  register ssize_t
178    i;
179
180  if (!isValid())
181    return(std::string());
182
183  for (i=0; i<14; i++)
184  {
185    char
186      buffer[6];
187
188    double
189      value;
190
191    unsigned int
192      hex;
193
194    if (i < 7)
195      value=_srgbHuPhash[i];
196    else
197      value=_hclpHuPhash[i-7];
198
199    hex=0;
200    while(hex < 7 && fabs(value*10) < 65536)
201    {
202      value=value*10;
203      hex++;
204    }
205
206    hex=(hex<<1);
207    if (value < 0.0)
208      hex|=1;
209    hex=(hex<<16)+(unsigned int)(value < 0.0 ? -(value - 0.5) : value + 0.5);
210    (void) FormatLocaleString(buffer,6,"%05x",hex);
211    hash+=std::string(buffer);
212  }
213  return(hash);
214}
215
216Magick::PixelChannel Magick::ChannelPerceptualHash::channel() const
217{
218  return(_channel);
219}
220
221bool Magick::ChannelPerceptualHash::isValid() const
222{
223  return(_channel != SyncPixelChannel);
224}
225
226double Magick::ChannelPerceptualHash::sumSquaredDifferences(
227  const ChannelPerceptualHash &channelPerceptualHash_)
228{
229  double
230    ssd;
231
232  register ssize_t
233    i;
234
235  ssd=0.0;
236  for (i=0; i<7; i++)
237  {
238    ssd+=((_srgbHuPhash[i]-channelPerceptualHash_._srgbHuPhash[i])*
239      (_srgbHuPhash[i]-channelPerceptualHash_._srgbHuPhash[i]));
240    ssd+=((_hclpHuPhash[i]-channelPerceptualHash_._hclpHuPhash[i])*
241      (_hclpHuPhash[i]-channelPerceptualHash_._hclpHuPhash[i]));
242  }
243  return(ssd);
244}
245
246double Magick::ChannelPerceptualHash::srgbHuPhash(const size_t index_) const
247{
248  if (index_ > 6)
249    throw ErrorOption("Valid range for index is 0-6");
250
251  return(_srgbHuPhash.at(index_));
252}
253
254double Magick::ChannelPerceptualHash::hclpHuPhash(const size_t index_) const
255{
256  if (index_ > 6)
257    throw ErrorOption("Valid range for index is 0-6");
258
259  return(_hclpHuPhash.at(index_));
260}
261
262Magick::ChannelPerceptualHash::ChannelPerceptualHash(
263  const PixelChannel channel_,
264  const MagickCore::ChannelPerceptualHash *channelPerceptualHash_)
265  : _channel(channel_),
266    _srgbHuPhash(7),
267    _hclpHuPhash(7)
268{
269  register ssize_t
270    i;
271
272  for (i=0; i<7; i++)
273  {
274    _srgbHuPhash[i]=channelPerceptualHash_->srgb_hu_phash[i];
275    _hclpHuPhash[i]=channelPerceptualHash_->hclp_hu_phash[i];
276  }
277}
278
279Magick::ChannelStatistics::ChannelStatistics(void)
280  : _channel(SyncPixelChannel),
281    _area(0.0),
282    _depth(0.0),
283    _entropy(0.0),
284    _kurtosis(0.0),
285    _maxima(0.0),
286    _mean(0.0),
287    _minima(0.0),
288    _skewness(0.0),
289    _standardDeviation(0.0),
290    _sum(0.0),
291    _sumCubed(0.0),
292    _sumFourthPower(0.0),
293    _sumSquared(0.0),
294    _variance(0.0)
295{
296}
297
298Magick::ChannelStatistics::ChannelStatistics(
299  const ChannelStatistics &channelStatistics_)
300  : _channel(channelStatistics_._channel),
301    _area(channelStatistics_._area),
302    _depth(channelStatistics_._depth),
303    _entropy(channelStatistics_._entropy),
304    _kurtosis(channelStatistics_._kurtosis),
305    _maxima(channelStatistics_._maxima),
306    _mean(channelStatistics_._mean),
307    _minima(channelStatistics_._minima),
308    _skewness(channelStatistics_._skewness),
309    _standardDeviation(channelStatistics_._standardDeviation),
310    _sum(channelStatistics_._sum),
311    _sumCubed(channelStatistics_._sumCubed),
312    _sumFourthPower(channelStatistics_._sumFourthPower),
313    _sumSquared(channelStatistics_._sumSquared),
314    _variance(channelStatistics_._variance)
315{
316}
317
318Magick::ChannelStatistics::~ChannelStatistics(void)
319{
320}
321
322double Magick::ChannelStatistics::area() const
323{
324  return(_area);
325}
326
327Magick::PixelChannel Magick::ChannelStatistics::channel() const
328{
329  return(_channel);
330}
331
332size_t Magick::ChannelStatistics::depth() const
333{
334  return(_depth);
335}
336
337double Magick::ChannelStatistics::entropy() const
338{
339  return(_entropy);
340}
341
342bool Magick::ChannelStatistics::isValid() const
343{
344  return(_channel != SyncPixelChannel);
345}
346
347double Magick::ChannelStatistics::kurtosis() const
348{
349  return(_kurtosis);
350}
351
352double Magick::ChannelStatistics::maxima() const
353{
354  return(_maxima);
355}
356
357double Magick::ChannelStatistics::mean() const
358{
359  return(_mean);
360}
361
362double Magick::ChannelStatistics::minima() const
363{
364  return(_minima);
365}
366
367double Magick::ChannelStatistics::skewness() const
368{
369  return(_skewness);
370}
371
372double Magick::ChannelStatistics::standardDeviation() const
373{
374  return(_standardDeviation);
375}
376
377double Magick::ChannelStatistics::sum() const
378{
379  return(_sum);
380}
381
382double Magick::ChannelStatistics::sumCubed() const
383{
384  return(_sumCubed);
385}
386
387double Magick::ChannelStatistics::sumFourthPower() const
388{
389  return(_sumFourthPower);
390}
391
392double Magick::ChannelStatistics::sumSquared() const
393{
394  return(_sumSquared);
395}
396
397double Magick::ChannelStatistics::variance() const
398{
399  return(_variance);
400}
401
402Magick::ChannelStatistics::ChannelStatistics(const PixelChannel channel_,
403  const MagickCore::ChannelStatistics *channelStatistics_)
404  : _channel(channel_),
405    _area(channelStatistics_->area),
406    _depth(channelStatistics_->depth),
407    _entropy(channelStatistics_->entropy),
408    _kurtosis(channelStatistics_->kurtosis),
409    _maxima(channelStatistics_->maxima),
410    _mean(channelStatistics_->mean),
411    _minima(channelStatistics_->minima),
412    _skewness(channelStatistics_->skewness),
413    _standardDeviation(channelStatistics_->standard_deviation),
414    _sum(channelStatistics_->sum),
415    _sumCubed(channelStatistics_->sum_cubed),
416    _sumFourthPower(channelStatistics_->sum_fourth_power),
417    _sumSquared(channelStatistics_->sum_squared),
418    _variance(channelStatistics_->variance)
419{
420}
421
422Magick::ImageMoments::ImageMoments(void)
423  : _channels()
424{
425}
426
427Magick::ImageMoments::ImageMoments(const ImageMoments &imageMoments_)
428  : _channels(imageMoments_._channels)
429{
430}
431
432Magick::ImageMoments::~ImageMoments(void)
433{
434}
435
436Magick::ChannelMoments Magick::ImageMoments::channel(
437  const PixelChannel channel_) const
438{
439  for (std::vector<ChannelMoments>::const_iterator it = _channels.begin();
440       it != _channels.end(); ++it)
441  {
442    if (it->channel() == channel_)
443      return(*it);
444  }
445  return(ChannelMoments());
446}
447
448Magick::ImageMoments::ImageMoments(const Image &image_)
449  : _channels()
450{
451  MagickCore::ChannelMoments*
452    channel_moments;
453
454  GetPPException;
455  channel_moments=GetImageMoments(image_.constImage(),exceptionInfo);
456  if (channel_moments != (MagickCore::ChannelMoments *) NULL)
457    {
458      register ssize_t
459        i;
460
461      for (i=0; i < (ssize_t) GetPixelChannels(image_.constImage()); i++)
462      {
463        PixelChannel channel=GetPixelChannelChannel(image_.constImage(),i);
464        PixelTrait traits=GetPixelChannelTraits(image_.constImage(),channel);
465        if (traits == UndefinedPixelTrait)
466          continue;
467        if ((traits & UpdatePixelTrait) == 0)
468          continue;
469        _channels.push_back(Magick::ChannelMoments(channel,
470          &channel_moments[channel]));
471      }
472      _channels.push_back(Magick::ChannelMoments(CompositePixelChannel,
473        &channel_moments[CompositePixelChannel]));
474      channel_moments=(MagickCore::ChannelMoments *) RelinquishMagickMemory(
475        channel_moments);
476    }
477  ThrowPPException(image_.quiet());
478}
479
480Magick::ImagePerceptualHash::ImagePerceptualHash(void)
481  : _channels()
482{
483}
484
485Magick::ImagePerceptualHash::ImagePerceptualHash(
486  const ImagePerceptualHash &imagePerceptualHash_)
487  : _channels(imagePerceptualHash_._channels)
488{
489}
490
491Magick::ImagePerceptualHash::ImagePerceptualHash(const std::string &hash_)
492  : _channels()
493{
494  if (hash_.length() != 210)
495    throw ErrorOption("Invalid hash length");
496
497  _channels.push_back(Magick::ChannelPerceptualHash(RedPixelChannel,
498    hash_.substr(0, 70)));
499  _channels.push_back(Magick::ChannelPerceptualHash(GreenPixelChannel,
500    hash_.substr(70, 70)));
501  _channels.push_back(Magick::ChannelPerceptualHash(BluePixelChannel,
502    hash_.substr(140, 70)));
503}
504
505Magick::ImagePerceptualHash::~ImagePerceptualHash(void)
506{
507}
508
509Magick::ImagePerceptualHash::operator std::string() const
510{
511  if (!isValid())
512    return(std::string());
513
514  return static_cast<std::string>(_channels[0]) +
515    static_cast<std::string>(_channels[1]) +
516    static_cast<std::string>(_channels[2]);
517}
518
519Magick::ChannelPerceptualHash Magick::ImagePerceptualHash::channel(
520  const PixelChannel channel_) const
521{
522  for (std::vector<ChannelPerceptualHash>::const_iterator it =
523       _channels.begin(); it != _channels.end(); ++it)
524  {
525    if (it->channel() == channel_)
526      return(*it);
527  }
528  return(ChannelPerceptualHash());
529}
530
531bool Magick::ImagePerceptualHash::isValid() const
532{
533  if (_channels.size() != 3)
534    return(false);
535
536  if (_channels[0].channel() != RedPixelChannel)
537    return(false);
538
539  if (_channels[1].channel() != GreenPixelChannel)
540    return(false);
541
542  if (_channels[2].channel() != BluePixelChannel)
543    return(false);
544
545  return(true);
546}
547
548double Magick::ImagePerceptualHash::sumSquaredDifferences(
549      const ImagePerceptualHash &channelPerceptualHash_)
550{
551  double
552    ssd;
553
554  register ssize_t
555    i;
556
557  if (!isValid())
558    throw ErrorOption("instance is not valid");
559  if (!channelPerceptualHash_.isValid())
560    throw ErrorOption("channelPerceptualHash_ is not valid");
561
562  ssd=0.0;
563  for (i=0; i<3; i++)
564  {
565    ssd+=_channels[i].sumSquaredDifferences(_channels[i]);
566  }
567  return(ssd);
568}
569
570Magick::ImagePerceptualHash::ImagePerceptualHash(
571  const Image &image_)
572  : _channels()
573{
574  MagickCore::ChannelPerceptualHash*
575    channel_perceptual_hash;
576
577  PixelTrait
578    traits;
579
580  GetPPException;
581  channel_perceptual_hash=GetImagePerceptualHash(image_.constImage(),
582    exceptionInfo);
583  if (channel_perceptual_hash != (MagickCore::ChannelPerceptualHash *) NULL)
584    {
585      traits=GetPixelChannelTraits(image_.constImage(),RedPixelChannel);
586      if ((traits & UpdatePixelTrait) != 0)
587        _channels.push_back(Magick::ChannelPerceptualHash(RedPixelChannel,
588          &channel_perceptual_hash[RedPixelChannel]));
589      traits=GetPixelChannelTraits(image_.constImage(),GreenPixelChannel);
590      if ((traits & UpdatePixelTrait) != 0)
591        _channels.push_back(Magick::ChannelPerceptualHash(GreenPixelChannel,
592          &channel_perceptual_hash[GreenPixelChannel]));
593      traits=GetPixelChannelTraits(image_.constImage(),BluePixelChannel);
594      if ((traits & UpdatePixelTrait) != 0)
595        _channels.push_back(Magick::ChannelPerceptualHash(BluePixelChannel,
596          &channel_perceptual_hash[BluePixelChannel]));
597      channel_perceptual_hash=(MagickCore::ChannelPerceptualHash *)
598        RelinquishMagickMemory(channel_perceptual_hash);
599    }
600  ThrowPPException(image_.quiet());
601}
602
603Magick::ImageStatistics::ImageStatistics(void)
604  : _channels()
605{
606}
607
608Magick::ImageStatistics::ImageStatistics(
609  const ImageStatistics &imageStatistics_)
610  : _channels(imageStatistics_._channels)
611{
612}
613
614Magick::ImageStatistics::~ImageStatistics(void)
615{
616}
617
618Magick::ChannelStatistics Magick::ImageStatistics::channel(
619  const PixelChannel channel_) const
620{
621  for (std::vector<ChannelStatistics>::const_iterator it = _channels.begin();
622       it != _channels.end(); ++it)
623  {
624    if (it->channel() == channel_)
625      return(*it);
626  }
627  return(ChannelStatistics());
628}
629
630Magick::ImageStatistics::ImageStatistics(const Image &image_)
631  : _channels()
632{
633  MagickCore::ChannelStatistics*
634    channel_statistics;
635
636  GetPPException;
637  channel_statistics=GetImageStatistics(image_.constImage(),exceptionInfo);
638  if (channel_statistics != (MagickCore::ChannelStatistics *) NULL)
639    {
640      register ssize_t
641        i;
642
643      for (i=0; i < (ssize_t) GetPixelChannels(image_.constImage()); i++)
644      {
645        PixelChannel channel=GetPixelChannelChannel(image_.constImage(),i);
646        PixelTrait traits=GetPixelChannelTraits(image_.constImage(),channel);
647        if (traits == UndefinedPixelTrait)
648          continue;
649        if ((traits & UpdatePixelTrait) == 0)
650          continue;
651        _channels.push_back(Magick::ChannelStatistics(channel,
652          &channel_statistics[channel]));
653      }
654      _channels.push_back(Magick::ChannelStatistics(CompositePixelChannel,
655        &channel_statistics[CompositePixelChannel]));
656      channel_statistics=(MagickCore::ChannelStatistics *) RelinquishMagickMemory(
657        channel_statistics);
658    }
659  ThrowPPException(image_.quiet());
660}
661