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

nom.tam.util.Quantizer Maven / Gradle / Ivy

package nom.tam.util;

/*-
 * #%L
 * nom.tam.fits
 * %%
 * 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%
 */

import nom.tam.fits.Header;
import nom.tam.fits.HeaderCard;
import nom.tam.fits.header.Standard;

/**
 * 

* Quantizes floating point values as integers. FITS allows representing * floating-point values as integers, e.g. to allow more compact storage at some * tolerable level of precision loss. For example, you may store floating-point * values (4 bytes) discretized into 64k levels as 16-bit integers. The * conversion involves a linear transformation: *

* *
 *   {float-value}= {scaling} * {int-value} + {offset}
 * 
*

* and the inverse transformation: *

* *
 *   {int-value} = round(({float-value} - {offset}) / {scaling})
 * 
*

* The latter floating-point to integer conversion naturally results in some * loss of precision, comparable to the level of the scaling factor, i.e. the * peration of discrete levels at which information is preserved. *

*

* In addition to the scaling conversion, FITS also allows designating an * integer blanking value to indicate missing or invalid data, which is mapped * to NaN in the floating point representation. *

*

* Fits allows for quantized representations of floating-point data both in * image HDUs and for columns in binary table HDUs. The quantization parameters * are stored differently for the two types of HDUs, using the BSCALE, BZERO, * and BLANK keywords for images, and the TSCALn, TZEROn, and TNULLn keywords * for individual columns in a table. *

* * @author Attila Kovacs * @since 1.20 */ public class Quantizer { private Long blankingValue; private double scale = 1.0; private double offset; /** * Constructs a new decimal/integer conversion rule. * * @param scale * The scaling value, that is the spacing of the qunatized levels * @param offset * The floating-point value that corresponds to an integer value * of 0 (zero). * @param blankingValue * The value to use to represent NaN values in the integer * representation, that is missing or invalid data. */ public Quantizer(double scale, double offset, int blankingValue) { this(scale, offset, (long) blankingValue); } /** * Constructs a new decimal/integer conversion rule. * * @param scale * The scaling value, that is the spacing of the qunatized levels * @param offset * The floating-point value that corresponds to an integer value * of 0 (zero). * @param blankingValue * The value to use to represent NaN values in the integer * representation, that is missing or invalid data. It may be * null if the floating-point data is not expected * to contain NaN values ever. */ public Quantizer(double scale, double offset, Long blankingValue) { this.scale = scale; this.offset = offset; this.blankingValue = blankingValue; } /** * Converts a floating point value to its integer representation using the * quantization. * * @param value * the floating point value * @return the corresponding qunatized integer value * @see #toDouble(long) */ public long toLong(double value) { if (!Double.isFinite(value)) { if (blankingValue == null) { throw new IllegalStateException("No blanking value was defined."); } return blankingValue; } return Math.round((value - offset) / scale); } /** * Converts an integer value to the floating-point value it represents under * the qunatization. * * @param value * the integer value * @return the corresponding floating-point value, which may be NaN. * @see #toLong(double) */ public double toDouble(long value) { if (blankingValue != null && value == blankingValue) { return Double.NaN; } return scale * value + offset; } /** * Checks if the quantization is the same as the default quantization. For * example, maybe we don't need to (want to) write the quantization keywords * into the FITS headers if these are irrelevant and/or not meaningful. So * this method might help us decide when quantization is necessary / * meaningful vs when it is irrelevant. * * @return true if the scaling is 1.0, the offset 0.0, and the * blanking value is null. Otherwise false * . */ public boolean isDefault() { return scale == 1.0 && offset == 0.0 && blankingValue == null; } /** * Adds the quantization parameters to an image header, * * @param h * the image header. * @see #fromImageHeader(Header) * @see #editTableHeader(Header, int) */ public void editImageHeader(Header h) { h.addValue(Standard.BSCALE, scale); h.addValue(Standard.BZERO, offset); if (blankingValue != null) { h.addValue(Standard.BLANK, blankingValue); } else { h.deleteKey(Standard.BLANK); } } /** * Adds the quantization parameters to a binaty table header, * * @param h * the binary table header. * @param col * the zero-based Java column index * @see #fromTableHeader(Header, int) * @see #editImageHeader(Header) */ public void editTableHeader(Header h, int col) { Cursor c = h.iterator(); c.setKey(Standard.TFORMn.n(col + 1).key()); c.add(HeaderCard.create(Standard.TSCALn.n(col + 1), scale)); c.add(HeaderCard.create(Standard.TZEROn.n(col + 1), offset)); if (blankingValue != null) { c.add(HeaderCard.create(Standard.TNULLn.n(col + 1), blankingValue)); } else { h.deleteKey(Standard.TNULLn.n(col + 1)); } } /** * Returns the quantizer that is described by an image header. * * @param h * an image header * @return the quantizer that id described by the header. It may be the * default quantizer if the header does not contain any of the * quantization keywords. * @see #editImageHeader(Header) * @see #fromTableHeader(Header, int) * @see #isDefault() */ public static Quantizer fromImageHeader(Header h) { return new Quantizer(h.getDoubleValue(Standard.BSCALE, 1.0), h.getDoubleValue(Standard.BZERO, 0.0), // h.containsKey(Standard.BLANK) ? h.getLongValue(Standard.BLANK) : null); } /** * Returns the quantizer that is described by a binary table header. * * @param h * a binary table header * @param col * the zero-based Java column index * @return the quantizer that id described by the header. It may be the * default quantizer if the header does not contain any of the * quantization keywords. * @see #editTableHeader(Header, int) * @see #fromImageHeader(Header) * @see #isDefault() */ public static Quantizer fromTableHeader(Header h, int col) { return new Quantizer(h.getDoubleValue(Standard.TSCALn.n(col + 1), 1.0), // h.getDoubleValue(Standard.TZEROn.n(col + 1), 0.0), // h.containsKey(Standard.TNULLn.n(col + 1)) ? h.getLongValue(Standard.TNULLn.n(col + 1)) : null); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy