diff --git a/UnitTest/sicd_Test.h b/UnitTest/sicd_Test.h index 27417d826d..9b1f0467fb 100644 --- a/UnitTest/sicd_Test.h +++ b/UnitTest/sicd_Test.h @@ -28,8 +28,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/six/modules/c++/six.sicd/include/six/sicd/ComplexData.h b/six/modules/c++/six.sicd/include/six/sicd/ComplexData.h index 7b3f1f7917..b55a7d150b 100644 --- a/six/modules/c++/six.sicd/include/six/sicd/ComplexData.h +++ b/six/modules/c++/six.sicd/include/six/sicd/ComplexData.h @@ -162,7 +162,7 @@ struct ComplexData: public Data { imageData->pixelType = pixelType; } - bool convertPixels_(std::span, std::span, ptrdiff_t cutoff) const override; + bool convertPixels_(std::span, std::span) const override; /*! * Maps to: /SICD/ImageData/NumRows, diff --git a/six/modules/c++/six.sicd/include/six/sicd/ComplexToAMP8IPHS8I.h b/six/modules/c++/six.sicd/include/six/sicd/ComplexToAMP8IPHS8I.h deleted file mode 100644 index d3545448a5..0000000000 --- a/six/modules/c++/six.sicd/include/six/sicd/ComplexToAMP8IPHS8I.h +++ /dev/null @@ -1,80 +0,0 @@ -/* ========================================================================= -* This file is part of six.sicd-c++ -* ========================================================================= -* -* (C) Copyright 2021, Maxar Technologies, Inc. -* -* six.sicd-c++ is free software; you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation; either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this program; If not, -* see . -* -*/ -#ifndef SIX_six_sicd_ComplexToAMP8IPHS8I_h_INCLUDED_ -#define SIX_six_sicd_ComplexToAMP8IPHS8I_h_INCLUDED_ -#pragma once - -#include - -#include -#include -#include -#include - -#include - -namespace six -{ -namespace sicd -{ -namespace details -{ -/*! - * \brief A utility that's used to convert complex values into 8-bit amplitude and phase values. - */ -class ComplexToAMP8IPHS8I final -{ - /*! - * Create a lookup structure that converts from complex to amplitude and phase. - * @param pAmplitudeTable optional amplitude table. - */ - explicit ComplexToAMP8IPHS8I(const six::AmplitudeTable* pAmplitudeTable = nullptr); -public: - static const ComplexToAMP8IPHS8I* make(const six::AmplitudeTable* pAmplitudeTable, std::unique_ptr&); - - ~ComplexToAMP8IPHS8I() = default; - ComplexToAMP8IPHS8I(const ComplexToAMP8IPHS8I&) = delete; - ComplexToAMP8IPHS8I& operator=(const ComplexToAMP8IPHS8I&) = delete; - ComplexToAMP8IPHS8I(ComplexToAMP8IPHS8I&&) = delete; - ComplexToAMP8IPHS8I& operator=(ComplexToAMP8IPHS8I&&) = delete; - - /*! - * Get the nearest amplitude and phase value given a complex value - * @param v complex value to query with - * @return nearest amplitude and phase value - */ - AMP8I_PHS8I_t nearest_neighbor(const std::complex& v) const; - -private: - //! The sorted set of possible magnitudes order from small to large. - std::vector uncached_magnitudes; // Order is important! This must be ... - const std::vector& magnitudes; // ... before this. - - //! The difference in phase angle between two UINT phase values. - long double phase_delta; - //! Unit vector rays that represent each direction that phase can point. - std::array, UINT8_MAX + 1> phase_directions; -}; -} -} -} -#endif // SIX_six_sicd_ComplexToAMP8IPHS8I_h_INCLUDED_ diff --git a/six/modules/c++/six.sicd/include/six/sicd/ImageData.h b/six/modules/c++/six.sicd/include/six/sicd/ImageData.h index 10d57f773c..36e0093949 100644 --- a/six/modules/c++/six.sicd/include/six/sicd/ImageData.h +++ b/six/modules/c++/six.sicd/include/six/sicd/ImageData.h @@ -40,15 +40,6 @@ namespace six namespace sicd { using cx_float = std::complex; -struct AMP8I_PHS8I_t final // std::pair isn't trivial_copyable() -{ - uint8_t first; // amplitude - uint8_t second; // phase -}; -//! Fixed size 256 element array of complex values. -using input_values_t = std::array, UINT8_MAX + 1>; -//! Fixed size 256 x 256 matrix of complex values. -using input_amplitudes_t = std::array; class GeoData; /*! @@ -106,22 +97,18 @@ struct ImageData bool validate(const GeoData& geoData, logging::Logger& log) const; - // It would be nice to cache the results, but amplitudeTable could change at any time. - cx_float from_AMP8I_PHS8I(const AMP8I_PHS8I_t&) const; // for unit-tests - static void to_AMP8I_PHS8I(const AmplitudeTable*, std::span, std::span, ptrdiff_t cutoff = -1); // for unit-tests + static void testing_fromComplex_(std::span, std::span); // for unit-tests - static void from_AMP8I_PHS8I(const input_amplitudes_t& lookup, std::span, std::span, ptrdiff_t cutoff = -1); - void from_AMP8I_PHS8I(std::span, std::span, ptrdiff_t cutoff = -1) const; - void to_AMP8I_PHS8I(std::span, std::span, ptrdiff_t cutoff = -1) const; + static void toComplex(const six::Amp8iPhs8iLookup_t& lookup, std::span, std::span); + void toComplex(std::span, std::span) const; + void fromComplex(std::span, std::span) const; /*! * Create a lookup table for converting from AMP8I_PHS8I to complex. * @param pAmplitudeTable Input amplitude table. May be nullptr if no amplitude table is defined. - * @param pValues_ Output table's scope to keep it around past the function call. May be empty if there was no input amplitude table. * @return reference to the output lookup table. */ - static const input_amplitudes_t& get_RE32F_IM32F_values(const six::AmplitudeTable* pAmplitudeTable, - std::unique_ptr& pValues_); + static const six::Amp8iPhs8iLookup_t& getLookup(const six::AmplitudeTable* pAmplitudeTable); }; } diff --git a/six/modules/c++/six.sicd/include/six/sicd/Utilities.h b/six/modules/c++/six.sicd/include/six/sicd/Utilities.h index 898a000c77..241866e8f6 100644 --- a/six/modules/c++/six.sicd/include/six/sicd/Utilities.h +++ b/six/modules/c++/six.sicd/include/six/sicd/Utilities.h @@ -19,8 +19,9 @@ * see . * */ -#ifndef __SIX_SICD_UTILITIES_H__ -#define __SIX_SICD_UTILITIES_H__ +#pragma once +#ifndef SIX_six_sicd_Utilities_h_INCLUDED_ +#define SIX_six_sicd_Utilities_h_INCLUDED_ #include #include @@ -41,9 +42,8 @@ namespace six { namespace sicd { -class Utilities +struct Utilities final { -public: /*! * Build SceneGeometry from ComplexData members * \param data ComplexData from which to construct Geometry @@ -628,7 +628,16 @@ class Utilities const std::vector >& opPixels, std::vector >& spPixels); - static std::complex from_AMP8I_PHS8I(uint8_t input_amplitude, uint8_t input_value, const six::AmplitudeTable*); + // https://en.cppreference.com/w/cpp/algorithm/iota + // Generating all `uint8_t` values is slightly tricky because wrap-around/overflow must be avoided. + static std::vector iota_0_256(); // [0, 256), i.e., [0x00, 0xff] + + // Convert the amp/phase to a complex value using the given AmplitudeTable, if any. + // This call could be in a tight loop where the value of six::AmplitudeTable* is known outside of the loop; + // the overloads allow clients to avoid an inner `if`-check. + static std::complex toComplex(uint8_t amplitude, uint8_t phase, const six::AmplitudeTable* pAmplitudeTable); + static std::complex toComplex(uint8_t amplitude, uint8_t phase, const six::AmplitudeTable&); + static std::complex toComplex(uint8_t amplitude, uint8_t phase); }; @@ -654,9 +663,9 @@ extern void writeAsNITF(const std::filesystem::path&, const std::vector> make_complex_image(const types::RowCol&); - extern std::vector toBytes(const ComplexImageResult&, ptrdiff_t cutoff=-1); + extern std::vector toBytes(const ComplexImageResult&); } } } -#endif +#endif // SIX_six_sicd_Utilities_h_INCLUDED_ diff --git a/six/modules/c++/six.sicd/six.sicd.vcxproj b/six/modules/c++/six.sicd/six.sicd.vcxproj index 92b676a1ef..52ac800096 100644 --- a/six/modules/c++/six.sicd/six.sicd.vcxproj +++ b/six/modules/c++/six.sicd/six.sicd.vcxproj @@ -107,7 +107,6 @@ - diff --git a/six/modules/c++/six.sicd/six.sicd.vcxproj.filters b/six/modules/c++/six.sicd/six.sicd.vcxproj.filters index 0c3499b609..d8d539a33f 100644 --- a/six/modules/c++/six.sicd/six.sicd.vcxproj.filters +++ b/six/modules/c++/six.sicd/six.sicd.vcxproj.filters @@ -129,9 +129,6 @@ Header Files - - Header Files - diff --git a/six/modules/c++/six.sicd/source/ComplexData.cpp b/six/modules/c++/six.sicd/source/ComplexData.cpp index 4c3fdcf106..12d47d3502 100644 --- a/six/modules/c++/six.sicd/source/ComplexData.cpp +++ b/six/modules/c++/six.sicd/source/ComplexData.cpp @@ -369,15 +369,16 @@ inline std::span make_span(std::span bytes) return std::span(static_cast(cast_to_pvoid(bytes)), size); } -bool six::sicd::ComplexData::convertPixels_(std::span from_, std::span to_, ptrdiff_t cutoff) const +bool six::sicd::ComplexData::convertPixels_(std::span from_, std::span to_) const { if (getPixelType() != PixelType::AMP8I_PHS8I) { return false; // no conversion done as there is nothing to convert } + // Convert the pixels from a complex to AMP8I_PHS8I. const auto from = make_span(from_); - const auto to = make_span(to_); - imageData->to_AMP8I_PHS8I(from, to, cutoff); + const auto to = make_span(to_); + imageData->fromComplex(from, to); return true; // converted } diff --git a/six/modules/c++/six.sicd/source/ComplexToAMP8IPHS8I.cpp b/six/modules/c++/six.sicd/source/ComplexToAMP8IPHS8I.cpp index 63ca2d1d40..29211eb5bc 100644 --- a/six/modules/c++/six.sicd/source/ComplexToAMP8IPHS8I.cpp +++ b/six/modules/c++/six.sicd/source/ComplexToAMP8IPHS8I.cpp @@ -19,9 +19,10 @@ * see . * */ -#include "six/sicd/ComplexToAMP8IPHS8I.h" +#include "six/AmplitudeTable.h" #include +#include #include #include @@ -31,6 +32,8 @@ #include #include +#include "six/sicd/Utilities.h" + // https://github.com/ngageoint/six-library/pull/537#issuecomment-1026453353 /* I can add more detail, but be warned that my powerpoint skills aren't amazing and this is best drawn on a whiteboard or graph paper. @@ -65,17 +68,17 @@ inline long double GetPhase(const std::complex& v) return phase; } -static std::vector make_magnitudes(const six::AmplitudeTable* pAmplitudeTable) +template +static std::vector make_magnitudes_(TToComplexFunc toComplex) { - std::vector retval(UINT8_MAX + 1); - for (size_t i = 0; i <= UINT8_MAX; i++) // Be careful with indexing so that we don't wrap-around in the loops. + std::vector retval; + retval.reserve(UINT8_MAX + 1); + for (const auto amplitude : six::sicd::Utilities::iota_0_256()) { - static_assert(sizeof(size_t) > sizeof(uint8_t), "size_t can't hold UINT8_MAX!"); // AmpPhase -> Complex - const auto amplitude = gsl::narrow(i); - const auto value = amplitude; - const auto complex = six::sicd::Utilities::from_AMP8I_PHS8I(amplitude, value, pAmplitudeTable); - retval[i] = std::abs(complex); + const auto phase = amplitude; + const auto complex = toComplex(amplitude, phase); + retval.push_back(std::abs(complex)); } // I don't know if we can guarantee that the amplitude table is non-decreasing. @@ -86,90 +89,115 @@ static std::vector make_magnitudes(const six::AmplitudeTable* pAmpl } return retval; } +static inline auto make_magnitudes(const six::AmplitudeTable& amplitudeTable) +{ + const auto toComplex = [&](auto amplitude, auto phase) + { + return six::sicd::Utilities::toComplex(amplitude, phase, amplitudeTable); + }; + return make_magnitudes_(toComplex); +} +static inline auto make_magnitudes() +{ + static const auto toComplex = [](auto amplitude, auto phase) + { + return six::sicd::Utilities::toComplex(amplitude, phase); + }; + return make_magnitudes_(toComplex); +} + static const std::vector& get_magnitudes(const six::AmplitudeTable* pAmplitudeTable, std::vector& uncached_magnitudes) { if (pAmplitudeTable == nullptr) { - static const auto magnitudes = make_magnitudes(nullptr); // OK to cache, won't change + static const auto magnitudes = make_magnitudes(); // OK to cache, won't change return magnitudes; } - uncached_magnitudes = make_magnitudes(pAmplitudeTable); + uncached_magnitudes = make_magnitudes(*pAmplitudeTable); return uncached_magnitudes; } six::sicd::details::ComplexToAMP8IPHS8I::ComplexToAMP8IPHS8I(const six::AmplitudeTable *pAmplitudeTable) : magnitudes(get_magnitudes(pAmplitudeTable, uncached_magnitudes)) { - const auto p0 = GetPhase(Utilities::from_AMP8I_PHS8I(1, 0, pAmplitudeTable)); - const auto p1 = GetPhase(Utilities::from_AMP8I_PHS8I(1, 1, pAmplitudeTable)); + const auto p0 = GetPhase(Utilities::toComplex(1, 0, pAmplitudeTable)); + const auto p1 = GetPhase(Utilities::toComplex(1, 1, pAmplitudeTable)); assert(p0 == 0.0); assert(p1 > p0); phase_delta = p1 - p0; - for(size_t i = 0; i <= UINT8_MAX; i++) // Be careful with indexing so that we don't wrap-around in the loops. + size_t i = 0; + for(const auto value : six::sicd::Utilities::iota_0_256()) { - static_assert(sizeof(size_t) > sizeof(uint8_t), "size_t can't hold UINT8_MAX!"); - const units::Radians angle{ p0 + gsl::narrow_cast(i) * phase_delta }; + const units::Radians angle{ p0 + value * phase_delta }; long double y, x; SinCos(angle, y, x); phase_directions[i] = { x, y }; + i++; } } /*! * Find the nearest element given an iterator range. - * @tparam TIter type of iterator - * @param begin beginning of search range - * @param end end of search range * @param value query value * @return index of nearest value within the iterator range. */ -template -static uint8_t nearest(const TIter& begin, const TIter& end, long double value) +static inline uint8_t nearest(const std::vector& magnitudes, long double value) { + const auto begin = magnitudes.begin(); + const auto end = magnitudes.end(); + const auto it = std::lower_bound(begin, end, value); - if(it == begin) return 0; + if (it == begin) return 0; const auto prev_it = std::prev(it); - const auto nearest = (it == end || value - *prev_it <= *it - value) ? prev_it : it; - const auto distance = std::distance(begin, nearest); + const auto nearest_it = it == end ? prev_it : + (value - *prev_it <= *it - value ? prev_it : it); + const auto distance = std::distance(begin, nearest_it); assert(distance <= std::numeric_limits::max()); return gsl::narrow(distance); } -six::sicd::AMP8I_PHS8I_t six::sicd::details::ComplexToAMP8IPHS8I::nearest_neighbor(const std::complex &v) const +six::AMP8I_PHS8I_t six::sicd::details::ComplexToAMP8IPHS8I::nearest_neighbor(const std::complex &v) const { - six::sicd::AMP8I_PHS8I_t retval; + six::AMP8I_PHS8I_t retval; // Phase is determined via arithmetic because it's equally spaced. // There's an intentional conversion to zero when we cast 256 -> uint8. That wrap around // handles cases that are close to 2PI. - retval.second = gsl::narrow_cast(std::round(GetPhase(v) / phase_delta)); + retval.phase = gsl::narrow_cast(std::round(GetPhase(v) / phase_delta)); // We have to do a 1D nearest neighbor search for magnitude. // But it's not the magnitude of the input complex value - it's the projection of // the complex value onto the ray of candidate magnitudes at the selected phase. // i.e. dot product. - auto&& phase_direction = phase_directions[retval.second]; + auto&& phase_direction = phase_directions[retval.phase]; const auto projection = phase_direction.real() * v.real() + phase_direction.imag() * v.imag(); //assert(std::abs(projection - std::abs(v)) < 1e-5); // TODO ??? - retval.first = nearest(magnitudes.begin(), magnitudes.end(), projection); + retval.amplitude = nearest(magnitudes, projection); return retval; } -const six::sicd::details::ComplexToAMP8IPHS8I* six::sicd::details::ComplexToAMP8IPHS8I::make(const six::AmplitudeTable* pAmplitudeTable, - std::unique_ptr& pTree) +const six::sicd::details::ComplexToAMP8IPHS8I& six::sicd::details::ComplexToAMP8IPHS8I::make(const six::AmplitudeTable* pAmplitudeTable) { if (pAmplitudeTable == nullptr) { // this won't change, so OK to cache static const six::sicd::details::ComplexToAMP8IPHS8I tree; - return &tree; + return tree; } else { - pTree.reset(new six::sicd::details::ComplexToAMP8IPHS8I(pAmplitudeTable)); - return pTree.get(); + auto pFromComplex = pAmplitudeTable->getFromComplex(); + if (pFromComplex != nullptr) + { + return *pFromComplex; + } + + // constructor is private, can't use make_unique + std::unique_ptr pTree(new six::sicd::details::ComplexToAMP8IPHS8I(pAmplitudeTable)); + pAmplitudeTable->cacheFromComplex_(std::move(pTree)); + return *(pAmplitudeTable->getFromComplex()); } } \ No newline at end of file diff --git a/six/modules/c++/six.sicd/source/ImageData.cpp b/six/modules/c++/six.sicd/source/ImageData.cpp index 60511ebcd2..c92ef781fe 100644 --- a/six/modules/c++/six.sicd/source/ImageData.cpp +++ b/six/modules/c++/six.sicd/source/ImageData.cpp @@ -32,9 +32,12 @@ #include #include +#include "six/AmplitudeTable.h" #include "six/sicd/GeoData.h" #include "six/sicd/Utilities.h" -#include "six/sicd/ComplexToAMP8IPHS8I.h" + +using namespace six; +using namespace six::sicd; // There was in coda-oss, but I removed it. // @@ -42,47 +45,44 @@ // bit "half baked," and perhaps shouldn't be emulated. Then, C++17 added // parallel algorithms which might be a better way of satisfying our immediate // needs (below) ... although we're still at C++14. -namespace +template +static inline OutputIt transform_async(const InputIt first1, const InputIt last1, OutputIt d_first, TFunc f, + typename std::iterator_traits::difference_type cutoff) { - namespace details + // https://en.cppreference.com/w/cpp/thread/async + const auto len = std::distance(first1, last1); + if (len < cutoff) { - template - inline OutputIt transform_async(const InputIt first1, const InputIt last1, OutputIt d_first, TFunc f, - typename std::iterator_traits::difference_type cutoff, std::launch policy) - { - // https://en.cppreference.com/w/cpp/thread/async - const auto len = std::distance(first1, last1); - if (len < cutoff) - { - return std::transform(first1, last1, d_first, f); - } - - const auto mid1 = first1 + len / 2; - const auto d_mid = d_first + len / 2; - auto handle = std::async(policy, transform_async, mid1, last1, d_mid, f, cutoff, policy); - details::transform_async(first1, mid1, d_first, f, cutoff, policy); - return handle.get(); - } + return std::transform(first1, last1, d_first, f); } - template - inline OutputIt transform_async(const InputIt first1, const InputIt last1, OutputIt d_first, TFunc f, - typename std::iterator_traits::difference_type cutoff, std::launch policy) + + constexpr auto policy = std::launch::async; + + const auto mid1 = first1 + len / 2; + const auto d_mid = d_first + len / 2; + auto handle = std::async(policy, transform_async, mid1, last1, d_mid, f, cutoff); + transform_async(first1, mid1, d_first, f, cutoff); + return handle.get(); +} +template +static inline void transform(std::span inputs, std::span results, TFunc f) +{ + constexpr ptrdiff_t cutoff_ = 0; // too slow w/o multi-threading + if (cutoff_ < 0) { - // details::... eliminates the overload - return details::transform_async(first1, last1, d_first, f, cutoff, policy); + std::ignore = std::transform(inputs.begin(), inputs.end(), results.begin(), f); } - template - inline OutputIt transform_async(const InputIt first1, const InputIt last1, OutputIt d_first, TFunc f, - typename std::iterator_traits::difference_type cutoff) + else { - const std::launch policy = std::launch::deferred | std::launch::async; - return transform_async(first1, last1, d_first, f, cutoff, policy); + // The value of "default_cutoff" was determined by testing; there is nothing special about it, feel free to change it. + constexpr auto dimension = 128 * 8; + constexpr auto default_cutoff = dimension * dimension; + const auto cutoff = cutoff_ == 0 ? default_cutoff : cutoff_; + + std::ignore = transform_async(inputs.begin(), inputs.end(), results.begin(), f, cutoff); } } -using namespace six; -using namespace six::sicd; - bool ImageData::operator==(const ImageData& rhs) const { return (pixelType == rhs.pixelType && @@ -175,167 +175,98 @@ bool ImageData::validate(const GeoData& geoData, logging::Logger& log) const return valid; } -struct KDNode_t final +template +static auto createLookup(TToComplexFunc toComplex) { - cx_float result; - AMP8I_PHS8I_t amp_and_value; -}; -static std::vector make_nodes(const six::AmplitudeTable* pAmplitudeTable) -{ - // For all possible amp/phase values (there are "only" 256*256), get and save the + auto retval = std::make_unique(); // too big for the stack + auto& values = *retval; + + // For all possible amp/phase values (there are "only" 256*256=65536), get and save the // complex value. - // - // Be careful with indexing so that we don't wrap-around in the loops. - std::vector retval; - retval.reserve(UINT8_MAX * UINT8_MAX); - for (uint16_t input_amplitude = 0; input_amplitude <= UINT8_MAX; input_amplitude++) + for (const auto amplitude : Utilities::iota_0_256()) { - KDNode_t v; - v.amp_and_value.first = gsl::narrow(input_amplitude); - - for (uint16_t input_value = 0; input_value <= UINT8_MAX; input_value++) + for (const auto phase : Utilities::iota_0_256()) { - v.amp_and_value.second = gsl::narrow(input_value); - v.result = six::sicd::Utilities::from_AMP8I_PHS8I(v.amp_and_value.first, v.amp_and_value.second, pAmplitudeTable); - retval.push_back(v); + values[amplitude][phase] = toComplex(amplitude, phase); } } + return retval; } - -// input_amplitudes_t is too big for the stack -static std::unique_ptr AMP8I_PHS8I_to_RE32F_IM32F_(const six::AmplitudeTable* pAmplitudeTable) +static auto createLookup(const six::AmplitudeTable& amplitudeTable) { - // Get all 256x256 values for the AmplitudeTable - auto nodes = make_nodes(pAmplitudeTable); - - auto retval = std::make_unique(); - auto& values = *retval; - for (auto&& n : nodes) - { - values[n.amp_and_value.first][n.amp_and_value.second] = std::move(n.result); - } - - return retval; + const auto toComplex = [&litudeTable](auto amplitude, auto phase) { + return six::sicd::Utilities::toComplex(amplitude, phase, amplitudeTable); + }; + return createLookup(toComplex); } - -// This is a non-templatized function so that there is copy of the "static" data with a NULL AmplutdeTable. -static const input_amplitudes_t* get_cached_RE32F_IM32F_values(const six::AmplitudeTable* pAmplitudeTable) +static auto createLookup() { - if (pAmplitudeTable == nullptr) - { - static const auto RE32F_IM32F_values_no_amp = AMP8I_PHS8I_to_RE32F_IM32F_(nullptr); - return RE32F_IM32F_values_no_amp.get(); - } - return nullptr; + static const auto toComplex = [](auto amplitude, auto phase) { + return six::sicd::Utilities::toComplex(amplitude, phase); + }; + return createLookup(toComplex); } -std::complex ImageData::from_AMP8I_PHS8I(const AMP8I_PHS8I_t& input) const +static const six::Amp8iPhs8iLookup_t* getCachedLookup(const six::AmplitudeTable* pAmplitudeTable) { - if (pixelType != PixelType::AMP8I_PHS8I) - { - throw std::runtime_error("pxielType must be AMP8I_PHS8I"); - } - - auto const pAmplitudeTable = amplitudeTable.get(); - auto const pValues = get_cached_RE32F_IM32F_values(pAmplitudeTable); - - // Do we have a cahced result to use (no amplitude table)? - // Or must it be recomputed (have an amplutude table)? - if (pValues != nullptr) + if (pAmplitudeTable == nullptr) { - return (*pValues)[input.first][input.second]; + static const auto lookup_no_table = createLookup(); + return lookup_no_table.get(); } - const auto S = Utilities::from_AMP8I_PHS8I(input.first, input.second, pAmplitudeTable); - return std::complex(gsl::narrow_cast(S.real()), gsl::narrow_cast(S.imag())); + // Maybe one has already been created and stored on the table? + return pAmplitudeTable->getLookup(); } -const input_amplitudes_t& ImageData::get_RE32F_IM32F_values(const six::AmplitudeTable* pAmplitudeTable, - std::unique_ptr& pValues_) +const six::Amp8iPhs8iLookup_t& ImageData::getLookup(const six::AmplitudeTable* pAmplitudeTable) { - const input_amplitudes_t* pValues = get_cached_RE32F_IM32F_values(pAmplitudeTable); - if (pValues == nullptr) + auto pLookup = getCachedLookup(pAmplitudeTable); + if (pLookup == nullptr) { assert(pAmplitudeTable != nullptr); - pValues_ = AMP8I_PHS8I_to_RE32F_IM32F_(pAmplitudeTable); - pValues = pValues_.get(); + auto& amplitudeTable = *pAmplitudeTable; + auto lookup = createLookup(amplitudeTable); + amplitudeTable.cacheLookup_(std::move(lookup)); + pLookup = amplitudeTable.getLookup(); } - assert(pValues != nullptr); - return *pValues; + assert(pLookup != nullptr); + return *pLookup; } -void ImageData::from_AMP8I_PHS8I(std::span inputs, std::span> results, - ptrdiff_t cutoff_) const +void ImageData::toComplex(const six::Amp8iPhs8iLookup_t& values, std::span inputs, std::span> results) { - if (pixelType != PixelType::AMP8I_PHS8I) + const auto toComplex_ = [&values](const auto& v) { - throw std::runtime_error("pxielType must be AMP8I_PHS8I"); - } - - std::unique_ptr pValues_; - const auto& values = get_RE32F_IM32F_values(amplitudeTable.get(), pValues_); - from_AMP8I_PHS8I(values, inputs, results, cutoff_); + return values[v.amplitude][v.phase]; + }; + transform(inputs, results, toComplex_); } - -void ImageData::from_AMP8I_PHS8I(const input_amplitudes_t& values, std::span inputs, std::span> results, - ptrdiff_t cutoff_) +void ImageData::toComplex(std::span inputs, std::span> results) const { - const auto get_RE32F_IM32F_value_f = [&values](const six::sicd::AMP8I_PHS8I_t& v) - { - return values[v.first][v.second]; - }; - - if (cutoff_ < 0) - { - std::ignore = std::transform(inputs.begin(), inputs.end(), results.begin(), - get_RE32F_IM32F_value_f); - } - else + if (pixelType != PixelType::AMP8I_PHS8I) { - // The value of "default_cutoff" was determined by testing; there is nothing special about it, feel free to change it. - constexpr auto dimension = 128 * 8; - constexpr auto default_cutoff = dimension * dimension; - const auto cutoff = cutoff_ == 0 ? default_cutoff : cutoff_; - std::ignore = transform_async(inputs.begin(), inputs.end(), results.begin(), - get_RE32F_IM32F_value_f, cutoff, std::launch::async); + throw std::runtime_error("pxielType must be AMP8I_PHS8I"); } + + const auto& values = getLookup(amplitudeTable.get()); + toComplex(values, inputs, results); } -template -static void to_AMP8I_PHS8I_(std::span inputs, std::span results, - const TConverter& tree, ptrdiff_t cutoff_) +void ImageData::fromComplex(std::span inputs, std::span results) const { - const auto nearest_neighbor_f = [&](const std::complex& v) + // make a structure to quickly find the nearest neighbor + auto& converter = six::sicd::details::ComplexToAMP8IPHS8I::make(amplitudeTable.get()); + const auto fromComplex_ = [&converter](const auto& v) { - return tree.nearest_neighbor(v); + return converter.nearest_neighbor(v); }; - - if (cutoff_ < 0) - { - std::ignore = std::transform(inputs.begin(), inputs.end(), results.begin(), - nearest_neighbor_f); - } - else - { - // The value of "default_cutoff" was determined by testing; there is nothing special about it, feel free to change it. - constexpr auto dimension = 128 * 8; - constexpr auto default_cutoff = dimension * dimension; - const auto cutoff = cutoff_ == 0 ? default_cutoff : cutoff_; - std::ignore = transform_async(inputs.begin(), inputs.end(), results.begin(), - nearest_neighbor_f, cutoff, std::launch::async); - } + transform(inputs, results, fromComplex_); } -void ImageData::to_AMP8I_PHS8I(std::span inputs, std::span results, - ptrdiff_t cutoff) const +void ImageData::testing_fromComplex_(std::span inputs, std::span results) { - to_AMP8I_PHS8I(amplitudeTable.get(), inputs, results, cutoff); -} -void ImageData::to_AMP8I_PHS8I(const AmplitudeTable* pAmplitudeTable, - std::span inputs, std::span results, ptrdiff_t cutoff) -{ - // make a structure to quickly find the nearest neighbor - std::unique_ptr pConvert; // not-cached, non-NULL amplitudeTable - const auto& converter = *(six::sicd::details::ComplexToAMP8IPHS8I::make(pAmplitudeTable, pConvert)); - to_AMP8I_PHS8I_(inputs, results, converter, cutoff); + static const ImageData imageData; + assert(imageData.amplitudeTable.get() == nullptr); + imageData.fromComplex(inputs, results); } diff --git a/six/modules/c++/six.sicd/source/Utilities.cpp b/six/modules/c++/six.sicd/source/Utilities.cpp index 71ef8251a4..3bd9c225ae 100644 --- a/six/modules/c++/six.sicd/source/Utilities.cpp +++ b/six/modules/c++/six.sicd/source/Utilities.cpp @@ -32,24 +32,26 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include +#include +#include #include #include #include #include #include -#include -#include -#include -#include -#include #include #include #include @@ -81,24 +83,11 @@ six::Region buildRegion(const types::RowCol& offset, } } -std::complex six::sicd::Utilities::from_AMP8I_PHS8I(uint8_t input_amplitude, uint8_t input_value, const six::AmplitudeTable* pAmplitudeTable) +static std::complex toComplex_(long double A, uint8_t phase) { - long double A = 0.0; - if (pAmplitudeTable != nullptr) - { - // A = AmpTable( input_amplitude ) - auto& AmpTable = *(pAmplitudeTable); - A = AmpTable.index(input_amplitude); - } - else - { - // A = input_amplitude(i.e. 0 to 255) - A = input_amplitude; - } - // The phase values should be read in (values 0 to 255) and converted to float by doing: // P = (1 / 256) * input_value - const long double P = (1.0 / 256.0) * input_value; + const long double P = (1.0 / 256.0) * phase; // To convert the amplitude and phase values to complex float (i.e. real and imaginary): // S = A * cos(2 * pi * P) + j * A * sin(2 * pi * P) @@ -108,6 +97,47 @@ std::complex six::sicd::Utilities::from_AMP8I_PHS8I(uint8_t input_a std::complex S(A * cos_angle, A * sin_angle); return S; } +std::complex six::sicd::Utilities::toComplex(uint8_t amplitude, uint8_t phase) +{ + // A = input_amplitude(i.e. 0 to 255) + const long double A = amplitude; + return toComplex_(A, phase); +} +std::complex six::sicd::Utilities::toComplex(uint8_t amplitude, uint8_t phase, const six::AmplitudeTable& amplitudeTable) +{ + const long double A = amplitudeTable.index(amplitude); + return toComplex_(A, phase); +} +std::complex six::sicd::Utilities::toComplex(uint8_t amplitude, uint8_t phase, const six::AmplitudeTable* pAmplitudeTable) +{ + if (pAmplitudeTable != nullptr) + { + return toComplex(amplitude, phase, *pAmplitudeTable); + } + else + { + return toComplex(amplitude, phase); + } +} + +static auto iota_0_256_() +{ + static_assert(sizeof(size_t) > sizeof(uint8_t), "size_t can't hold UINT8_MAX!"); + + std::vector retval; + retval.reserve(UINT8_MAX + 1); + for (size_t i = 0; i <= UINT8_MAX; i++) // Be careful with indexing so that we don't wrap-around in the loop. + { + retval.push_back(gsl::narrow(i)); + } + assert(retval.size() == UINT8_MAX + 1); + return retval; +} +std::vector six::sicd::Utilities::iota_0_256() +{ + static const auto retval = iota_0_256_(); + return retval; +} namespace { @@ -174,27 +204,25 @@ class SICD_readerAndConverter final // There's type mangling going on here. // We're taking std::vector and saying it's packed with std::pair. - static_assert(sizeof(uint8_t) * 2 == sizeof(six::sicd::AMP8I_PHS8I_t), "expected packed layout in pair"); - auto packed = reinterpret_cast(tempVector.data()); + static_assert(sizeof(uint8_t) * 2 == sizeof(six::AMP8I_PHS8I_t), "expected packed layout in pair"); + auto packed = reinterpret_cast(tempVector.data()); // Reuse image data's conversion to complex. - static const ptrdiff_t kDefaultCutoff = 0; size_t count = (elementsPerRow * rowsToRead) / 2; - std::span input(packed, count); - std::span> output(bufferPtr, input.size()); - six::sicd::ImageData::from_AMP8I_PHS8I(lookup, input, output, kDefaultCutoff); + auto const input = sys::make_span(packed, count); + auto const output = sys::make_span(bufferPtr, input.size()); + six::sicd::ImageData::toComplex(lookup, input, output); } const types::RowCol& offset; std::complex* buffer; - std::unique_ptr lookupScope; - const six::sicd::input_amplitudes_t& lookup; + const six::Amp8iPhs8iLookup_t& lookup; public: SICD_readerAndConverter(six::NITFReadControl& reader, size_t imageNumber, const types::RowCol& offset, const types::RowCol& extent, size_t elementsPerRow, std::complex* buffer, const six::AmplitudeTable* pAmplitudeTable = nullptr) - : offset(offset), buffer(buffer), lookupScope(nullptr), lookup(six::sicd::ImageData::get_RE32F_IM32F_values(pAmplitudeTable, lookupScope)) + : offset(offset), buffer(buffer), lookup(six::sicd::ImageData::getLookup(pAmplitudeTable)) { SICDreader(reader, imageNumber, offset, extent, elementsPerRow, [&](size_t elementsPerRow, size_t row, size_t rowsToRead, const std::vector& tempVector) @@ -1640,7 +1668,7 @@ std::vector> six::sicd::testing::make_complex_image(const ty return image; } -std::vector six::sicd::testing::toBytes(const ComplexImageResult& result, ptrdiff_t cutoff) +std::vector six::sicd::testing::toBytes(const ComplexImageResult& result) { const auto& image = result.widebandData; const auto bytes = sys::as_bytes(image); @@ -1650,7 +1678,7 @@ std::vector six::sicd::testing::toBytes(const ComplexImageResult& res if (data.getPixelType() == six::PixelType::AMP8I_PHS8I) { retval.resize(image.size() * data.getNumBytesPerPixel()); - data.convertPixels(bytes, sys::make_span(retval), cutoff); + data.convertPixels(bytes, sys::make_span(retval)); } else { diff --git a/six/modules/c++/six.sicd/unittests/test_AMP8I_PHS8I.cpp b/six/modules/c++/six.sicd/unittests/test_AMP8I_PHS8I.cpp index 7f2ef7bf1e..1706424671 100644 --- a/six/modules/c++/six.sicd/unittests/test_AMP8I_PHS8I.cpp +++ b/six/modules/c++/six.sicd/unittests/test_AMP8I_PHS8I.cpp @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include #include @@ -56,7 +56,7 @@ #pragma warning(disable: 4459) // declaration of '...' hides global declaration #endif -using AMP8I_PHS8I_t = six::sicd::AMP8I_PHS8I_t; +using AMP8I_PHS8I_t = six::AMP8I_PHS8I_t; static std::shared_ptr getContainer(six::sicd::NITFReadComplexXMLControl& reader) { @@ -133,7 +133,7 @@ static void test_assert_eq(const std::string& testName, for (size_t i = 0; i < actuals.size(); i++) { const auto& v = amp8i_phs8i[i]; - const auto S = six::sicd::Utilities::from_AMP8I_PHS8I(v.first, v.second, nullptr); + const auto S = six::sicd::Utilities::toComplex(v.amplitude, v.phase); const std::complex result(gsl::narrow_cast(S.real()), gsl::narrow_cast(S.imag())); const auto& expected = actuals[i]; TEST_ASSERT_EQ(expected, result); @@ -141,19 +141,19 @@ static void test_assert_eq(const std::string& testName, } static void from_AMP8I_PHS8I(const six::sicd::ImageData& imageData, - const std::vector& inputs_, std::vector>& results_, ptrdiff_t cutoff = -1) + const std::vector& inputs_, std::vector>& results_) { const std::span inputs(inputs_.data(), inputs_.size()); const std::span> results(results_.data(), results_.size()); - imageData.from_AMP8I_PHS8I(inputs, results, cutoff); + imageData.toComplex(inputs, results); } static void to_AMP8I_PHS8I(const six::sicd::ImageData& imageData, - const std::vector>& inputs_, std::vector& results_, ptrdiff_t cutoff = -1) + const std::vector>& inputs_, std::vector& results_) { const std::span> inputs(inputs_.data(), inputs_.size()); const std::span results(results_.data(), results_.size()); - imageData.to_AMP8I_PHS8I(inputs, results, cutoff); + imageData.fromComplex(inputs, results); } TEST_CASE(test_8bit_ampphs) @@ -163,12 +163,12 @@ TEST_CASE(test_8bit_ampphs) std::vector inputs; std::vector> expecteds; - for (uint16_t input_amplitude = 0; input_amplitude <= UINT8_MAX; input_amplitude++) + for (const auto amplitude : six::sicd::Utilities::iota_0_256()) { - for (uint16_t input_value = 0; input_value <= UINT8_MAX; input_value++) + for (const auto phase : six::sicd::Utilities::iota_0_256()) { - AMP8I_PHS8I_t input{ static_cast(input_amplitude), static_cast(input_value) }; - const auto S = six::sicd::Utilities::from_AMP8I_PHS8I(static_cast(input_amplitude), static_cast(input_value), nullptr); + AMP8I_PHS8I_t input{ amplitude, phase }; + const auto S = six::sicd::Utilities::toComplex(amplitude, phase); inputs.push_back(std::move(input)); expecteds.emplace_back(gsl::narrow_cast(S.real()), gsl::narrow_cast(S.imag())); @@ -186,8 +186,7 @@ TEST_CASE(test_8bit_ampphs) test_assert_eq(testName, actuals, amp8i_phs8i); // ... and again, async - const auto cutoff = actuals.size() / 10; // be sure std::async is called - to_AMP8I_PHS8I(imageData, actuals, amp8i_phs8i, cutoff); + to_AMP8I_PHS8I(imageData, actuals, amp8i_phs8i); test_assert_eq(testName, actuals, amp8i_phs8i); } @@ -219,7 +218,7 @@ static std::vector > read_8bit_ampphs(const std::string& tes const auto pAmplitudeTable = imageData.amplitudeTable.get(); if (pAmplitudeTable != nullptr) { - amplitudeTable = *pAmplitudeTable; + amplitudeTable = std::move(*pAmplitudeTable); } const auto numBytesPerPixel = complexData.getNumBytesPerPixel(); @@ -246,13 +245,13 @@ static Pair to_AMP8I_PHS8I(const six::sicd::ImageData& imageData, cons const auto size = sys::debug ? widebandData.size() / 200 : widebandData.size(); const std::span> widebandData_(widebandData.data(), size); std::vector results(widebandData_.size()); - imageData.to_AMP8I_PHS8I(widebandData_, std::span< AMP8I_PHS8I_t>(results.data(), results.size()), 0); + imageData.fromComplex(widebandData_, std::span< AMP8I_PHS8I_t>(results.data(), results.size())); Pair retval{ 0, 0 }; for (const auto& r : results) { - retval.first += r.first; - retval.second += r.second; + retval.first += r.amplitude; + retval.second += r.phase; } return retval; } @@ -268,7 +267,7 @@ TEST_CASE(read_8bit_ampphs_with_table) const auto widebandData = read_8bit_ampphs(testName, inputPathname, amplitudeTable, pComplexData, expected_sum); TEST_ASSERT_TRUE(amplitudeTable.has_value()); - const auto& AmpTable = amplitudeTable.value(); + auto& AmpTable = amplitudeTable.value(); for (size_t i = 0; i < AmpTable.size(); i++) { // be sure we don't have garbage data @@ -276,7 +275,7 @@ TEST_CASE(read_8bit_ampphs_with_table) } six::sicd::ImageData imageData; - imageData.amplitudeTable.reset(std::make_unique< six::AmplitudeTable>(AmpTable)); + imageData.amplitudeTable.reset(std::make_unique< six::AmplitudeTable>(std::move(AmpTable))); const auto actual = to_AMP8I_PHS8I(imageData, widebandData); const auto expected(sys::debug ? Pair{12647523, 16973148} : Pair{ 3044868397, 3394353166 }); @@ -402,7 +401,7 @@ static std::vector> adjust_image(const six::sicd::ComplexDat std::span from(from_.data(), from_.size()); std::vector> retval(from.size()); - complexData.imageData->from_AMP8I_PHS8I(from, std::span>(retval.data(), retval.size())); + complexData.imageData->toComplex(from, std::span>(retval.data(), retval.size())); return retval; } static std::vector> make_complex_image(const six::sicd::ComplexData& complexData, const types::RowCol& dims) @@ -509,14 +508,14 @@ static void test_assert_image_(const std::string& testName, const std::span> input(image.data(), image.size()); std::vector result(input.size()); std::span< AMP8I_PHS8I_t> result_(result.data(), result.size()); - complexData.imageData->to_AMP8I_PHS8I(input, result_); + complexData.imageData->fromComplex(input, result_); static const std::vector expected_amp8i_phs8i{ AMP8I_PHS8I_t{91, 42}, AMP8I_PHS8I_t{42, 42}, AMP8I_PHS8I_t{42, 42}, AMP8I_PHS8I_t{42, 93} }; // "[******]" for (size_t i = 0; i < result.size(); i++) { - TEST_ASSERT_EQ(result[i].first, expected_amp8i_phs8i[i].first); - TEST_ASSERT_EQ(result[i].second, expected_amp8i_phs8i[i].second); + TEST_ASSERT_EQ(result[i].amplitude, expected_amp8i_phs8i[i].amplitude); + TEST_ASSERT_EQ(result[i].phase, expected_amp8i_phs8i[i].phase); } } @@ -565,11 +564,11 @@ static void test_adjusted_values(const std::string& testName, const std::vector< std::vector actual(expected.size()); std::span actual_(actual.data(), actual.size()); std::span> values_(adjusted_values.data(), adjusted_values.size()); - six::sicd::ImageData::to_AMP8I_PHS8I(nullptr /*pAmplitudeTable*/, values_, actual_); + six::sicd::ImageData::testing_fromComplex_(values_, actual_); for (size_t i = 0; i < expected.size(); i++) { - TEST_ASSERT_EQ(expected[i].first, actual[i].first); - TEST_ASSERT_EQ(expected[i].second, actual[i].second); + TEST_ASSERT_EQ(expected[i].amplitude, actual[i].amplitude); + TEST_ASSERT_EQ(expected[i].phase, actual[i].phase); } } @@ -588,12 +587,12 @@ TEST_CASE(test_nearest_neighbor) std::vector actual(expected.size()); std::span actual_(actual.data(), actual.size()); std::span> values_(values.data(), values.size()); - six::sicd::ImageData::to_AMP8I_PHS8I(nullptr /*pAmplitudeTable*/, values_, actual_); + six::sicd::ImageData::testing_fromComplex_(values_, actual_); for (size_t i = 0; i < expected.size(); i++) { - TEST_ASSERT_EQ(expected[i].first, actual[i].first); - TEST_ASSERT_EQ(expected[i].second, actual[i].second); + TEST_ASSERT_EQ(expected[i].amplitude, actual[i].amplitude); + TEST_ASSERT_EQ(expected[i].phase, actual[i].phase); } auto other_expected = expected; @@ -601,29 +600,29 @@ TEST_CASE(test_nearest_neighbor) constexpr auto delta = 0.0122f; test_adjusted_values(testName, values, other_expected, std::complex(delta, 0.0f)); - other_expected[0].second = 32; + other_expected[0].phase = 32; test_adjusted_values(testName, values, other_expected, std::complex(delta, delta)); - other_expected[0].second += 32; + other_expected[0].phase += 32; test_adjusted_values(testName, values, other_expected, std::complex(0.0f, delta)); - other_expected[0].second += 32; + other_expected[0].phase += 32; test_adjusted_values(testName, values, other_expected, std::complex(-delta, delta)); - other_expected[0].second += 32; + other_expected[0].phase += 32; test_adjusted_values(testName, values, other_expected, std::complex(-delta, 0.0f)); - other_expected[0].second += 32; + other_expected[0].phase += 32; test_adjusted_values(testName, values, other_expected, std::complex(-delta, -delta)); - other_expected[0].second += 32; + other_expected[0].phase += 32; test_adjusted_values(testName, values, other_expected, std::complex(0.0f, -delta)); - other_expected[0].second += 32; + other_expected[0].phase += 32; test_adjusted_values(testName, values, other_expected, std::complex(delta, -delta)); - other_expected[0].second += 32; - TEST_ASSERT_EQ(other_expected[0].second, expected[0].second); + other_expected[0].phase += 32; + TEST_ASSERT_EQ(other_expected[0].phase, expected[0].phase); } TEST_CASE(test_verify_phase_uint8_ordering) @@ -632,7 +631,7 @@ TEST_CASE(test_verify_phase_uint8_ordering) // If this fails, then a core assumption of the ComplexToAmpPhase8I structure is wrong. auto to_phase = [](int v) { - double p = std::arg(six::sicd::Utilities::from_AMP8I_PHS8I(1, v, nullptr)); + double p = std::arg(six::sicd::Utilities::toComplex(1, v)); if(p < 0) p += 2.0 * M_PI; return p; }; @@ -674,8 +673,8 @@ static void do_test_ComplexToAMP8IPHS8I_(const std::string& testName, best = i; } } - TEST_ASSERT_EQ(test_integral.first, best.integral.first); - TEST_ASSERT_EQ(test_integral.second, best.integral.second); + TEST_ASSERT_EQ(test_integral.amplitude, best.integral.amplitude); + TEST_ASSERT_EQ(test_integral.phase, best.integral.phase); } using it_t = std::vector>::const_iterator; static void test_ComplexToAMP8IPHS8I_(const std::string& testName, @@ -710,20 +709,19 @@ TEST_CASE(test_ComplexToAMP8IPHS8I) { // Set up a converter that has a fake amplitude table. six::AmplitudeTable amplitudeTable; // "amp" is a (somewhat) reserved with MSVC - for(size_t i = 0; i < 256; i++) + for(const auto i : six::sicd::Utilities::iota_0_256()) { amplitudeTable.index(i) = static_cast(i) + 10.0; } - std::unique_ptr pTree; // not-cached, non-NULL amplitudeTable - const auto& item = *(six::sicd::details::ComplexToAMP8IPHS8I::make(&litudeTable, pTree)); + const auto& item = six::sicd::details::ComplexToAMP8IPHS8I::make(&litudeTable); // Generate the full 256x256 matrix of possible AMP8I_PHS8I values. std::vector candidates; - for(int i = 0; i < 256; i++) { - for(int j = 0; j < 256; j++) { + for(const auto amplitude : six::sicd::Utilities::iota_0_256()) { + for(const auto phase : six::sicd::Utilities::iota_0_256()) { Pairs p; - p.integral = {gsl::narrow(i), gsl::narrow(j)}; - p.floating = six::sicd::Utilities::from_AMP8I_PHS8I(i, j, &litudeTable); + p.integral = { amplitude, phase }; + p.floating = six::sicd::Utilities::toComplex(amplitude, phase, amplitudeTable); candidates.push_back(p); } } @@ -733,8 +731,8 @@ TEST_CASE(test_ComplexToAMP8IPHS8I) for(auto& i : candidates) { auto truth = i.integral; auto test = item.nearest_neighbor(std::complex(i.floating.real(), i.floating.imag())); - TEST_ASSERT_EQ(truth.first, test.first); - TEST_ASSERT_EQ(truth.second, test.second); + TEST_ASSERT_EQ(truth.amplitude, test.amplitude); + TEST_ASSERT_EQ(truth.phase, test.phase); } // Run an edge case that's very close to a phase of 2PI. @@ -742,7 +740,7 @@ TEST_CASE(test_ComplexToAMP8IPHS8I) std::complex problem { 1.0f, -1e-4f }; - TEST_ASSERT_EQ(item.nearest_neighbor(problem).second, 0); + TEST_ASSERT_EQ(item.nearest_neighbor(problem).phase, 0); // Verify the nearest neighbor property via random search through the possible space. // For each sampled point we check that we found the true nearest neighbor. diff --git a/six/modules/c++/six/include/six/Adapters.h b/six/modules/c++/six/include/six/Adapters.h index d3d9cbd165..7c15a9bbc2 100644 --- a/six/modules/c++/six/include/six/Adapters.h +++ b/six/modules/c++/six/include/six/Adapters.h @@ -156,22 +156,22 @@ class NewMemoryWriteHandler final : public nitf::WriteHandler // all of our code NewMemoryWriteHandler(const NITFSegmentInfo& info, std::span buffer, - size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t cutoff); + size_t firstRow, const Data& data, bool doByteSwap); NewMemoryWriteHandler(const NITFSegmentInfo& info, std::span buffer, - size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t); + size_t firstRow, const Data& data, bool doByteSwap); NewMemoryWriteHandler(const NITFSegmentInfo& info, std::span buffer, - size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t); + size_t firstRow, const Data& data, bool doByteSwap); NewMemoryWriteHandler(const NITFSegmentInfo& info, std::span> buffer, - size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t cutoff); + size_t firstRow, const Data& data, bool doByteSwap); NewMemoryWriteHandler(const NITFSegmentInfo& info, std::span> buffer, - size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t); + size_t firstRow, const Data& data, bool doByteSwap); NewMemoryWriteHandler(const NITFSegmentInfo& info, std::span> buffer, - size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t); + size_t firstRow, const Data& data, bool doByteSwap); }; diff --git a/six/modules/c++/six/include/six/AmplitudeTable.h b/six/modules/c++/six/include/six/AmplitudeTable.h new file mode 100644 index 0000000000..5f4e0e1fed --- /dev/null +++ b/six/modules/c++/six/include/six/AmplitudeTable.h @@ -0,0 +1,323 @@ +/* ========================================================================= + * This file is part of six-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * + * six-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#pragma once +#ifndef SIX_six_AmplitudeTable_h_INCLUDED_ +#define SIX_six_AmplitudeTable_h_INCLUDED_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "six/Enums.h" + +namespace six +{ +/*! + * \struct LUT + * \brief Lookup table + * + * This lookup table object has a number of entries (for pixel ops, + * this is usually 256), and an output space (size) which can be anything + * (e.g., 2 for short) depending on the type that is being housed + * + */ +struct LUT +{ + std::vector table; + size_t numEntries = 0; + size_t elementSize = 0; + + //! Initialize with a number of entries and known output space + LUT(size_t entries, size_t outputSpace) : + table(entries * outputSpace), + numEntries(entries), + elementSize(outputSpace) + { + } + + //! Initialize with an existing LUT, which we copy + LUT(const unsigned char* interleavedLUT, + size_t entries, + size_t outputSpace) : + table(interleavedLUT, interleavedLUT + entries * outputSpace), + numEntries(entries), + elementSize(outputSpace) + { + } + + //! Initialize from nitf::LookupTable read from a NITF + LUT(const nitf::LookupTable& lookupTable) : LUT(lookupTable.getEntries(), lookupTable.getTables()) + { + // NITF stores the tables consecutively. + // Need to interleave them for SIX + if (elementSize == 3) + { + // Imagine the vector is a matrix and then transpose it + for (size_t ii = 0; ii < table.size(); ++ii) + { + table[(ii % numEntries) * elementSize + + (ii / numEntries)] = lookupTable.getTable()[ii]; + } + } + + // I'm not sure why this is a special case, but elements get + // swapped if we try to use the above formula + else if (elementSize == 2) + { + for (size_t ii = 0; ii < numEntries; ++ii) + { + table[2 * ii] = lookupTable.getTable()[numEntries + ii]; + table[2 * ii + 1] = lookupTable.getTable()[ii]; + } + } + } + + virtual ~LUT() = default; + + bool operator==(const LUT& rhs) const + { + return (table == rhs.table && + numEntries == rhs.numEntries && + elementSize == rhs.elementSize); + } + + //! Gives back a pointer at table[i * elementSize] + unsigned char* operator[](size_t i) noexcept + { + return &(table[i * elementSize]); + } + + //! Gives back a pointer at table[i * elementSize] + const unsigned char* operator[](size_t i) const noexcept + { + return &(table[i * elementSize]); + } + + unsigned char* getTable() noexcept + { + return table.empty() ? nullptr : table.data(); + } + + const unsigned char* getTable() const noexcept + { + + return table.empty() ? nullptr : table.data(); + } + + virtual void clone(std::unique_ptr& result) const + { + result = std::make_unique(getTable(), numEntries, elementSize); + } + virtual LUT* clone() const + { + std::unique_ptr result; + clone(result); + return result.release(); + } +}; + +/*! + * \struct AmplitudeTable + * \brief SICD 'AmpTable' parameter + * + * This is a fixed size (256-element) LUT. For AMP8I_PHS8I data, + * the amplitude and phase parts are stored as unsigned 8-bit integers. + * If an amplitude table is given, the amplitude component should be + * interpreted as an index into the AmpTable, ultimately yielding the + * double precision amplitude value + */ + +// Store the computed `std::complex` for every possible +// amp/phs pair, a total of 256*256 values. + //! Fixed size 256 element array of complex values. +using phase_values_t = std::array, UINT8_MAX + 1>; +//! Fixed size 256 x 256 matrix of complex values. +using Amp8iPhs8iLookup_t = std::array; + +// More descriptive than std::pair +struct AMP8I_PHS8I_t final +{ + uint8_t amplitude; + uint8_t phase; +}; + +struct AmplitudeTable; // forward +namespace sicd +{ +namespace details +{ +/*! + * \brief A utility that's used to convert complex values into 8-bit amplitude and phase values. + * + * *** Implemetned in SIX.SICD *** + */ +class ComplexToAMP8IPHS8I final +{ + /*! + * Create a lookup structure that converts from complex to amplitude and phase. + * @param pAmplitudeTable optional amplitude table. + */ + explicit ComplexToAMP8IPHS8I(const six::AmplitudeTable* pAmplitudeTable = nullptr); + +public: + static const ComplexToAMP8IPHS8I& make(const six::AmplitudeTable* pAmplitudeTable); // AmplitudeTable* = NULL is cached + + ~ComplexToAMP8IPHS8I() = default; + ComplexToAMP8IPHS8I(const ComplexToAMP8IPHS8I&) = delete; + ComplexToAMP8IPHS8I& operator=(const ComplexToAMP8IPHS8I&) = delete; + ComplexToAMP8IPHS8I(ComplexToAMP8IPHS8I&&) = default; + ComplexToAMP8IPHS8I& operator=(ComplexToAMP8IPHS8I&&) = default; + + /*! + * Get the nearest amplitude and phase value given a complex value + * @param v complex value to query with + * @return nearest amplitude and phase value + */ + AMP8I_PHS8I_t nearest_neighbor(const std::complex& v) const; + +private: + //! The sorted set of possible magnitudes order from small to large. + std::vector uncached_magnitudes; // Order is important! This must be ... + const std::vector& magnitudes; // ... before this. + + //! The difference in phase angle between two UINT phase values. + long double phase_delta; + //! Unit vector rays that represent each direction that phase can point. + std::array, UINT8_MAX + 1> phase_directions; +}; +} +} + +struct AmplitudeTable final : public LUT +{ + //! Constructor. Creates a 256-entry table + AmplitudeTable(size_t elementSize) noexcept(false) : + LUT(UINT8_MAX + 1 /*i.e., 256*/, elementSize) + { + } + AmplitudeTable() noexcept(false) : AmplitudeTable(sizeof(double)) + { + } + AmplitudeTable(const nitf::LookupTable& lookupTable) noexcept(false) : LUT(lookupTable) + { + if (size() != 256) + { + throw std::invalid_argument("lookupTable should have 256 elements."); + } + } + + AmplitudeTable(const AmplitudeTable&) = delete; // use clone() + AmplitudeTable& operator=(const AmplitudeTable&) = delete; // use clone() + AmplitudeTable(AmplitudeTable&&) = default; + AmplitudeTable& operator=(AmplitudeTable&&) = default; + + size_t size() const noexcept + { + return numEntries; + } + + bool operator==(const AmplitudeTable& rhs) const + { + const LUT* pThis = this; + const LUT* pRHS = &rhs; + return *(pThis) == *(pRHS); + } + bool operator!=(const AmplitudeTable& rhs) const + { + return !(*this == rhs); + } + + const double& index(size_t ii) const noexcept + { + const void* const this_ii = (*this)[ii]; + return *static_cast(this_ii); + } + double& index(size_t ii) noexcept + { + void* const this_ii = (*this)[ii]; + return *static_cast(this_ii); + } + + void clone(std::unique_ptr& ret) const + { + ret = std::make_unique(); + for (size_t ii = 0; ii < numEntries; ++ii) + { + void* const ret_ii = (*ret)[ii]; + *static_cast(ret_ii) = index(ii); + } + + // Don't copy the lookup table; it will be re-created if needed. + } + void clone(std::unique_ptr& ret) const override + { + std::unique_ptr result; + clone(result); + ret.reset(result.release()); + } + AmplitudeTable* clone() const override + { + std::unique_ptr ret; + clone(ret); + return ret.release(); + } + + // This is a "cache" mostly because this is a convenient place to store the data; it + // doesn't take that long to generate the lookup table. Note that existing code wants + // to work with a `const AmplitudeTable &`, thus `mutable` ... . + void cacheLookup_(std::unique_ptr&& lookup) const + { + pLookup = std::move(lookup); + } + const Amp8iPhs8iLookup_t* getLookup() const + { + return pLookup.get(); + } + + // Again, this is a convenient place to store the data as it depends on an AmplitudeTable instance. + void cacheFromComplex_(std::unique_ptr&& fromComplex) const + { + pFromComplex = std::move(fromComplex); + } + const sicd::details::ComplexToAMP8IPHS8I* getFromComplex() const + { + return pFromComplex.get(); + } + +private: + mutable std::unique_ptr pLookup; // to big for the stack + mutable std::unique_ptr pFromComplex; +}; + +} + +#endif // SIX_six_AmplitudeTable_h_INCLUDED_ diff --git a/six/modules/c++/six/include/six/Data.h b/six/modules/c++/six/include/six/Data.h index d22e212db1..5f23fe820e 100644 --- a/six/modules/c++/six/include/six/Data.h +++ b/six/modules/c++/six/include/six/Data.h @@ -80,9 +80,9 @@ struct Data */ virtual PixelType getPixelType() const = 0; virtual void setPixelType(PixelType pixelType) = 0; - virtual bool convertPixels_(std::span, std::span, ptrdiff_t /*cutoff*/) const { return false; } + virtual bool convertPixels_(std::span, std::span) const { return false; } template - bool convertPixels(std::span from, std::span to, ptrdiff_t cutoff = -1) const + bool convertPixels(std::span from, std::span to) const { // coda-oss checks to be sure T and U are trivially_copyable. While this is // correct (converting something else to bytes doesn't make sense), existing @@ -91,14 +91,14 @@ struct Data // TODO: use std::as_bytes() directly const void* const pFrom = from.data(); - auto const pFromBytes = static_cast(pFrom); - const std::span fromBytes(pFromBytes, from.size_bytes()); + auto const pFromBytes = static_cast(pFrom); + const std::span fromBytes(pFromBytes, from.size_bytes()); - void* const pTo = to.data(); - auto const pToBytes = static_cast(pTo); - const std::span toBytes(pToBytes, to.size_bytes()); + void* const pTo = to.data(); + auto const pToBytes = static_cast(pTo); + const std::span toBytes(pToBytes, to.size_bytes()); - return convertPixels_(fromBytes, toBytes, cutoff); + return convertPixels_(fromBytes, toBytes); } /*! diff --git a/six/modules/c++/six/include/six/Types.h b/six/modules/c++/six/include/six/Types.h index 2c8f8b5490..9b83b98414 100644 --- a/six/modules/c++/six/include/six/Types.h +++ b/six/modules/c++/six/include/six/Types.h @@ -19,9 +19,9 @@ * see . * */ -#ifndef __SIX_TYPES_H__ -#define __SIX_TYPES_H__ #pragma once +#ifndef SIX_six_Types_h_INCLUDED_ +#define SIX_six_Types_h_INCLUDED_ #include @@ -29,7 +29,8 @@ #include #include #include -#include +#include +#include #include #include @@ -44,6 +45,7 @@ #include "scene/Types.h" #include "scene/FrameType.h" #include "six/Enums.h" +#include "six/AmplitudeTable.h" namespace six { @@ -289,171 +291,6 @@ struct SCP } }; -/*! - * \struct LUT - * \brief Lookup table - * - * This lookup table object has a number of entries (for pixel ops, - * this is usually 256), and an output space (size) which can be anything - * (e.g., 2 for short) depending on the type that is being housed - * - */ -struct LUT -{ - std::vector table; - size_t numEntries = 0; - size_t elementSize = 0; - - //! Initialize with a number of entries and known output space - LUT(size_t entries, size_t outputSpace) : - table(entries * outputSpace), - numEntries(entries), - elementSize(outputSpace) - { - } - - //! Initialize with an existing LUT, which we copy - LUT(const unsigned char* interleavedLUT, - size_t entries, - size_t outputSpace) : - table(interleavedLUT, interleavedLUT + entries * outputSpace), - numEntries(entries), - elementSize(outputSpace) - { - } - - //! Initialize from nitf::LookupTable read from a NITF - LUT(const nitf::LookupTable& lookupTable) : LUT(lookupTable.getEntries(), lookupTable.getTables()) - { - // NITF stores the tables consecutively. - // Need to interleave them for SIX - if (elementSize == 3) - { - // Imagine the vector is a matrix and then transpose it - for (size_t ii = 0; ii < table.size(); ++ii) - { - table[(ii % numEntries) * elementSize + - (ii / numEntries)] = lookupTable.getTable()[ii]; - } - } - - // I'm not sure why this is a special case, but elements get - // swapped if we try to use the above formula - else if (elementSize == 2) - { - for (size_t ii = 0; ii < numEntries; ++ii) - { - table[2 * ii] = lookupTable.getTable()[numEntries + ii]; - table[2 * ii + 1] = lookupTable.getTable()[ii]; - } - } - } - - virtual ~LUT() = default; - - bool operator==(const LUT& rhs) const - { - return (table == rhs.table && - numEntries == rhs.numEntries && - elementSize == rhs.elementSize); - } - - //! Gives back a pointer at table[i * elementSize] - unsigned char* operator[](size_t i) - { - return &(table[i * elementSize]); - } - - //! Gives back a pointer at table[i * elementSize] - const unsigned char* operator[](size_t i) const - { - return &(table[i * elementSize]); - } - - unsigned char* getTable() - { - return table.empty() ? nullptr : table.data(); - } - - const unsigned char* getTable() const - { - - return table.empty() ? nullptr : table.data(); - } - - virtual LUT* clone() const - { - return std::make_unique(getTable(), numEntries, elementSize).release(); - } -}; - -/*! - * \struct AmplitudeTable - * \brief SICD 'AmpTable' parameter - * - * This is a fixed size (256-element) LUT. For AMP8I_PHS8I data, - * the amplitude and phase parts are stored as unsigned 8-bit integers. - * If an amplitude table is given, the amplitude component should be - * interpreted as an index into the AmpTable, ultimately yielding the - * double precision amplitude value - */ -struct AmplitudeTable final : public LUT -{ - //! Constructor. Creates a 256-entry table - AmplitudeTable(size_t elementSize) noexcept(false) : - LUT(UINT8_MAX + 1 /*i.e., 256*/, elementSize) - { - } - AmplitudeTable() noexcept(false) : AmplitudeTable(sizeof(double)) - { - } - AmplitudeTable(const nitf::LookupTable& lookupTable) noexcept(false) : LUT(lookupTable) - { - if (size() != 256) - { - throw std::invalid_argument("lookupTable should have 256 elements."); - } - } - - size_t size() const - { - return numEntries; - } - - bool operator==(const AmplitudeTable& rhs) const - { - const LUT* pThis = this; - const LUT* pRHS = &rhs; - return *(pThis) == *(pRHS); - } - bool operator!=(const AmplitudeTable& rhs) const - { - return !(*this == rhs); - } - - const double& index(size_t ii) const - { - const void* this_ii = (*this)[ii]; - return *static_cast(this_ii); - } - double& index(size_t ii) - { - void* this_ii = (*this)[ii]; - return *static_cast(this_ii); - } - - AmplitudeTable* clone() const - { - auto ret = std::make_unique(); - for (size_t ii = 0; ii < numEntries; ++ii) - { - void* ret_ii = (*ret)[ii]; - *static_cast(ret_ii) = index(ii); - } - return ret.release(); - } -}; - /*! * \struct Corners * \brief Image corners @@ -578,4 +415,4 @@ ImageMode getImageMode(RadarModeType radarMode); DECLARE_EXCEPTION(MissingRequired); } -#endif +#endif // SIX_six_Types_h_INCLUDED_ diff --git a/six/modules/c++/six/include/six/WriteControl.h b/six/modules/c++/six/include/six/WriteControl.h index e843c3c66b..26f868bddf 100644 --- a/six/modules/c++/six/include/six/WriteControl.h +++ b/six/modules/c++/six/include/six/WriteControl.h @@ -78,14 +78,6 @@ struct WriteControl */ static const char OPT_BUFFER_SIZE[]; - // Control multi-threading for AMP8I_PHS8I conversion in six::sicd::ImageData. - // A negative means no multithreading, 0 will have "the system" guess at - // an appropriate cutoff (a hardcoded value based on testing). Any other - // positive value is the number of pixels to process in each thread; it should - // be fairly large to make-up for the overhead of threading. - static const std::string AMP8I_PHS8I_CUTOFF; - static constexpr ptrdiff_t AMP8I_PHS8I_DEFAULT_CUTOFF = 0; // to_AMP8I_PHS8I() is too slow w/o multi-threading - //! Constructor. Null-sets the Container WriteControl() noexcept(false) : mLogger(mLog, mOwnLog, nullptr) diff --git a/six/modules/c++/six/six.vcxproj b/six/modules/c++/six/six.vcxproj index efa1c1ea30..96c047599c 100644 --- a/six/modules/c++/six/six.vcxproj +++ b/six/modules/c++/six/six.vcxproj @@ -104,6 +104,7 @@ + diff --git a/six/modules/c++/six/six.vcxproj.filters b/six/modules/c++/six/six.vcxproj.filters index fc65f9b6e4..39b8a3b39c 100644 --- a/six/modules/c++/six/six.vcxproj.filters +++ b/six/modules/c++/six/six.vcxproj.filters @@ -150,6 +150,9 @@ Header Files + + Header Files + diff --git a/six/modules/c++/six/source/Adapters.cpp b/six/modules/c++/six/source/Adapters.cpp index 30be34b8e0..642081ebda 100644 --- a/six/modules/c++/six/source/Adapters.cpp +++ b/six/modules/c++/six/source/Adapters.cpp @@ -197,12 +197,11 @@ struct NewMemoryWriteHandler::Impl final // This needs to persist beyhond the constructor std::vector> ampi8i_phs8i; - void convertPixels(NewMemoryWriteHandler& instance, const NITFSegmentInfo& info, std::span> buffer, const Data& data, - ptrdiff_t cutoff = -1) + void convertPixels(NewMemoryWriteHandler& instance, const NITFSegmentInfo& info, std::span> buffer, const Data& data) { ampi8i_phs8i.resize(buffer.size()); const std::span> ampi8i_phs8i_(ampi8i_phs8i.data(), ampi8i_phs8i.size()); - if (!data.convertPixels(buffer, ampi8i_phs8i_, cutoff)) + if (!data.convertPixels(buffer, ampi8i_phs8i_)) { throw std::runtime_error("Unable to convert pixels."); } @@ -246,8 +245,7 @@ inline const std::byte* cast(std::span buffer) } NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, - std::span buffer, size_t firstRow, const Data& data, bool doByteSwap, - ptrdiff_t cutoff) + std::span buffer, size_t firstRow, const Data& data, bool doByteSwap) : NewMemoryWriteHandler(info, cast(buffer), firstRow, data, doByteSwap) { validate_bandSize(buffer, info, data); @@ -261,18 +259,17 @@ NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, const void* pBuffer_ = buffer.data(); const auto pBuffer = static_cast*>(pBuffer_); const std::span> buffer_(pBuffer, buffer.size() / sizeof(std::complex)); - m_pImpl->convertPixels(*this, info, buffer_, data, cutoff); + m_pImpl->convertPixels(*this, info, buffer_, data); } } NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, - std::span> buffer, size_t firstRow, const Data& data, bool doByteSwap, - ptrdiff_t cutoff) + std::span> buffer, size_t firstRow, const Data& data, bool doByteSwap) : NewMemoryWriteHandler(info, cast(buffer), firstRow, data, doByteSwap) { if (data.getPixelType() == six::PixelType::AMP8I_PHS8I) { - m_pImpl->convertPixels(*this, info, buffer, data, cutoff); + m_pImpl->convertPixels(*this, info, buffer, data); } else if (data.getPixelType() != six::PixelType::RE32F_IM32F) { @@ -285,7 +282,7 @@ NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, } NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, - std::span> buffer, size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t) + std::span> buffer, size_t firstRow, const Data& data, bool doByteSwap) : NewMemoryWriteHandler(info, cast(buffer), firstRow, data, doByteSwap) { // This is for the uncommon case where the data is already in this format; normally, it is std::complex. @@ -297,7 +294,7 @@ NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, } NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, - std::span> buffer, size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t) + std::span> buffer, size_t firstRow, const Data& data, bool doByteSwap) : NewMemoryWriteHandler(info, cast(buffer), firstRow, data, doByteSwap) { // Each pixel is stored as a pair of numbers that represent the real and imaginary @@ -310,7 +307,7 @@ NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, validate_buffer(buffer, info, data); } NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, - std::span buffer, size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t) + std::span buffer, size_t firstRow, const Data& data, bool doByteSwap) : NewMemoryWriteHandler(info, cast(buffer), firstRow, data, doByteSwap) { switch (data.getPixelType()) @@ -326,7 +323,7 @@ NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, validate_buffer(buffer, info, data); } NewMemoryWriteHandler::NewMemoryWriteHandler(const NITFSegmentInfo& info, - std::span buffer, size_t firstRow, const Data& data, bool doByteSwap, ptrdiff_t) + std::span buffer, size_t firstRow, const Data& data, bool doByteSwap) : NewMemoryWriteHandler(info, cast(buffer), firstRow, data, doByteSwap) { if (data.getPixelType() != six::PixelType::MONO16I) diff --git a/six/modules/c++/six/source/NITFWriteControl.cpp b/six/modules/c++/six/source/NITFWriteControl.cpp index 89f3fd48a8..b4557d9423 100644 --- a/six/modules/c++/six/source/NITFWriteControl.cpp +++ b/six/modules/c++/six/source/NITFWriteControl.cpp @@ -222,23 +222,20 @@ static auto asBytes(BufferList::value_type pImageData, // this bypasses the normal NITF ImageWriter and streams directly to the output template inline std::shared_ptr makeWriteHandler(const NITFSegmentInfo& segmentInfo, - std::span imageData, const Data& data, bool doByteSwap, - ptrdiff_t cutoff) // for eventual use by to_AMP8I_PHS8I() + std::span imageData, const Data& data, bool doByteSwap) { return std::make_shared(segmentInfo, - imageData, segmentInfo.getFirstRow(), data, doByteSwap, - cutoff); + imageData, segmentInfo.getFirstRow(), data, doByteSwap); } inline std::shared_ptr makeWriteHandler(const NITFSegmentInfo& segmentInfo, - BufferList::value_type pImageData, const Data& data, bool doByteSwap, - ptrdiff_t cutoff) // for eventual use by to_AMP8I_PHS8I() + BufferList::value_type pImageData, const Data& data, bool doByteSwap) { const auto pImageData_ = asBytes(pImageData, segmentInfo, data); - return makeWriteHandler(segmentInfo, pImageData_, data, doByteSwap, cutoff); + return makeWriteHandler(segmentInfo, pImageData_, data, doByteSwap); } inline std::shared_ptr makeWriteHandler(NITFSegmentInfo segmentInfo, -io::InputStream* imageData, const Data& data, bool doByteSwap, ptrdiff_t) +io::InputStream* imageData, const Data& data, bool doByteSwap) { //! TODO: This section of code (unlike the memory section above) // does not account for blocked writing or J2K compression. @@ -248,12 +245,11 @@ return std::make_shared(segmentInfo, imageData, data, doByte template void writeWithoutNitro(nitf::Writer& mWriter, const TImageData& imageData, - const std::vector& imageSegments, size_t startIndex, const Data& data, bool doByteSwap, - ptrdiff_t cutoff) // for eventual use by to_AMP8I_PHS8I() + const std::vector& imageSegments, size_t startIndex, const Data& data, bool doByteSwap) { for (size_t j = 0; j < imageSegments.size(); ++j) { - auto writeHandler = makeWriteHandler(imageSegments[j], imageData, data, doByteSwap, cutoff); + auto writeHandler = makeWriteHandler(imageSegments[j], imageData, data, doByteSwap); mWriter.setImageWriteHandler(static_cast(startIndex + j), writeHandler); } } @@ -273,13 +269,6 @@ bool NITFWriteControl::do_prepareIO(size_t imageDataSize, nitf::IOInterface& out return shouldByteSwap(); } -ptrdiff_t NITFWriteControl::AMP8I_PHS8I_cutoff() const -{ - static const Parameter default_cutoff_parameter(WriteControl::AMP8I_PHS8I_DEFAULT_CUTOFF); - const ptrdiff_t cutoff = getOptions().getParameter(WriteControl::AMP8I_PHS8I_CUTOFF, default_cutoff_parameter); - return cutoff; -} - void NITFWriteControl::save(const SourceList& imageData, nitf::IOInterface& outputFile, const std::vector& schemaPaths) @@ -297,7 +286,7 @@ void NITFWriteControl::save(const SourceList& imageData, const auto startIndex = info.getStartIndex(); const six::Data* const pData = info.getData(); - writeWithoutNitro(mWriter, imageData[i], imageSegments, startIndex, *pData, doByteSwap, AMP8I_PHS8I_cutoff()); + writeWithoutNitro(mWriter, imageData[i], imageSegments, startIndex, *pData, doByteSwap); } addDataAndWrite(schemaPaths); @@ -457,7 +446,7 @@ void NITFWriteControl::write_imageData(const T& imageData, const NITFImageInfo& } else { - writeWithoutNitro(mWriter, imageData, imageSegments, startIndex, *pData, doByteSwap, AMP8I_PHS8I_cutoff()); + writeWithoutNitro(mWriter, imageData, imageSegments, startIndex, *pData, doByteSwap); } if (legend) diff --git a/six/modules/c++/six/source/WriteControl.cpp b/six/modules/c++/six/source/WriteControl.cpp index 99b623ad49..05221b0988 100644 --- a/six/modules/c++/six/source/WriteControl.cpp +++ b/six/modules/c++/six/source/WriteControl.cpp @@ -23,4 +23,4 @@ const char six::WriteControl::OPT_BYTE_SWAP[] = "ByteSwap"; const char six::WriteControl::OPT_BUFFER_SIZE[] = "BufferSize"; -const std::string six::WriteControl::AMP8I_PHS8I_CUTOFF("AMP8I_PHS8I_cutoff"); +