You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
661 lines
16 KiB
661 lines
16 KiB
// This may look like C code, but it is really -*- C++ -*-
|
|
//
|
|
// Copyright Dirk Lemstra 2014-2015
|
|
//
|
|
// Implementation of channel moments.
|
|
//
|
|
|
|
#define MAGICKCORE_IMPLEMENTATION 1
|
|
#define MAGICK_PLUSPLUS_IMPLEMENTATION 1
|
|
|
|
#include "Magick++/Include.h"
|
|
#include "Magick++/Exception.h"
|
|
#include "Magick++/Statistic.h"
|
|
#include "Magick++/Image.h"
|
|
|
|
using namespace std;
|
|
|
|
Magick::ChannelMoments::ChannelMoments(void)
|
|
: _channel(SyncPixelChannel),
|
|
_huInvariants(8),
|
|
_centroidX(0.0),
|
|
_centroidY(0.0),
|
|
_ellipseAxisX(0.0),
|
|
_ellipseAxisY(0.0),
|
|
_ellipseAngle(0.0),
|
|
_ellipseEccentricity(0.0),
|
|
_ellipseIntensity(0.0)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelMoments::ChannelMoments(const ChannelMoments &channelMoments_)
|
|
: _channel(channelMoments_._channel),
|
|
_huInvariants(channelMoments_._huInvariants),
|
|
_centroidX(channelMoments_._centroidX),
|
|
_centroidY(channelMoments_._centroidY),
|
|
_ellipseAxisX(channelMoments_._ellipseAxisX),
|
|
_ellipseAxisY(channelMoments_._ellipseAxisY),
|
|
_ellipseAngle(channelMoments_._ellipseAngle),
|
|
_ellipseEccentricity(channelMoments_._ellipseEccentricity),
|
|
_ellipseIntensity(channelMoments_._ellipseIntensity)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelMoments::~ChannelMoments(void)
|
|
{
|
|
}
|
|
|
|
double Magick::ChannelMoments::centroidX(void) const
|
|
{
|
|
return(_centroidX);
|
|
}
|
|
|
|
double Magick::ChannelMoments::centroidY(void) const
|
|
{
|
|
return(_centroidY);
|
|
}
|
|
|
|
Magick::PixelChannel Magick::ChannelMoments::channel(void) const
|
|
{
|
|
return(_channel);
|
|
}
|
|
|
|
double Magick::ChannelMoments::ellipseAxisX(void) const
|
|
{
|
|
return(_ellipseAxisX);
|
|
}
|
|
|
|
double Magick::ChannelMoments::ellipseAxisY(void) const
|
|
{
|
|
return(_ellipseAxisY);
|
|
}
|
|
|
|
double Magick::ChannelMoments::ellipseAngle(void) const
|
|
{
|
|
return(_ellipseAngle);
|
|
}
|
|
|
|
double Magick::ChannelMoments::ellipseEccentricity(void) const
|
|
{
|
|
return(_ellipseEccentricity);
|
|
}
|
|
|
|
double Magick::ChannelMoments::ellipseIntensity(void) const
|
|
{
|
|
return(_ellipseIntensity);
|
|
}
|
|
|
|
double Magick::ChannelMoments::huInvariants(const size_t index_) const
|
|
{
|
|
if (index_ > 7)
|
|
throw ErrorOption("Valid range for index is 0-7");
|
|
|
|
return(_huInvariants.at(index_));
|
|
}
|
|
|
|
bool Magick::ChannelMoments::isValid() const
|
|
{
|
|
return(_channel != SyncPixelChannel);
|
|
}
|
|
|
|
Magick::ChannelMoments::ChannelMoments(const PixelChannel channel_,
|
|
const MagickCore::ChannelMoments *channelMoments_)
|
|
: _channel(channel_),
|
|
_huInvariants(),
|
|
_centroidX(channelMoments_->centroid.x),
|
|
_centroidY(channelMoments_->centroid.y),
|
|
_ellipseAxisX(channelMoments_->ellipse_axis.x),
|
|
_ellipseAxisY(channelMoments_->ellipse_axis.y),
|
|
_ellipseAngle(channelMoments_->ellipse_angle),
|
|
_ellipseEccentricity(channelMoments_->ellipse_eccentricity),
|
|
_ellipseIntensity(channelMoments_->ellipse_intensity)
|
|
{
|
|
ssize_t
|
|
i;
|
|
|
|
for (i=0; i<8; i++)
|
|
_huInvariants.push_back(channelMoments_->invariant[i]);
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash::ChannelPerceptualHash(void)
|
|
: _channel(SyncPixelChannel),
|
|
_srgbHuPhash(7),
|
|
_hclpHuPhash(7)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash::ChannelPerceptualHash(
|
|
const ChannelPerceptualHash &channelPerceptualHash_)
|
|
: _channel(channelPerceptualHash_._channel),
|
|
_srgbHuPhash(channelPerceptualHash_._srgbHuPhash),
|
|
_hclpHuPhash(channelPerceptualHash_._hclpHuPhash)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash::ChannelPerceptualHash(
|
|
const PixelChannel channel_,const std::string &hash_)
|
|
: _channel(channel_),
|
|
_srgbHuPhash(7),
|
|
_hclpHuPhash(7)
|
|
{
|
|
ssize_t
|
|
i;
|
|
|
|
if (hash_.length() != 70)
|
|
throw ErrorOption("Invalid hash length");
|
|
|
|
for (i=0; i<14; i++)
|
|
{
|
|
unsigned int
|
|
hex;
|
|
|
|
double
|
|
value;
|
|
|
|
if (sscanf(hash_.substr(i*5,5).c_str(),"%05x",&hex) != 1)
|
|
throw ErrorOption("Invalid hash value");
|
|
|
|
value=((unsigned short)hex) / pow(10.0, (double)(hex >> 17));
|
|
if (hex & (1 << 16))
|
|
value=-value;
|
|
if (i < 7)
|
|
_srgbHuPhash[i]=value;
|
|
else
|
|
_hclpHuPhash[i-7]=value;
|
|
}
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash::~ChannelPerceptualHash(void)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash::operator std::string() const
|
|
{
|
|
std::string
|
|
hash;
|
|
|
|
ssize_t
|
|
i;
|
|
|
|
if (!isValid())
|
|
return(std::string());
|
|
|
|
for (i=0; i<14; i++)
|
|
{
|
|
char
|
|
buffer[6];
|
|
|
|
double
|
|
value;
|
|
|
|
unsigned int
|
|
hex;
|
|
|
|
if (i < 7)
|
|
value=_srgbHuPhash[i];
|
|
else
|
|
value=_hclpHuPhash[i-7];
|
|
|
|
hex=0;
|
|
while(hex < 7 && fabs(value*10) < 65536)
|
|
{
|
|
value=value*10;
|
|
hex++;
|
|
}
|
|
|
|
hex=(hex<<1);
|
|
if (value < 0.0)
|
|
hex|=1;
|
|
hex=(hex<<16)+(unsigned int)(value < 0.0 ? -(value - 0.5) : value + 0.5);
|
|
(void) FormatLocaleString(buffer,6,"%05x",hex);
|
|
hash+=std::string(buffer);
|
|
}
|
|
return(hash);
|
|
}
|
|
|
|
Magick::PixelChannel Magick::ChannelPerceptualHash::channel() const
|
|
{
|
|
return(_channel);
|
|
}
|
|
|
|
bool Magick::ChannelPerceptualHash::isValid() const
|
|
{
|
|
return(_channel != SyncPixelChannel);
|
|
}
|
|
|
|
double Magick::ChannelPerceptualHash::sumSquaredDifferences(
|
|
const ChannelPerceptualHash &channelPerceptualHash_)
|
|
{
|
|
double
|
|
ssd;
|
|
|
|
ssize_t
|
|
i;
|
|
|
|
ssd=0.0;
|
|
for (i=0; i<7; i++)
|
|
{
|
|
ssd+=((_srgbHuPhash[i]-channelPerceptualHash_._srgbHuPhash[i])*
|
|
(_srgbHuPhash[i]-channelPerceptualHash_._srgbHuPhash[i]));
|
|
ssd+=((_hclpHuPhash[i]-channelPerceptualHash_._hclpHuPhash[i])*
|
|
(_hclpHuPhash[i]-channelPerceptualHash_._hclpHuPhash[i]));
|
|
}
|
|
return(ssd);
|
|
}
|
|
|
|
double Magick::ChannelPerceptualHash::srgbHuPhash(const size_t index_) const
|
|
{
|
|
if (index_ > 6)
|
|
throw ErrorOption("Valid range for index is 0-6");
|
|
|
|
return(_srgbHuPhash.at(index_));
|
|
}
|
|
|
|
double Magick::ChannelPerceptualHash::hclpHuPhash(const size_t index_) const
|
|
{
|
|
if (index_ > 6)
|
|
throw ErrorOption("Valid range for index is 0-6");
|
|
|
|
return(_hclpHuPhash.at(index_));
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash::ChannelPerceptualHash(
|
|
const PixelChannel channel_,
|
|
const MagickCore::ChannelPerceptualHash *channelPerceptualHash_)
|
|
: _channel(channel_),
|
|
_srgbHuPhash(7),
|
|
_hclpHuPhash(7)
|
|
{
|
|
ssize_t
|
|
i;
|
|
|
|
for (i=0; i<7; i++)
|
|
{
|
|
_srgbHuPhash[i]=channelPerceptualHash_->phash[0][i];
|
|
_hclpHuPhash[i]=channelPerceptualHash_->phash[1][i];
|
|
}
|
|
}
|
|
|
|
Magick::ChannelStatistics::ChannelStatistics(void)
|
|
: _channel(SyncPixelChannel),
|
|
_area(0.0),
|
|
_depth(0.0),
|
|
_entropy(0.0),
|
|
_kurtosis(0.0),
|
|
_maxima(0.0),
|
|
_mean(0.0),
|
|
_minima(0.0),
|
|
_skewness(0.0),
|
|
_standardDeviation(0.0),
|
|
_sum(0.0),
|
|
_sumCubed(0.0),
|
|
_sumFourthPower(0.0),
|
|
_sumSquared(0.0),
|
|
_variance(0.0)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelStatistics::ChannelStatistics(
|
|
const ChannelStatistics &channelStatistics_)
|
|
: _channel(channelStatistics_._channel),
|
|
_area(channelStatistics_._area),
|
|
_depth(channelStatistics_._depth),
|
|
_entropy(channelStatistics_._entropy),
|
|
_kurtosis(channelStatistics_._kurtosis),
|
|
_maxima(channelStatistics_._maxima),
|
|
_mean(channelStatistics_._mean),
|
|
_minima(channelStatistics_._minima),
|
|
_skewness(channelStatistics_._skewness),
|
|
_standardDeviation(channelStatistics_._standardDeviation),
|
|
_sum(channelStatistics_._sum),
|
|
_sumCubed(channelStatistics_._sumCubed),
|
|
_sumFourthPower(channelStatistics_._sumFourthPower),
|
|
_sumSquared(channelStatistics_._sumSquared),
|
|
_variance(channelStatistics_._variance)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelStatistics::~ChannelStatistics(void)
|
|
{
|
|
}
|
|
|
|
double Magick::ChannelStatistics::area() const
|
|
{
|
|
return(_area);
|
|
}
|
|
|
|
Magick::PixelChannel Magick::ChannelStatistics::channel() const
|
|
{
|
|
return(_channel);
|
|
}
|
|
|
|
size_t Magick::ChannelStatistics::depth() const
|
|
{
|
|
return(_depth);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::entropy() const
|
|
{
|
|
return(_entropy);
|
|
}
|
|
|
|
bool Magick::ChannelStatistics::isValid() const
|
|
{
|
|
return(_channel != SyncPixelChannel);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::kurtosis() const
|
|
{
|
|
return(_kurtosis);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::maxima() const
|
|
{
|
|
return(_maxima);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::mean() const
|
|
{
|
|
return(_mean);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::minima() const
|
|
{
|
|
return(_minima);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::skewness() const
|
|
{
|
|
return(_skewness);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::standardDeviation() const
|
|
{
|
|
return(_standardDeviation);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::sum() const
|
|
{
|
|
return(_sum);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::sumCubed() const
|
|
{
|
|
return(_sumCubed);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::sumFourthPower() const
|
|
{
|
|
return(_sumFourthPower);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::sumSquared() const
|
|
{
|
|
return(_sumSquared);
|
|
}
|
|
|
|
double Magick::ChannelStatistics::variance() const
|
|
{
|
|
return(_variance);
|
|
}
|
|
|
|
Magick::ChannelStatistics::ChannelStatistics(const PixelChannel channel_,
|
|
const MagickCore::ChannelStatistics *channelStatistics_)
|
|
: _channel(channel_),
|
|
_area(channelStatistics_->area),
|
|
_depth(channelStatistics_->depth),
|
|
_entropy(channelStatistics_->entropy),
|
|
_kurtosis(channelStatistics_->kurtosis),
|
|
_maxima(channelStatistics_->maxima),
|
|
_mean(channelStatistics_->mean),
|
|
_minima(channelStatistics_->minima),
|
|
_skewness(channelStatistics_->skewness),
|
|
_standardDeviation(channelStatistics_->standard_deviation),
|
|
_sum(channelStatistics_->sum),
|
|
_sumCubed(channelStatistics_->sum_cubed),
|
|
_sumFourthPower(channelStatistics_->sum_fourth_power),
|
|
_sumSquared(channelStatistics_->sum_squared),
|
|
_variance(channelStatistics_->variance)
|
|
{
|
|
}
|
|
|
|
Magick::ImageMoments::ImageMoments(void)
|
|
: _channels()
|
|
{
|
|
}
|
|
|
|
Magick::ImageMoments::ImageMoments(const ImageMoments &imageMoments_)
|
|
: _channels(imageMoments_._channels)
|
|
{
|
|
}
|
|
|
|
Magick::ImageMoments::~ImageMoments(void)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelMoments Magick::ImageMoments::channel(
|
|
const PixelChannel channel_) const
|
|
{
|
|
for (std::vector<ChannelMoments>::const_iterator it = _channels.begin();
|
|
it != _channels.end(); ++it)
|
|
{
|
|
if (it->channel() == channel_)
|
|
return(*it);
|
|
}
|
|
return(ChannelMoments());
|
|
}
|
|
|
|
Magick::ImageMoments::ImageMoments(const Image &image_)
|
|
: _channels()
|
|
{
|
|
MagickCore::ChannelMoments*
|
|
channel_moments;
|
|
|
|
GetPPException;
|
|
channel_moments=GetImageMoments(image_.constImage(),exceptionInfo);
|
|
if (channel_moments != (MagickCore::ChannelMoments *) NULL)
|
|
{
|
|
ssize_t
|
|
i;
|
|
|
|
for (i=0; i < (ssize_t) GetPixelChannels(image_.constImage()); i++)
|
|
{
|
|
PixelChannel channel=GetPixelChannelChannel(image_.constImage(),i);
|
|
PixelTrait traits=GetPixelChannelTraits(image_.constImage(),channel);
|
|
if (traits == UndefinedPixelTrait)
|
|
continue;
|
|
if ((traits & UpdatePixelTrait) == 0)
|
|
continue;
|
|
_channels.push_back(Magick::ChannelMoments(channel,
|
|
&channel_moments[channel]));
|
|
}
|
|
_channels.push_back(Magick::ChannelMoments(CompositePixelChannel,
|
|
&channel_moments[CompositePixelChannel]));
|
|
channel_moments=(MagickCore::ChannelMoments *) RelinquishMagickMemory(
|
|
channel_moments);
|
|
}
|
|
ThrowPPException(image_.quiet());
|
|
}
|
|
|
|
Magick::ImagePerceptualHash::ImagePerceptualHash(void)
|
|
: _channels()
|
|
{
|
|
}
|
|
|
|
Magick::ImagePerceptualHash::ImagePerceptualHash(
|
|
const ImagePerceptualHash &imagePerceptualHash_)
|
|
: _channels(imagePerceptualHash_._channels)
|
|
{
|
|
}
|
|
|
|
Magick::ImagePerceptualHash::ImagePerceptualHash(const std::string &hash_)
|
|
: _channels()
|
|
{
|
|
if (hash_.length() != 210)
|
|
throw ErrorOption("Invalid hash length");
|
|
|
|
_channels.push_back(Magick::ChannelPerceptualHash(RedPixelChannel,
|
|
hash_.substr(0, 70)));
|
|
_channels.push_back(Magick::ChannelPerceptualHash(GreenPixelChannel,
|
|
hash_.substr(70, 70)));
|
|
_channels.push_back(Magick::ChannelPerceptualHash(BluePixelChannel,
|
|
hash_.substr(140, 70)));
|
|
}
|
|
|
|
Magick::ImagePerceptualHash::~ImagePerceptualHash(void)
|
|
{
|
|
}
|
|
|
|
Magick::ImagePerceptualHash::operator std::string() const
|
|
{
|
|
if (!isValid())
|
|
return(std::string());
|
|
|
|
return static_cast<std::string>(_channels[0]) +
|
|
static_cast<std::string>(_channels[1]) +
|
|
static_cast<std::string>(_channels[2]);
|
|
}
|
|
|
|
Magick::ChannelPerceptualHash Magick::ImagePerceptualHash::channel(
|
|
const PixelChannel channel_) const
|
|
{
|
|
for (std::vector<ChannelPerceptualHash>::const_iterator it =
|
|
_channels.begin(); it != _channels.end(); ++it)
|
|
{
|
|
if (it->channel() == channel_)
|
|
return(*it);
|
|
}
|
|
return(ChannelPerceptualHash());
|
|
}
|
|
|
|
bool Magick::ImagePerceptualHash::isValid() const
|
|
{
|
|
if (_channels.size() != 3)
|
|
return(false);
|
|
|
|
if (_channels[0].channel() != RedPixelChannel)
|
|
return(false);
|
|
|
|
if (_channels[1].channel() != GreenPixelChannel)
|
|
return(false);
|
|
|
|
if (_channels[2].channel() != BluePixelChannel)
|
|
return(false);
|
|
|
|
return(true);
|
|
}
|
|
|
|
double Magick::ImagePerceptualHash::sumSquaredDifferences(
|
|
const ImagePerceptualHash &channelPerceptualHash_)
|
|
{
|
|
double
|
|
ssd;
|
|
|
|
ssize_t
|
|
i;
|
|
|
|
if (!isValid())
|
|
throw ErrorOption("instance is not valid");
|
|
if (!channelPerceptualHash_.isValid())
|
|
throw ErrorOption("channelPerceptualHash_ is not valid");
|
|
|
|
ssd=0.0;
|
|
for (i=0; i<3; i++)
|
|
{
|
|
ssd+=_channels[i].sumSquaredDifferences(_channels[i]);
|
|
}
|
|
return(ssd);
|
|
}
|
|
|
|
Magick::ImagePerceptualHash::ImagePerceptualHash(
|
|
const Image &image_)
|
|
: _channels()
|
|
{
|
|
MagickCore::ChannelPerceptualHash*
|
|
channel_perceptual_hash;
|
|
|
|
PixelTrait
|
|
traits;
|
|
|
|
GetPPException;
|
|
channel_perceptual_hash=GetImagePerceptualHash(image_.constImage(),
|
|
exceptionInfo);
|
|
if (channel_perceptual_hash != (MagickCore::ChannelPerceptualHash *) NULL)
|
|
{
|
|
traits=GetPixelChannelTraits(image_.constImage(),RedPixelChannel);
|
|
if ((traits & UpdatePixelTrait) != 0)
|
|
_channels.push_back(Magick::ChannelPerceptualHash(RedPixelChannel,
|
|
&channel_perceptual_hash[RedPixelChannel]));
|
|
traits=GetPixelChannelTraits(image_.constImage(),GreenPixelChannel);
|
|
if ((traits & UpdatePixelTrait) != 0)
|
|
_channels.push_back(Magick::ChannelPerceptualHash(GreenPixelChannel,
|
|
&channel_perceptual_hash[GreenPixelChannel]));
|
|
traits=GetPixelChannelTraits(image_.constImage(),BluePixelChannel);
|
|
if ((traits & UpdatePixelTrait) != 0)
|
|
_channels.push_back(Magick::ChannelPerceptualHash(BluePixelChannel,
|
|
&channel_perceptual_hash[BluePixelChannel]));
|
|
channel_perceptual_hash=(MagickCore::ChannelPerceptualHash *)
|
|
RelinquishMagickMemory(channel_perceptual_hash);
|
|
}
|
|
ThrowPPException(image_.quiet());
|
|
}
|
|
|
|
Magick::ImageStatistics::ImageStatistics(void)
|
|
: _channels()
|
|
{
|
|
}
|
|
|
|
Magick::ImageStatistics::ImageStatistics(
|
|
const ImageStatistics &imageStatistics_)
|
|
: _channels(imageStatistics_._channels)
|
|
{
|
|
}
|
|
|
|
Magick::ImageStatistics::~ImageStatistics(void)
|
|
{
|
|
}
|
|
|
|
Magick::ChannelStatistics Magick::ImageStatistics::channel(
|
|
const PixelChannel channel_) const
|
|
{
|
|
for (std::vector<ChannelStatistics>::const_iterator it = _channels.begin();
|
|
it != _channels.end(); ++it)
|
|
{
|
|
if (it->channel() == channel_)
|
|
return(*it);
|
|
}
|
|
return(ChannelStatistics());
|
|
}
|
|
|
|
Magick::ImageStatistics::ImageStatistics(const Image &image_)
|
|
: _channels()
|
|
{
|
|
MagickCore::ChannelStatistics*
|
|
channel_statistics;
|
|
|
|
GetPPException;
|
|
channel_statistics=GetImageStatistics(image_.constImage(),exceptionInfo);
|
|
if (channel_statistics != (MagickCore::ChannelStatistics *) NULL)
|
|
{
|
|
ssize_t
|
|
i;
|
|
|
|
for (i=0; i < (ssize_t) GetPixelChannels(image_.constImage()); i++)
|
|
{
|
|
PixelChannel channel=GetPixelChannelChannel(image_.constImage(),i);
|
|
PixelTrait traits=GetPixelChannelTraits(image_.constImage(),channel);
|
|
if (traits == UndefinedPixelTrait)
|
|
continue;
|
|
if ((traits & UpdatePixelTrait) == 0)
|
|
continue;
|
|
_channels.push_back(Magick::ChannelStatistics(channel,
|
|
&channel_statistics[channel]));
|
|
}
|
|
_channels.push_back(Magick::ChannelStatistics(CompositePixelChannel,
|
|
&channel_statistics[CompositePixelChannel]));
|
|
channel_statistics=(MagickCore::ChannelStatistics *) RelinquishMagickMemory(
|
|
channel_statistics);
|
|
}
|
|
ThrowPPException(image_.quiet());
|
|
}
|