25 if (name ==
"HCOMPRESS")
38 return "GZIP_SHUFFLE";
45 os <<
"Unrecognized compression algorithm: " << algorithm;
64 "Unsupported compression algorithm: HCOMPRESS_1");
67 os <<
"Unrecognized cfitsio compression: " << cfitsio;
86 os <<
"Unrecognized compression algorithm: " << algorithm;
92 int rows,
float quantizeLevel_)
93 :
algorithm(algorithm_), tiles(
ndarray::allocate(MAX_COMPRESS_DIM)), quantizeLevel(quantizeLevel_) {
96 for (
int ii = 2; ii < MAX_COMPRESS_DIM; ++ii)
tiles[ii] = 1;
116 return "STDEV_POSITIVE";
118 return "STDEV_NEGATIVE";
125 os <<
"Unrecognized scaling algorithm: " <<
algorithm;
132 float quantizeLevel_,
float quantizePad_,
bool fuzz_,
double bscale_,
137 seed(
std::
abs(seed_ - 1) % (N_RANDOM - 1) +
139 maskPlanes(maskPlanes_),
141 quantizePad(quantizePad_),
148 template <
typename T,
int N>
150 ndarray::Array<bool, N, N>
const&
mask) {
152 auto const& flatMask = ndarray::flatten<1>(
mask);
153 for (
auto mm = flatMask.begin(); mm != flatMask.end(); ++mm) {
156 ndarray::Array<T, 1, 1> array = ndarray::allocate(num);
157 auto const& flatImage = ndarray::flatten<1>(
image);
158 auto mm = ndarray::flatten<1>(
mask).begin();
159 auto aa = array.begin();
160 for (
auto ii = flatImage.begin(); ii != flatImage.end(); ++ii, ++mm) {
167 auto const q1 = num / 4;
168 auto const q2 = num / 2;
169 auto const q3 = q1 + q2;
174 T
const median = num % 2 ? array[num / 2] : 0.5 * (array[num / 2] + array[num / 2 - 1]);
177 T
const lq = array[q1];
178 T
const uq = array[q3];
183 template <
typename T,
int N>
184 std::pair<T, T> calculateMinMax(ndarray::Array<T const, N, N>
const& image,
185 ndarray::Array<bool, N, N>
const& mask) {
187 auto mm = ndarray::flatten<1>(
mask).begin();
188 auto const& flatImage = ndarray::flatten<1>(
image);
189 for (
auto ii = flatImage.begin(); ii != flatImage.end(); ++ii, ++mm) {
193 if (*ii < min) min = *ii;
199 template <
typename T>
200 double rangeForBitpix(
int bitpix,
bool cfitsioPadding) {
204 double range =
std::pow(2.0, bitpix) - 1;
205 if (cfitsioPadding) {
213 template <
typename T,
int N>
214 ImageScale ImageScalingOptions::determineFromRange(ndarray::Array<T const, N, N>
const&
image,
215 ndarray::Array<bool, N, N>
const&
mask,
bool isUnsigned,
216 bool cfitsioPadding)
const {
217 auto minMax = calculateMinMax(image, mask);
218 T
const min = minMax.first;
219 T
const max = minMax.second;
221 double range = rangeForBitpix<T>(
bitpix, cfitsioPadding);
223 double const bscale =
static_cast<T
>((max -
min) / range);
224 double bzero =
static_cast<T
>(isUnsigned ?
min : min + 0.5 * range *
bscale);
225 if (cfitsioPadding) {
232 template <
typename T,
int N>
233 ImageScale ImageScalingOptions::determineFromStdev(ndarray::Array<T const, N, N>
const& image,
234 ndarray::Array<bool, N, N>
const& mask,
bool isUnsigned,
235 bool cfitsioPadding)
const {
236 auto stats = calculateMedianStdev(image, mask);
237 auto const median = stats.first,
stdev = stats.second;
241 auto minMax = calculateMinMax(image, mask);
242 T
const min = minMax.first;
243 T
const max = minMax.second;
244 double range = rangeForBitpix<T>(
bitpix, cfitsioPadding);
245 double const numUnique = (max -
min) / bscale;
249 if (numUnique < range) {
257 diskVal = (bitpix == 8) ? 0 : -(1L << (bitpix - 1));
263 diskVal = (bitpix == 8) ? 255 : (1L << (bitpix - 1)) - 1;
276 double bzero =
static_cast<T
>(imageVal - bscale * diskVal);
281 template <
typename T,
class Enable =
void>
283 static double constexpr value = 0.0;
291 static double constexpr value = 0.0;
295 template <
typename T>
296 struct Bzero<T, typename
std::enable_if<std::numeric_limits<T>::is_integer &&
297 !std::numeric_limits<T>::is_signed>
::type> {
301 #ifndef DOXYGEN // suppress a bogus Doxygen complaint about an documented symbol 302 template <
typename T,
int N>
304 ndarray::Array<bool, N, N>
const& mask)
const {
308 "Image scaling not supported for integral types");
316 return determineFromRange(image, mask, isUnsigned, cfitsioPadding);
322 return determineFromStdev(image, mask, isUnsigned, cfitsioPadding);
335 class CfitsioRandom {
338 CfitsioRandom(
int seed) : _seed(seed) {
339 assert(seed != 0 && seed < N_RANDOM);
345 void resetForTile(
int iTile) {
346 _start = (iTile + _seed - 1) % N_RANDOM;
360 if (_index == N_RANDOM) {
362 if (_start == N_RANDOM) {
370 template <
typename T>
371 ndarray::Array<T, 1, 1> forImage(
typename ndarray::Array<T const, 2, 2>::Index
const& shape,
372 ndarray::Array<long, 1>
const& tiles) {
373 std::size_t const xSize = shape[1], ySize = shape[0];
374 ndarray::Array<T, 1, 1> out = ndarray::allocate(xSize * ySize);
375 std::size_t const xTileSize = tiles[0] <= 0 ? xSize : tiles[0];
376 std::size_t const yTileSize = tiles[1] < 0 ? ySize : (tiles[1] == 0 ? 1 : tiles[1]);
377 int const xNumTiles =
std::ceil(xSize / static_cast<float>(xTileSize));
378 int const yNumTiles =
std::ceil(ySize / static_cast<float>(yTileSize));
379 for (
int iTile = 0, yTile = 0; yTile < yNumTiles; ++yTile) {
380 int const yStart = yTile * yTileSize;
381 int const yStop =
std::min(yStart + yTileSize, ySize);
382 for (
int xTile = 0; xTile < xNumTiles; ++xTile, ++iTile) {
383 int const xStart = xTile * xTileSize;
384 int const xStop =
std::min(xStart + xTileSize, xSize);
386 for (
int y = yStart;
y < yStop; ++
y) {
387 auto iter = out.begin() +
y * xSize + xStart;
388 for (
int x = xStart;
x < xStop; ++
x, ++iter) {
389 *iter =
static_cast<T
>(getNext());
399 void reseed() { _index =
static_cast<int>(
fits_rand_value[_start] * 500); }
408 template <
typename T>
410 bool forceNonfiniteRemoval,
bool fuzz,
411 ndarray::Array<long, 1>
const& tiles,
416 "Floating-point images may not be converted to different floating-point types");
420 "Scaling may not be applied to floating-point images");
426 if (!forceNonfiniteRemoval) {
431 ndarray::Array<T, 1, 1> out = ndarray::allocate(image.getNumElements());
432 auto outIter = out.begin();
433 auto const& flatImage = ndarray::flatten<1>(
image);
434 for (
auto inIter = flatImage.begin(); inIter != flatImage.end(); ++inIter, ++outIter) {
454 ndarray::Array<double, 1, 1> out;
456 if (tiles.isEmpty()) {
458 "Tile sizes must be provided if fuzzing is desired");
460 out = CfitsioRandom(seed).forImage<
double>(image.getShape(), tiles);
462 out = ndarray::allocate(num);
465 auto outIter = out.begin();
466 auto const& flatImage = ndarray::flatten<1>(
image);
467 for (
auto inIter = flatImage.begin(); inIter != flatImage.end(); ++inIter, ++outIter) {
468 double value = (*inIter -
bzero) * scale;
480 *outIter = (value < min ? blank : (value > max ? blank :
std::floor(value)));
485 template <
typename T>
487 ndarray::Array<T, 2, 2> memory = ndarray::allocate(image.getShape());
493 #define INSTANTIATE(TYPE) \ 494 template ImageScale ImageScalingOptions::determine<TYPE, 2>( \ 495 ndarray::Array<TYPE const, 2, 2> const& image, ndarray::Array<bool, 2, 2> const& mask) const; \ 496 template std::shared_ptr<detail::PixelArrayBase> ImageScale::toFits<TYPE>( \ 497 ndarray::Array<TYPE const, 2, 2> const&, bool, bool, ndarray::Array<long, 1> const&, int) const; \ 498 template ndarray::Array<TYPE, 2, 2> ImageScale::fromFits<TYPE>(ndarray::Array<TYPE, 2, 2> const&) const; int const N_RESERVED_VALUES
ImageScalingOptions()
Default Ctor.
Angle abs(Angle const &a)
int bitpix
Bits per pixel (0, 8,16,32,64,-32,-64)
std::shared_ptr< PixelArrayBase > makePixelArray(int bitpix, ndarray::Array< T, 1, 1 > const &array)
Create a PixelArray suitable for an image with the nominated BITPIX.
Scale to preserve dynamic range.
int compressionAlgorithmToCfitsio(ImageCompressionOptions::CompressionAlgorithm algorithm)
Convert ImageCompressionOptions::CompressionAlgorithm to cfitsio.
def scale(algorithm, min, max=None, frame=None)
#define INSTANTIATE(TYPE)
CompressionAlgorithm
Compression algorithms.
FITS BITPIX header value by C++ type.
int seed
Seed for random number generator when fuzzing.
Tiles tiles
Tile size; a dimension with 0 means infinite (e.g., to specify one row: 0,1)
ImageCompressionOptions(CompressionAlgorithm algorithm_, Tiles tiles_, float quantizeLevel_=0.0)
Custom compression.
A base class for image defects.
float quantizeLevel
Divisor of the standard deviation for STDEV_* scaling.
ndarray::Array< T, 2, 2 > fromFits(ndarray::Array< T, 2, 2 > const &image) const
Convert to an array.
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromString(std::string const &name)
Interpret compression algorithm expressed in string.
double bscale
Manually specified BSCALE (for MANUAL scaling)
std::string scalingAlgorithmToString(ImageScalingOptions::ScalingAlgorithm algorithm)
Provide string version of compression algorithm.
Scaling zero-point, set according to pixel type.
std::string compressionAlgorithmToString(ImageCompressionOptions::CompressionAlgorithm algorithm)
Provide string version of compression algorithm.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Standard GZIP compression.
float quantizeLevel
quantization level: 0.0 = none requires use of GZIP or GZIP_SHUFFLE
Scale based on the standard deviation, dynamic range negative.
double bzero
Manually specified BZERO (for MANUAL scaling)
Reports invalid arguments.
CompressionAlgorithm algorithm
Compresion algorithm to use.
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromCfitsio(int cfitsio)
Convert compression algorithm from cfitsio to ImageCompressionOptions::CompressionAlgorithm.
ImageScale determine(image::ImageBase< T > const &image, std::shared_ptr< image::Mask< image::MaskPixel > const > mask=nullptr) const
Determine the scaling for a particular image.
Scale based on the standard deviation, dynamic range positive+negative.
float quantizePad
Number of stdev to allow on the low/high side (for STDEV_POSITIVE/NEGATIVE)
bool fuzz
Fuzz the values when quantising floating-point values?
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
Scale based on the standard deviation. dynamic range positive.
ImageScalingOptions::ScalingAlgorithm scalingAlgorithmFromString(std::string const &name)
Interpret scaling algorithm expressed in string.
GZIP compression with shuffle (most-significant byte first)
std::shared_ptr< detail::PixelArrayBase > toFits(ndarray::Array< T const, 2, 2 > const &image, bool forceNonfiniteRemoval, bool fuzz=true, ndarray::Array< long, 1 > const &tiles=ndarray::Array< long, 1, 1 >(), int seed=1) const
Convert to an array of pixel values to write to FITS.