Coverage Report

Created: 2025-10-12 07:48

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