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 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 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 44 Magick::ChannelMoments::~ChannelMoments(void) 45 { 46 } 47 48 double Magick::ChannelMoments::centroidX(void) const 49 { 50 return(_centroidX); 51 } 52 53 double Magick::ChannelMoments::centroidY(void) const 54 { 55 return(_centroidY); 56 } 57 58 Magick::PixelChannel Magick::ChannelMoments::channel(void) const 59 { 60 return(_channel); 61 } 62 63 double Magick::ChannelMoments::ellipseAxisX(void) const 64 { 65 return(_ellipseAxisX); 66 } 67 68 double Magick::ChannelMoments::ellipseAxisY(void) const 69 { 70 return(_ellipseAxisY); 71 } 72 73 double Magick::ChannelMoments::ellipseAngle(void) const 74 { 75 return(_ellipseAngle); 76 } 77 78 double Magick::ChannelMoments::ellipseEccentricity(void) const 79 { 80 return(_ellipseEccentricity); 81 } 82 83 double Magick::ChannelMoments::ellipseIntensity(void) const 84 { 85 return(_ellipseIntensity); 86 } 87 88 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 96 bool Magick::ChannelMoments::isValid() const 97 { 98 return(_channel != SyncPixelChannel); 99 } 100 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 register ssize_t 114 i; 115 116 for (i=0; i<8; i++) 117 _huInvariants.push_back(channelMoments_->invariant[i]); 118 } 119 120 Magick::ChannelPerceptualHash::ChannelPerceptualHash(void) 121 : _channel(SyncPixelChannel), 122 _srgbHuPhash(7), 123 _hclpHuPhash(7) 124 { 125 } 126 127 Magick::ChannelPerceptualHash::ChannelPerceptualHash( 128 const ChannelPerceptualHash &channelPerceptualHash_) 129 : _channel(channelPerceptualHash_._channel), 130 _srgbHuPhash(channelPerceptualHash_._srgbHuPhash), 131 _hclpHuPhash(channelPerceptualHash_._hclpHuPhash) 132 { 133 } 134 135 Magick::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 168 Magick::ChannelPerceptualHash::~ChannelPerceptualHash(void) 169 { 170 } 171 172 Magick::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 216 Magick::PixelChannel Magick::ChannelPerceptualHash::channel() const 217 { 218 return(_channel); 219 } 220 221 bool Magick::ChannelPerceptualHash::isValid() const 222 { 223 return(_channel != SyncPixelChannel); 224 } 225 226 double 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 246 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 254 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 262 Magick::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 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 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 318 Magick::ChannelStatistics::~ChannelStatistics(void) 319 { 320 } 321 322 double Magick::ChannelStatistics::area() const 323 { 324 return(_area); 325 } 326 327 Magick::PixelChannel Magick::ChannelStatistics::channel() const 328 { 329 return(_channel); 330 } 331 332 size_t Magick::ChannelStatistics::depth() const 333 { 334 return(_depth); 335 } 336 337 double Magick::ChannelStatistics::entropy() const 338 { 339 return(_entropy); 340 } 341 342 bool Magick::ChannelStatistics::isValid() const 343 { 344 return(_channel != SyncPixelChannel); 345 } 346 347 double Magick::ChannelStatistics::kurtosis() const 348 { 349 return(_kurtosis); 350 } 351 352 double Magick::ChannelStatistics::maxima() const 353 { 354 return(_maxima); 355 } 356 357 double Magick::ChannelStatistics::mean() const 358 { 359 return(_mean); 360 } 361 362 double Magick::ChannelStatistics::minima() const 363 { 364 return(_minima); 365 } 366 367 double Magick::ChannelStatistics::skewness() const 368 { 369 return(_skewness); 370 } 371 372 double Magick::ChannelStatistics::standardDeviation() const 373 { 374 return(_standardDeviation); 375 } 376 377 double Magick::ChannelStatistics::sum() const 378 { 379 return(_sum); 380 } 381 382 double Magick::ChannelStatistics::sumCubed() const 383 { 384 return(_sumCubed); 385 } 386 387 double Magick::ChannelStatistics::sumFourthPower() const 388 { 389 return(_sumFourthPower); 390 } 391 392 double Magick::ChannelStatistics::sumSquared() const 393 { 394 return(_sumSquared); 395 } 396 397 double Magick::ChannelStatistics::variance() const 398 { 399 return(_variance); 400 } 401 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 422 Magick::ImageMoments::ImageMoments(void) 423 : _channels() 424 { 425 } 426 427 Magick::ImageMoments::ImageMoments(const ImageMoments &imageMoments_) 428 : _channels(imageMoments_._channels) 429 { 430 } 431 432 Magick::ImageMoments::~ImageMoments(void) 433 { 434 } 435 436 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 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 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 480 Magick::ImagePerceptualHash::ImagePerceptualHash(void) 481 : _channels() 482 { 483 } 484 485 Magick::ImagePerceptualHash::ImagePerceptualHash( 486 const ImagePerceptualHash &imagePerceptualHash_) 487 : _channels(imagePerceptualHash_._channels) 488 { 489 } 490 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 505 Magick::ImagePerceptualHash::~ImagePerceptualHash(void) 506 { 507 } 508 509 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 519 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 531 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 548 double 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 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 603 Magick::ImageStatistics::ImageStatistics(void) 604 : _channels() 605 { 606 } 607 608 Magick::ImageStatistics::ImageStatistics( 609 const ImageStatistics &imageStatistics_) 610 : _channels(imageStatistics_._channels) 611 { 612 } 613 614 Magick::ImageStatistics::~ImageStatistics(void) 615 { 616 } 617 618 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 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 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