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
16 using namespace std;
17
ChannelMoments(void)18 Magick::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
ChannelMoments(const ChannelMoments & channelMoments_)31 Magick::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
~ChannelMoments(void)44 Magick::ChannelMoments::~ChannelMoments(void)
45 {
46 }
47
centroidX(void) const48 double Magick::ChannelMoments::centroidX(void) const
49 {
50 return(_centroidX);
51 }
52
centroidY(void) const53 double Magick::ChannelMoments::centroidY(void) const
54 {
55 return(_centroidY);
56 }
57
channel(void) const58 Magick::PixelChannel Magick::ChannelMoments::channel(void) const
59 {
60 return(_channel);
61 }
62
ellipseAxisX(void) const63 double Magick::ChannelMoments::ellipseAxisX(void) const
64 {
65 return(_ellipseAxisX);
66 }
67
ellipseAxisY(void) const68 double Magick::ChannelMoments::ellipseAxisY(void) const
69 {
70 return(_ellipseAxisY);
71 }
72
ellipseAngle(void) const73 double Magick::ChannelMoments::ellipseAngle(void) const
74 {
75 return(_ellipseAngle);
76 }
77
ellipseEccentricity(void) const78 double Magick::ChannelMoments::ellipseEccentricity(void) const
79 {
80 return(_ellipseEccentricity);
81 }
82
ellipseIntensity(void) const83 double Magick::ChannelMoments::ellipseIntensity(void) const
84 {
85 return(_ellipseIntensity);
86 }
87
huInvariants(const size_t index_) const88 double 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
isValid() const96 bool Magick::ChannelMoments::isValid() const
97 {
98 return(_channel != SyncPixelChannel);
99 }
100
ChannelMoments(const PixelChannel channel_,const MagickCore::ChannelMoments * channelMoments_)101 Magick::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 ssize_t
114 i;
115
116 for (i=0; i<8; i++)
117 _huInvariants.push_back(channelMoments_->invariant[i]);
118 }
119
ChannelPerceptualHash(void)120 Magick::ChannelPerceptualHash::ChannelPerceptualHash(void)
121 : _channel(SyncPixelChannel),
122 _srgbHuPhash(7),
123 _hclpHuPhash(7)
124 {
125 }
126
ChannelPerceptualHash(const ChannelPerceptualHash & channelPerceptualHash_)127 Magick::ChannelPerceptualHash::ChannelPerceptualHash(
128 const ChannelPerceptualHash &channelPerceptualHash_)
129 : _channel(channelPerceptualHash_._channel),
130 _srgbHuPhash(channelPerceptualHash_._srgbHuPhash),
131 _hclpHuPhash(channelPerceptualHash_._hclpHuPhash)
132 {
133 }
134
ChannelPerceptualHash(const PixelChannel channel_,const std::string & hash_)135 Magick::ChannelPerceptualHash::ChannelPerceptualHash(
136 const PixelChannel channel_,const std::string &hash_)
137 : _channel(channel_),
138 _srgbHuPhash(7),
139 _hclpHuPhash(7)
140 {
141 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
~ChannelPerceptualHash(void)168 Magick::ChannelPerceptualHash::~ChannelPerceptualHash(void)
169 {
170 }
171
operator std::string() const172 Magick::ChannelPerceptualHash::operator std::string() const
173 {
174 std::string
175 hash;
176
177 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
channel() const216 Magick::PixelChannel Magick::ChannelPerceptualHash::channel() const
217 {
218 return(_channel);
219 }
220
isValid() const221 bool Magick::ChannelPerceptualHash::isValid() const
222 {
223 return(_channel != SyncPixelChannel);
224 }
225
sumSquaredDifferences(const ChannelPerceptualHash & channelPerceptualHash_)226 double Magick::ChannelPerceptualHash::sumSquaredDifferences(
227 const ChannelPerceptualHash &channelPerceptualHash_)
228 {
229 double
230 ssd;
231
232 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
srgbHuPhash(const size_t index_) const246 double 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
hclpHuPhash(const size_t index_) const254 double 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
ChannelPerceptualHash(const PixelChannel channel_,const MagickCore::ChannelPerceptualHash * channelPerceptualHash_)262 Magick::ChannelPerceptualHash::ChannelPerceptualHash(
263 const PixelChannel channel_,
264 const MagickCore::ChannelPerceptualHash *channelPerceptualHash_)
265 : _channel(channel_),
266 _srgbHuPhash(7),
267 _hclpHuPhash(7)
268 {
269 ssize_t
270 i;
271
272 for (i=0; i<7; i++)
273 {
274 _srgbHuPhash[i]=channelPerceptualHash_->phash[0][i];
275 _hclpHuPhash[i]=channelPerceptualHash_->phash[1][i];
276 }
277 }
278
ChannelStatistics(void)279 Magick::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
ChannelStatistics(const ChannelStatistics & channelStatistics_)298 Magick::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
~ChannelStatistics(void)318 Magick::ChannelStatistics::~ChannelStatistics(void)
319 {
320 }
321
area() const322 double Magick::ChannelStatistics::area() const
323 {
324 return(_area);
325 }
326
channel() const327 Magick::PixelChannel Magick::ChannelStatistics::channel() const
328 {
329 return(_channel);
330 }
331
depth() const332 size_t Magick::ChannelStatistics::depth() const
333 {
334 return(_depth);
335 }
336
entropy() const337 double Magick::ChannelStatistics::entropy() const
338 {
339 return(_entropy);
340 }
341
isValid() const342 bool Magick::ChannelStatistics::isValid() const
343 {
344 return(_channel != SyncPixelChannel);
345 }
346
kurtosis() const347 double Magick::ChannelStatistics::kurtosis() const
348 {
349 return(_kurtosis);
350 }
351
maxima() const352 double Magick::ChannelStatistics::maxima() const
353 {
354 return(_maxima);
355 }
356
mean() const357 double Magick::ChannelStatistics::mean() const
358 {
359 return(_mean);
360 }
361
minima() const362 double Magick::ChannelStatistics::minima() const
363 {
364 return(_minima);
365 }
366
skewness() const367 double Magick::ChannelStatistics::skewness() const
368 {
369 return(_skewness);
370 }
371
standardDeviation() const372 double Magick::ChannelStatistics::standardDeviation() const
373 {
374 return(_standardDeviation);
375 }
376
sum() const377 double Magick::ChannelStatistics::sum() const
378 {
379 return(_sum);
380 }
381
sumCubed() const382 double Magick::ChannelStatistics::sumCubed() const
383 {
384 return(_sumCubed);
385 }
386
sumFourthPower() const387 double Magick::ChannelStatistics::sumFourthPower() const
388 {
389 return(_sumFourthPower);
390 }
391
sumSquared() const392 double Magick::ChannelStatistics::sumSquared() const
393 {
394 return(_sumSquared);
395 }
396
variance() const397 double Magick::ChannelStatistics::variance() const
398 {
399 return(_variance);
400 }
401
ChannelStatistics(const PixelChannel channel_,const MagickCore::ChannelStatistics * channelStatistics_)402 Magick::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
ImageMoments(void)422 Magick::ImageMoments::ImageMoments(void)
423 : _channels()
424 {
425 }
426
ImageMoments(const ImageMoments & imageMoments_)427 Magick::ImageMoments::ImageMoments(const ImageMoments &imageMoments_)
428 : _channels(imageMoments_._channels)
429 {
430 }
431
~ImageMoments(void)432 Magick::ImageMoments::~ImageMoments(void)
433 {
434 }
435
channel(const PixelChannel channel_) const436 Magick::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
ImageMoments(const Image & image_)448 Magick::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 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
ImagePerceptualHash(void)480 Magick::ImagePerceptualHash::ImagePerceptualHash(void)
481 : _channels()
482 {
483 }
484
ImagePerceptualHash(const ImagePerceptualHash & imagePerceptualHash_)485 Magick::ImagePerceptualHash::ImagePerceptualHash(
486 const ImagePerceptualHash &imagePerceptualHash_)
487 : _channels(imagePerceptualHash_._channels)
488 {
489 }
490
ImagePerceptualHash(const std::string & hash_)491 Magick::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
~ImagePerceptualHash(void)505 Magick::ImagePerceptualHash::~ImagePerceptualHash(void)
506 {
507 }
508
operator std::string() const509 Magick::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
channel(const PixelChannel channel_) const519 Magick::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
isValid() const531 bool 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
sumSquaredDifferences(const ImagePerceptualHash & channelPerceptualHash_)548 double Magick::ImagePerceptualHash::sumSquaredDifferences(
549 const ImagePerceptualHash &channelPerceptualHash_)
550 {
551 double
552 ssd;
553
554 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
ImagePerceptualHash(const Image & image_)570 Magick::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
ImageStatistics(void)603 Magick::ImageStatistics::ImageStatistics(void)
604 : _channels()
605 {
606 }
607
ImageStatistics(const ImageStatistics & imageStatistics_)608 Magick::ImageStatistics::ImageStatistics(
609 const ImageStatistics &imageStatistics_)
610 : _channels(imageStatistics_._channels)
611 {
612 }
613
~ImageStatistics(void)614 Magick::ImageStatistics::~ImageStatistics(void)
615 {
616 }
617
channel(const PixelChannel channel_) const618 Magick::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
ImageStatistics(const Image & image_)630 Magick::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 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