/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2022 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include namespace { constexpr const char* _loggerCat = "Histogram"; } // namespace namespace openspace { Histogram::Histogram(float minValue, float maxValue, int numBins, float* data) : _numBins(numBins) , _minValue(minValue) , _maxValue(maxValue) , _data(data) { if (!data) { _data = new float[numBins]; for (int i = 0; i < numBins; ++i) { _data[i] = 0.0; } } } Histogram::~Histogram() { delete[] _data; } int Histogram::numBins() const { return _numBins; } float Histogram::minValue() const { return _minValue; } float Histogram::maxValue() const { return _maxValue; } bool Histogram::isValid() const { return _numBins != -1; } bool Histogram::add(float value, float repeat) { if (value < _minValue || value > _maxValue) { // Out of range return false; } const float normalizedValue = (value - _minValue) / (_maxValue - _minValue); const int binIndex = static_cast(std::min( static_cast(floor(normalizedValue * _numBins)), _numBins - 1.f )); // [0, _numBins - 1] _data[binIndex] += repeat; _numValues = static_cast(_numValues + repeat); return true; } void Histogram::changeRange(float minValue, float maxValue){ if (minValue > _minValue && maxValue < _maxValue) { return; } float* oldData = _data; float oldMin = _minValue; float oldMax = _maxValue; float* newData = new float[_numBins]{0.0}; for(int i=0; i<_numBins; i++){ float unNormalizedValue = i*(oldMax-oldMin)+oldMin; float normalizedValue = (unNormalizedValue - _minValue) / (_maxValue - _minValue); // [0.0, 1.0] int binIndex = static_cast(std::min( static_cast(floor(normalizedValue * _numBins)), _numBins - 1.f )); // [0, _numBins - 1] newData[binIndex] = oldData[i]; } _data = newData; delete oldData; _minValue = minValue; _maxValue = maxValue; } bool Histogram::add(const Histogram& histogram) { if (_minValue == histogram.minValue() && _maxValue == histogram.maxValue() && _numBins == histogram.numBins()) { const float* data = histogram.data(); for (int i = 0; i < _numBins; i++) { _data[i] += data[i]; } _numValues += histogram._numValues; return true; } else { LERROR("Dimension mismatch"); return false; } } bool Histogram::addRectangle(float lowBin, float highBin, float value) { if (lowBin == highBin) { return true; } if (lowBin > highBin) { std::swap(lowBin, highBin); } if (lowBin < _minValue || highBin > _maxValue) { // Out of range return false; } const float normalizedLowBin = (lowBin - _minValue) / (_maxValue - _minValue); const float normalizedHighBin = (highBin - _minValue) / (_maxValue - _minValue); const float lowBinIndex = normalizedLowBin * _numBins; const float highBinIndex = normalizedHighBin * _numBins; const int fillLow = static_cast(floor(lowBinIndex)); const int fillHigh = static_cast(ceil(highBinIndex)); for (int i = fillLow; i < fillHigh; i++) { _data[i] += value; } if (lowBinIndex > fillLow) { const float diff = lowBinIndex - fillLow; _data[fillLow] -= diff * value; } if (highBinIndex < fillHigh) { const float diff = -highBinIndex + fillHigh; _data[fillHigh - 1] -= diff * value; } return true; } float Histogram::interpolate(float bin) const { const float normalizedBin = (bin - _minValue) / (_maxValue - _minValue); const float binIndex = normalizedBin * _numBins - 0.5f; // Center const float interpolator = binIndex - floor(binIndex); int binLow = static_cast(floor(binIndex)); int binHigh = static_cast(ceil(binIndex)); // Clamp bins if (binLow < 0) { binLow = 0; } if (binHigh >= _numBins) { binHigh = _numBins - 1; } return (1.f - interpolator) * _data[binLow] + interpolator * _data[binHigh]; } float Histogram::sample(int binIndex) const { ghoul_assert(binIndex >= 0 && binIndex < _numBins, "binIndex out of range"); return _data[binIndex]; } const float* Histogram::data() const { return _data; } std::vector> Histogram::getDecimated(int) const { // Return a copy of _data decimated as in Ljung 2004 return std::vector>(); } void Histogram::normalize() { float sum = 0.0; for (int i = 0; i < _numBins; i++) { sum += _data[i]; } for (int i = 0; i < _numBins; i++) { _data[i] /= sum; } } /* * Will create an internal array for histogram equalization. * Old histogram value is the index of the array, and the new equalized * value will be the value at the index. */ void Histogram::generateEqualizer() { float previousCdf = 0.0f; _equalizer = std::vector(_numBins, 0.0f); for (int i = 0; i < _numBins; i++) { const float probability = _data[i] / static_cast(_numValues); const float cdf = std::min(1.0f, previousCdf + probability); _equalizer[i] = cdf * (_numBins-1); previousCdf = cdf; } } /* * Will return a equalized histogram */ Histogram Histogram::equalize() { Histogram equalizedHistogram(_minValue, _maxValue, _numBins); for (int i = 0; i < _numBins; i++) { equalizedHistogram._data[static_cast(_equalizer[i])] += _data[i]; } equalizedHistogram._numValues = _numValues; return equalizedHistogram; } /* * Given a value within the domain of this histogram (_minValue < value < maxValue), * this method will use its equalizer to return a histogram equalized result. */ float Histogram::equalize(float value) const { // if (value < _minValue || value > _maxValue) { // LWARNING( // "Equalized value is is not within domain of histogram. min: " + // std::to_string(_minValue) + " max: " + std::to_string(_maxValue) + // " val: " + std::to_string(value) // ); // } float normalizedValue = (value-_minValue)/(_maxValue-_minValue); int bin = static_cast(floor(normalizedValue * _numBins)); // If value == _maxValues then bin == _numBins, which is a invalid index. bin = std::min(_numBins-1, bin); bin = std::max(0, bin); return _equalizer[bin]; } float Histogram::entropy() { float entropy = 0.f; for (int i = 0; i < _numBins; ++i) { if (_data[i] != 0) { entropy -= (_data[i] / static_cast(_numValues)) * (log2(_data[i]) / static_cast(_numValues)); } } return entropy; } std::vector Histogram::getBinaryData() const { std::vector dataVector( reinterpret_cast(_data), reinterpret_cast(_data + _numValues) ); return dataVector; } void Histogram::print() const { //for (int i = 0; i < _numBins; i++) { // //float low = _minValue + float(i) / _numBins * (_maxValue - _minValue); // //float high = low + (_maxValue - _minValue) / float(_numBins); // // std::cout << i << " [" << low << ", " << high << "]" // // << " " << _data[i] << std::endl; // std::cout << _data[i]/static_cast(_numValues) << ", "; //} //std::cout << std::endl; //// std::cout << std::endl << std::endl << std::endl<< "==============" << std::endl; } float Histogram::highestBinValue(bool equalized, int overBins){ int highestBin = 0; float highestValue = 0; for (int i = 0; i < _numBins; i++) { float value = 0; int num = 0; for (int j=0; j 0) { value += _data[i-j]; num++; } if (i + j < _numBins) { value += _data[i+j]; num++; } } value += _data[i]; value /= static_cast(++num); if (value > highestValue) { highestBin = i; highestValue = value; } } if (!equalized) { float low = _minValue + static_cast(highestBin) / _numBins * (_maxValue - _minValue); float high = low + (_maxValue - _minValue) / static_cast(_numBins); return (high+low) / 2.f; } else { return highestBin / static_cast(_numBins); // return equalize((high+low)/2.0); } } float Histogram::binWidth() { return (_maxValue - _minValue) / _numBins; } } // namespace openspace