All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nom.tam.fits.compression.algorithm.quant.QuantizeOption Maven / Gradle / Ivy

Go to download

Java library for reading and writing FITS files. FITS, the Flexible Image Transport System, is the format commonly used in the archiving and transport of astronomical data.

There is a newer version: 1.21.0
Show newest version
package nom.tam.fits.compression.algorithm.quant;

import nom.tam.fits.compression.algorithm.api.ICompressOption;
import nom.tam.fits.compression.provider.param.api.ICompressParameters;
import nom.tam.fits.compression.provider.param.base.BundledParameters;
import nom.tam.fits.compression.provider.param.quant.QuantizeParameters;

/*
 * #%L
 * nom.tam FITS library
 * %%
 * Copyright (C) 1996 - 2024 nom-tam-fits
 * %%
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 *
 * 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 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.
 * #L%
 */

/**
 * Quantization options when they are part of the compression scheme. When compressing tables and images includes
 * quantization (integer representation of floating point data), users can control how exactly the quantization should
 * be performed. When reading compressed FITS files, these options will be set automatically based on the header values
 * recorded in the compressed HDU.
 * 
 * @see nom.tam.image.compression.hdu.CompressedImageHDU#setQuantAlgorithm(String)
 * @see nom.tam.image.compression.hdu.CompressedImageHDU#getCompressOption(Class)
 */
public class QuantizeOption implements ICompressOption {

    /**
     * and including NULL_VALUE. These values may not be used to represent the quantized and scaled floating point pixel
     * values If lossy Hcompression is used, and the tiledImageOperation contains null values, then it is also possible
     * for the compressed values to slightly exceed the range of the actual (lossless) values so we must reserve a
     * little more space value used to represent undefined pixels
     */
    private static final int NULL_VALUE = Integer.MIN_VALUE + 1;

    /** Shared configuration across copies */
    private Config config;

    /** The parameters that represent settings for this option in the FITS headers and/or compressed data columns */
    protected QuantizeParameters parameters;

    private ICompressOption compressOption;

    private double bScale = Double.NaN;

    private double bZero = Double.NaN;

    private double nullValue = Double.NaN;

    private Integer nullValueIndicator;

    private boolean checkNull;

    private int intMaxValue;

    private int intMinValue;

    private double maxValue;

    private double minValue;

    private int tileIndex = 0;

    private int tileHeight;

    private int tileWidth;

    QuantizeOption() {
        this(null);
    }

    /**
     * Creates a new set of quantization options, to be used together with the specified compression options.
     *
     * @param compressOption Compression-specific options to pair with these quantization options, or null.
     *
     * @since                1.18
     */
    public QuantizeOption(ICompressOption compressOption) {
        parameters = new QuantizeParameters(this);
        config = new Config();
        this.compressOption = compressOption;
    }

    @Override
    public QuantizeOption copy() {
        try {
            QuantizeOption copy = (QuantizeOption) clone();
            if (compressOption != null) {
                copy.compressOption = compressOption.copy();
            }
            copy.parameters = parameters.copy(copy);
            return copy;
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException("option could not be cloned", e);
        }
    }

    /**
     * Returns the integer value that represents missing (null) data in the quantized representation.
     * 
     * @return the integer blanking value (null value).
     * 
     * @see    #setBNull(Integer)
     */
    public Integer getBNull() {
        return nullValueIndicator;
    }

    /**
     * Returns the quantization level.
     * 
     * @return the floating-point difference between integer levels in the quantized data.
     * 
     * @see    #setBScale(double)
     * @see    #getBZero()
     */
    public double getBScale() {
        return bScale;
    }

    /**
     * Returns the quantization offset.
     * 
     * @return the floating-point value corresponding to the integer level 0.
     * 
     * @see    #setBZero(double)
     * @see    #getBScale()
     */
    public double getBZero() {
        return bZero;
    }

    @Override
    public ICompressParameters getCompressionParameters() {
        if (compressOption == null) {
            return parameters;
        }
        return new BundledParameters(parameters, compressOption.getCompressionParameters());
    }

    /**
     * Returns the compression or quantization options, recast for the selected option class.
     * 
     * @param     the generic type of the compression option
     * @param  clazz the option class for the compression algorithm used with the quantization, or
     *                   QunatizeOption.class for our own options.
     * 
     * @return       the recast options for the requested class or null id we do not have access to options
     *                   of the requested class.
     * 
     * @see          #getCompressOption()
     */
    public  T getCompressOption(Class clazz) {
        return unwrap(clazz);
    }

    /**
     * Returns the options for the compression algorithm that accompanies quantization.
     * 
     * @return the options for the compression algorithm, or null
     * 
     * @see    #getCompressOption(Class)
     */
    public final ICompressOption getCompressOption() {
        return compressOption;
    }

    /**
     * Returns the maximum integer level in the quantized representation.
     * 
     * @return the maximum integer level in the quantized data.
     * 
     * @see    #getMaxValue()
     * @see    #getIntMinValue()
     */
    public int getIntMaxValue() {
        return intMaxValue;
    }

    /**
     * Returns the maximum integer level in the quantized representation.
     * 
     * @return the maximum integer level in the quantized data.
     * 
     * @see    #getMinValue()
     * @see    #getIntMinValue()
     */
    public int getIntMinValue() {
        return intMinValue;
    }

    /**
     * Returns the maximum floating-point value in the data
     * 
     * @return the maximum floating-point value in the data before quantization.
     * 
     * @see    #getIntMaxValue()
     * @see    #getMinValue()
     */
    public double getMaxValue() {
        return maxValue;
    }

    /**
     * Returns the minimum floating-point value in the data
     * 
     * @return the minimum floating-point value in the data before quantization.
     * 
     * @see    #getIntMinValue()
     * @see    #getMaxValue()
     */
    public double getMinValue() {
        return minValue;
    }

    /**
     * Returns the floating-point value that indicates a null datum in the image before quantization is
     * applied. Normally, the FITS standard is that NaN values indicate null values in floating-point
     * images. While this class allows using other values also, they are not recommended since they are not supported by
     * FITS in a standard way.
     * 
     * @return the floating-point value that represents a null value (missing data) in the image before
     *             quantization.
     * 
     * @see    #setNullValue(double)
     * @see    #getNullValueIndicator()
     * @see    #isCheckNull()
     */
    public double getNullValue() {
        return nullValue;
    }

    /**
     * @deprecated use {@link #getBNull()} instead (duplicate method). Returns the integer value that represents missing
     *                 data (null) in the quantized representation.
     * 
     * @return     the integer blanking value (null value).
     * 
     * @see        #setBNull(Integer)
     */
    public final Integer getNullValueIndicator() {
        return getBNull();
    }

    /**
     * Returns the quantization resolution level used for automatic qunatization. For Gaussian noise the quantization
     * level is the standard deviation of the noise divided by this Q value. Thus Q values of a few will ensure that
     * quantization retains just about all of the information in the noisy data.
     * 
     * @return The current Q value, defined as the number of quantized levels per standard deviation (for Gaussian
     *             noise).
     * 
     * @see    #setQlevel(double)
     * @see    #getBScale()
     */
    public double getQLevel() {
        return config.qlevel;
    }

    /**
     * Gets the random seed value used for dithering
     * 
     * @return the random seed value used for dithering
     * 
     * @see    #setSeed(long)
     * @see    RandomSequence
     */
    public long getSeed() {
        return config.seed;
    }

    /**
     * Returns the sequential tile index that this option is currently configured for.
     * 
     * @return the sequential tile index that the quantization is configured for
     * 
     * @see    #setTileIndex(int)
     */
    public long getTileIndex() {
        return tileIndex;
    }

    /**
     * Returns the tile height
     * 
     * @return the tile height in pixels
     * 
     * @see    #setTileHeight(int)
     * @see    #getTileWidth()
     */
    @Override
    public int getTileHeight() {
        return tileHeight;
    }

    /**
     * Returns the tile width
     * 
     * @return the tile width in pixels
     * 
     * @see    #setTileWidth(int)
     * @see    #getTileHeight()
     */
    @Override
    public int getTileWidth() {
        return tileWidth;
    }

    /**
     * Checks whether we force the integer quantized level 0 to correspond to a floating-point level 0.0, when using
     * automatic quantization.
     * 
     * @return true if we want to keep `BZERO` at 0 when quantizing automatically.
     * 
     * @see    #setCenterOnZero(boolean)
     */
    public boolean isCenterOnZero() {
        return config.centerOnZero;
    }

    /**
     * Whether the floating-point data may contain null values (normally NaNs).
     * 
     * @return true if we should expect null in the floating-point data. This is automatically
     *             true if {@link #setBNull(Integer)} was called with a non-null value.
     * 
     * @see    #setBNull(Integer)
     */
    public boolean isCheckNull() {
        return checkNull;
    }

    /**
     * Whether automatic quantization treats 0.0 as a special value. Normally values within the `BSCALE` quantization
     * level around 0.0 will be assigned the same integer quanta, and will become indistinguishable in the quantized
     * data. Some software may, in their misguided ways, assign exact zero values a special meaning (such as no data) in
     * which case we may want to distinguish these as we apply quantization. However, it is generally not a good idea to
     * use 0 as a special value.
     * 
     * @return true to treat 0.0 (exact) as a special value, or false to treat is as any other
     *             measured value (recommended).
     * 
     * @see    #setCheckZero(boolean)
     * @see    #getBScale()
     */
    public boolean isCheckZero() {
        return config.checkZero;
    }

    /**
     * Whether dithering is enabled
     * 
     * @return true if dithering is enabled, or else false
     * 
     * @see    #setDither(boolean)
     * @see    #isDither2()
     */
    public boolean isDither() {
        return config.dither;
    }

    /**
     * Whether dither method 2 is used.
     * 
     * @return true if dither method 2 is used, or else false
     * 
     * @see    #setDither2(boolean)
     * @see    #isDither()
     */
    public boolean isDither2() {
        return config.dither2;
    }

    @Override
    public boolean isLossyCompression() {
        return true;
    }

    /**
     * Sets the integer value that represents missing data (null) in the quantized representation.
     * 
     * @param  blank the new integer blanking value (that is one that denotes a missing or null datum).
     *                   Setting this option to null disables the treatment of issing or null
     *                   data.
     * 
     * @return       itself
     * 
     * @see          #getBNull()
     * @see          #isCheckNull()
     */
    public ICompressOption setBNull(Integer blank) {
        if (blank != null) {
            nullValueIndicator = blank;
            checkNull = true;
        } else {
            checkNull = false;
        }
        return this;
    }

    /**
     * Sets the quantization level.
     * 
     * @param  value the new floating-point difference between integer levels in the quantized data.
     * 
     * @return       itself
     * 
     * @see          #setQlevel(double)
     * @see          #setBZero(double)
     * @see          #getBScale()
     */
    public QuantizeOption setBScale(double value) {
        bScale = value;
        return this;
    }

    /**
     * Sets the quantization offset.
     * 
     * @param  value the new floating-point value corresponding to the integer level 0.
     * 
     * @return       itself
     * 
     * @see          #setBScale(double)
     * @see          #getBZero()
     */
    public QuantizeOption setBZero(double value) {
        bZero = value;
        return this;
    }

    /**
     * Enabled or disables keeping `BZERO` at 0 when using automatic quantization.
     * 
     * @param  value true to keep `BZERO` at 0 when quantizing automatically, that is keep the integer
     *                   quantized level 0 correspond to floating-point level 0.0. Or, false to let the
     *                   automatic quantization algorithm determine the optimal quantization offset.
     * 
     * @return       iftself
     * 
     * @see          #isCenterOnZero()
     */
    public QuantizeOption setCenterOnZero(boolean value) {
        config.centerOnZero = value;
        return this;
    }

    /**
     * @deprecated       {@link #setBNull(Integer)} controls this feature automatically as needed. Sets whether we
     *                       should expect the floating-point data to contain null values (normally NaNs).
     * 
     * @param      value true if the floating-point data may contain null values.
     * 
     * @return           itself
     * 
     * @see              #setCheckNull(boolean)
     * @see              #setBNull(Integer)
     * @see              #getNullValue()
     */
    public QuantizeOption setCheckNull(boolean value) {
        checkNull = value;
        if (nullValueIndicator == null) {
            nullValueIndicator = NULL_VALUE;
        }
        return this;
    }

    /**
     * Sets whether automatic quantization is to treat 0.0 as a special value. Normally values within the `BSCALE`
     * quantization level around 0.0 will be assigned the same integer quanta, and will become indistinguishable in the
     * quantized data. However some software may assign exact zero values a special meaning (such as no data) in which
     * case we may want to distinguish these as we apply qunatization. However, it is generally not a good idea to use 0
     * as a special value. To mark missing data, the FITS standard recognises only NaN as a special value -- while all
     * other values should constitute valid measurements.
     * 
     * @deprecated       It is strongly discouraged to treat 0.0 values as special. FITS only recognises NaN as a
     *                       special floating-point value marking missing data. All other floating point values are
     *                       considered valid measurements.
     * 
     * @param      value whether to treat values around 0.0 as special.
     * 
     * @return           itself
     * 
     * @see              #isCheckZero()
     */
    public QuantizeOption setCheckZero(boolean value) {
        config.checkZero = value;
        return this;
    }

    /**
     * Enables or disables dithering.
     * 
     * @param  value true to enable dithering, or else false to disable
     * 
     * @return       itself
     * 
     * @see          #isDither()
     * @see          #setDither2(boolean)
     */
    public QuantizeOption setDither(boolean value) {
        config.dither = value;
        return this;
    }

    /**
     * Sets whether dithering is to use method 2.
     * 
     * @param  value true to use dither method 2, or else false for method 1.
     * 
     * @return       itself
     * 
     * @see          #isDither2()
     * @see          #setDither(boolean)
     */
    public QuantizeOption setDither2(boolean value) {
        config.dither2 = value;
        return this;
    }

    /**
     * Sets the maximum integer level in the quantized representation.
     * 
     * @param  value the new maximum integer level in the quantized data.
     * 
     * @return       itself
     * 
     * @see          #getIntMaxValue()
     * @see          #setIntMinValue(int)
     */
    public QuantizeOption setIntMaxValue(int value) {
        intMaxValue = value;
        return this;
    }

    /**
     * Sets the minimum integer level in the quantized representation.
     * 
     * @param  value the new minimum integer level in the quantized data.
     * 
     * @return       itself
     * 
     * @see          #getIntMinValue()
     * @see          #setIntMaxValue(int)
     */
    public QuantizeOption setIntMinValue(int value) {
        intMinValue = value;
        return this;
    }

    /**
     * Sets the maximum floating-point value in the data
     * 
     * @param  value the maximum floating-point value in the data before quantization.
     * 
     * @return       itself
     * 
     * @see          #getMaxValue()
     * @see          #setMinValue(double)
     */
    public QuantizeOption setMaxValue(double value) {
        maxValue = value;
        return this;
    }

    /**
     * Sets the minimum floating-point value in the data
     * 
     * @param  value the mininum floating-point value in the data before quantization.
     * 
     * @return       itself
     * 
     * @see          #getMinValue()
     * @see          #setMaxValue(double)
     */
    public QuantizeOption setMinValue(double value) {
        minValue = value;
        return this;
    }

    /**
     * @deprecated       The use of null values other than NaN for floating-point data types is not
     *                       standard in FITS. You should therefore avoid using this method to change it. Returns the
     *                       floating-point value that indicates a null datum in the image before
     *                       quantization is applied. Normally, the FITS standard is that NaN values indicate
     *                       null values in floating-point images. While this class allows using other
     *                       values also, they are not recommended since they are not supported by FITS in a standard
     *                       way.
     * 
     * @param      value the new floating-point value that represents a null value (missing data) in the
     *                       image before quantization.
     * 
     * @return           itself
     * 
     * @see              #setNullValue(double)
     */
    public QuantizeOption setNullValue(double value) {
        nullValue = value;
        return this;
    }

    @Override
    public void setParameters(ICompressParameters parameters) {
        if (parameters instanceof QuantizeParameters) {
            this.parameters = (QuantizeParameters) parameters.copy(this);
        } else if (parameters instanceof BundledParameters) {
            BundledParameters bundle = (BundledParameters) parameters;
            for (int i = 0; i < bundle.size(); i++) {
                setParameters(bundle.get(i));
            }
        } else if (compressOption != null) {
            compressOption.setParameters(parameters);
        }
    }

    /**
     * Sets the quantization resolution level to use for automatic quantization. For Gaussian noise the quantization
     * level is the standard deviation of the noise divided by this Q value. Thus Q values of a few will ensusre that
     * quantization retains just about all of the information contained in the noisy data.
     * 
     * @param  value The new Q value, defined as the number of quantized levels per standard deviation (for Gaussian
     *                   noise).
     * 
     * @return       itself
     * 
     * @see          #getQLevel()
     * @see          #setBScale(double)
     */
    public QuantizeOption setQlevel(double value) {
        config.qlevel = value;
        return this;
    }

    /**
     * Sets the seed value for the dither random generator
     *
     * @param  value The seed value, as in ZDITHER0, normally a number between 1 and 10000 (inclusive).
     *
     * @return       itself
     *
     * @see          #setTileIndex(int)
     */
    public QuantizeOption setSeed(long value) {
        config.seed = value;
        return this;
    }

    /**
     * Sets the tile index for which to initialize the random number generator with the given seed (i.e.
     * ZDITHER0 value).
     *
     * @param  index The 0-based tile index
     *
     * @return       itself
     *
     * @see          #setSeed(long)
     */
    public QuantizeOption setTileIndex(int index) {
        tileIndex = index;
        return this;
    }

    @Override
    public QuantizeOption setTileHeight(int value) {
        tileHeight = value;
        if (compressOption != null) {
            compressOption.setTileHeight(value);
        }
        return this;
    }

    @Override
    public QuantizeOption setTileWidth(int value) {
        tileWidth = value;
        if (compressOption != null) {
            compressOption.setTileWidth(value);
        }
        return this;
    }

    @Override
    public  T unwrap(Class clazz) {
        if (clazz.isAssignableFrom(this.getClass())) {
            return clazz.cast(this);
        }
        if (compressOption != null) {
            if (clazz.isAssignableFrom(compressOption.getClass())) {
                return clazz.cast(compressOption);
            }
        }
        return null;
    }

    /**
     * Stores configuration in a way that can be shared and modified across enclosing option copies.
     * 
     * @author Attila Kovacs
     *
     * @since  1.18
     */
    private static final class Config {

        private boolean centerOnZero;

        private boolean checkZero;

        private boolean dither;

        private boolean dither2;

        private double qlevel = Double.NaN;

        private long seed = 1L;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy