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