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

gov.nasa.pds.objectAccess.TwoDImageExporter Maven / Gradle / Ivy

There is a newer version: 2.8.4
Show newest version
// Copyright 2019, California Institute of Technology ("Caltech").
// U.S. Government sponsorship acknowledged.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
// * Neither the name of Caltech nor its operating division, the Jet Propulsion
// Laboratory, nor the names of its contributors may be used to endorse or
// promote products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package gov.nasa.pds.objectAccess;

import gov.nasa.arc.pds.xml.generated.Array2DImage;
import gov.nasa.arc.pds.xml.generated.AxisArray;
import gov.nasa.arc.pds.xml.generated.DisplaySettings;
import gov.nasa.arc.pds.xml.generated.FileAreaObservational;
import gov.nasa.pds.label.DisplayDirection;
import gov.nasa.pds.objectAccess.DataType.NumericDataType;

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Iterator;
import java.util.List;

import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp;

import jpl.mipl.io.plugins.DOMtoPDSlabel;
import jpl.mipl.io.plugins.ImageToPDS_DOM;
import jpl.mipl.io.vicar.AlreadyOpenException;
import nom.tam.fits.Fits;
import nom.tam.fits.FitsException;
import nom.tam.fits.FitsFactory;
import nom.tam.fits.ImageHDU;
import nom.tam.util.BufferedDataOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.primitives.UnsignedLong;
import com.sun.media.jai.codec.SeekableStream;


/** Class for converting 2D PDS images.
 * @author dcberrio, mcayanan
 *
 */
public class TwoDImageExporter extends ImageExporter 
implements Exporter {

	Logger logger = LoggerFactory.getLogger(TwoDImageExporter.class);

	private NumericDataType rawDataType;

	/**
	 * Default target settings are 8-bit gray scale
	 */
	private int targetPixelBitDepth = 8;
	private int targetLevels = (int) Math.pow(2,targetPixelBitDepth);
	private IndexColorModel colorModel;
	private BufferedImage bufferedImage;
	private int imageType = BufferedImage.TYPE_BYTE_INDEXED;
	private boolean maximizeDynamicRange = true;
	private String exportType = "PNG";
	private Array2DImage pdsImage;
	private boolean lineDirectionDown = true;
	private boolean sampleDirectionRight = true;
	private boolean firstIndexFastest = true;
	private int numberOfBands = 1;
	private double scalingFactor = 1.0;
	private double valueOffset = 0.0;
  private double dataMin = Double.NEGATIVE_INFINITY;
  private double dataMax = Double.POSITIVE_INFINITY;  


	TwoDImageExporter(FileAreaObservational fileArea, ObjectProvider provider)
	    throws IOException {
		super(fileArea, provider);
	}

	TwoDImageExporter(File label, int fileAreaIndex) throws Exception {
	  this(label.toURI().toURL(), fileAreaIndex);
	} 
	
	TwoDImageExporter(URL label, int fileAreaIndex) throws Exception {
		super(label, fileAreaIndex);
	}


	private void setImageType() {
		switch (targetPixelBitDepth) {
			case 8:
				imageType = BufferedImage.TYPE_BYTE_INDEXED;
				break;
			case 16:
				imageType = BufferedImage.TYPE_USHORT_GRAY;
		}

	}

	@Override
	public void convert(OutputStream outputStream, int objectIndex)
			throws IOException {
		List imageList = getObjectProvider().getArray2DImages(getObservationalFileArea());
		setArray2DImage(imageList.get(objectIndex));
		convert(getArray2DImage(), outputStream);
	}

	/**
	 * Converts a 2D array file to a viewable image file.
	 *
	 * @param outputStream the output stream
	 * @param array2DImage the array2DImage object to convert
	 * @throws IOException if there is an exception writing to the stream or reading the image
	 */
	@Override
	public void convert(Array2DImage array2DImage, OutputStream outputStream) throws IOException {
		setArray2DImage(array2DImage);
		int lines = 0;
		int samples = 0;
		if (array2DImage.getAxes() == 2) {
			for (AxisArray axis : array2DImage.getAxisArraies()) {
				//TODO axis ordering -- how does axis order related to index order?
				if (axis.getSequenceNumber() == 2) {
					samples = axis.getElements().intValueExact();
				} else {
					lines = axis.getElements().intValueExact();
				}
			}
		}

    BufferedInputStream bufferedInputStream = new BufferedInputStream(
        new URL(getObjectProvider().getRoot(),
    getObservationalFileArea().getFile().getFileName()).openStream());
		bufferedInputStream.skip(array2DImage.getOffset().getValue().longValueExact());
		
    int scanline_stride = samples;
    int[] band_offsets = new int[1];
    for (int i = 0; i < 1; i++) {
      band_offsets[i] = 0;
    }
    /* Taken from Vicar IO library. */
    int dataBufferType = DataBuffer.TYPE_FLOAT;
    String dataType = array2DImage.getElementArray().getDataType();
    SampleModel sampleModel =  new PixelInterleavedSampleModel(
        dataBufferType, samples, lines, 1, scanline_stride, band_offsets);
    ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
    ImageTypeSpecifier imageType = new ImageTypeSpecifier(colorModel, sampleModel);
    bufferedImage = imageType.createBufferedImage(samples, lines);

		flexReadToRaster(bufferedInputStream, bufferedImage, lines, samples);
    // Auto scale the image if there were no min/max values defined in the label.
    bufferedImage = scaleImage(bufferedImage);

    // Call JAI's reformat operation to allow image data to be displayable
    bufferedImage = toDisplayableImage(bufferedImage);
    
		if (exportType.equals("VICAR") || exportType.equalsIgnoreCase("PDS3")) {
			try {
				writeLabel(outputStream, getExportType());
			} catch (Exception e) {
				// Caught by method
			}
		}
		// ImageIO write
		writeRasterImage(outputStream, bufferedImage);
		outputStream.close();
	}

	private void setImageElementsDataType(Array2DImage array2dImage) {
		try {
			setRawDataType(Enum.valueOf(NumericDataType.class, array2dImage.getElementArray().getDataType()));
		} catch (Exception e) {
			logger.error("Array data type is not valid, null, or unsupported", e);
			throw new IllegalArgumentException("Array data type is not valid, null, or unsupported");
		}
	}


	/** Read in the data maximum and minimum values.
	 * TODO
	 * There's various types of range scaling/levels adjustment that we could do:
	 * 1) Scale all values according to difference between maximum input value and
	 *  the  target pixel bit depth using a linear transformation
	 * 2) Scale all values according to the difference between the maximum
	 * space of input values and the target pixel bit depth...ie not based on
	 * actual input values
	 * The default (maximizeDynamicRange true) effects 1) and maximizeDynamicRange false does #2.
	 * @param array2dImage
	 */
	private void setImageStatistics(Array2DImage array2dImage) {	  
		if (array2dImage.getDisplay2DImage() != null) {
  		if (array2dImage.getDisplay2DImage().getLineDisplayDirection().equalsIgnoreCase("UP")) {
  			lineDirectionDown = false;
  		}
  		if (array2dImage.getDisplay2DImage().getSampleDisplayDirection().equalsIgnoreCase("LEFT")) {
  			setSampleDirectionRight(false);
  		}
		}
		
		if (array2dImage.getLocalIdentifier() != null) {
		  DisplaySettings ds = getDisplaySettings(array2dImage.getLocalIdentifier());
      if (ds != null) {
        DisplayDirection lineDir = null;
        try {
          lineDir = DisplayDirection.getDirectionFromValue(
            ds.getDisplayDirection().getVerticalDisplayDirection());
          if (lineDir.equals(DisplayDirection.BOTTOM_TO_TOP)) {
            lineDirectionDown = false;
          } else if (lineDir.equals(DisplayDirection.TOP_TO_BOTTOM)) {
            lineDirectionDown = true;
          }
        } catch (NullPointerException ignore) {
          logger.error("Cannot find vertical_display_direction element "
              + "in the Display_Direction area with identifier '"
              + array2dImage.getLocalIdentifier() + "'.");
        }
        
        DisplayDirection sampleDir = null;
        try {
          sampleDir = DisplayDirection.getDirectionFromValue(
            ds.getDisplayDirection().getHorizontalDisplayDirection());
          if (sampleDir.equals(DisplayDirection.RIGHT_TO_LEFT)) {
            setSampleDirectionRight(false);
          } else if (sampleDir.equals(DisplayDirection.LEFT_TO_RIGHT)) {
            setSampleDirectionRight(true);
          }
        } catch (NullPointerException ignore) {
          logger.error("Cannot find horizontal_display_direction element "
              + "in the Display_Direction area with identifier '"
              + array2dImage.getLocalIdentifier() + "'.");
        }
      } else {
        logger.info("No display settings found for identifier '"
            + array2dImage.getLocalIdentifier() + "'.");
      }
		} else {
		  logger.info("No display settings found. Missing local_identifier "
		      + "element in the Array_2D_Image area.");
		}
		
		if (array2dImage.getAxisIndexOrder().equalsIgnoreCase("LAST INDEX FASTEST")) {
			setFirstIndexFastest(false);
		}
		
    if (array2dImage.getElementArray().getScalingFactor() != null) {
      scalingFactor = array2dImage.getElementArray().getScalingFactor().doubleValue();
    }
    
    if (array2dImage.getElementArray().getValueOffset() != null) {
      valueOffset = array2dImage.getElementArray().getValueOffset().doubleValue();
    }
    
    // Does the min/max values specified in the label represent the stored
    // value? If so, then we're doing this right in factoring the scaling_factor
    // and offset.
    if (array2dImage.getObjectStatistics() != null) {
      if (array2dImage.getObjectStatistics().getMinimum() != null) {
        dataMin = array2dImage.getObjectStatistics().getMinimum();
        dataMin = (dataMin * scalingFactor) + valueOffset;
      }
      if (array2dImage.getObjectStatistics().getMaximum() != null) {
        dataMax = array2dImage.getObjectStatistics().getMaximum();
        dataMax = (dataMax * scalingFactor) + valueOffset;
      }
    }
		//TODO Handle adjusting dynamic range more completely?

	}


	private void flexReadToRaster(BufferedInputStream inputStream, 
	    BufferedImage bufferedImage, int lines, int samples) throws IOException {
	  WritableRaster raster = bufferedImage.getRaster();
		int countBytes = -1;
		SeekableStream si = null;

		try {
		  si = SeekableStream.wrapInputStream(inputStream, false);
			int xWrite = 0;
			int yWrite = 0;
			if (firstIndexFastest) {
				for (int x = 0; x < samples; x++){
					if (sampleDirectionRight) {
						xWrite = x;
					} else {
						xWrite = samples-x-1;
					}
					for(int y = 0; y < lines; y++){
						countBytes += 2;
						double value = 0;
            switch (rawDataType) {
            case SignedByte:
              value = si.readByte();
              break;
            case UnsignedByte:
              value = si.readUnsignedByte();
              break;
            case UnsignedLSB2:
              value = si.readUnsignedShortLE();
              break;
            case SignedLSB2:
              value = si.readShortLE();
              break;
            case UnsignedMSB2:
              value = si.readUnsignedShort();
              break;              
            case SignedMSB2:
              value = si.readShort();
              break;
            case UnsignedMSB4:
              value = si.readUnsignedInt();
              break;
            case UnsignedMSB8:
              value = UnsignedLong.valueOf(si.readLong()).doubleValue();
              break;           
            case IEEE754MSBSingle:
              value = si.readFloat();
              break;
            case IEEE754MSBDouble:
              value = si.readDouble();
              break;
            }
						//TODO test other input data types
						if (lineDirectionDown) {
							yWrite = y;
						} else {
							yWrite = lines-y-1;
						}
            value = (value * scalingFactor) + valueOffset;
            if (value < dataMin) {
              value = dataMin;
            }
            if (value > dataMax) {
              value = dataMax;
            }
            raster.setSample(xWrite, yWrite, 0, value);
					}
				}
			} else { 
  			for (int y = 0; y < lines; y++) {
  				if (lineDirectionDown) {
  				  yWrite = y;
  				} else {
  				  yWrite = lines-y-1;
  				}
  				for (int x = 0; x < samples; x++) {
  					countBytes += 2;
  					double value = 0;
            switch (rawDataType) {
            case SignedByte:
              value = si.readByte();
              break;
            case UnsignedByte:
              value = si.readUnsignedByte();
              break;
            case UnsignedLSB2:
              value = si.readUnsignedShortLE();
              break;
            case SignedLSB2:
              value = si.readShortLE();
              break;
            case UnsignedMSB2:
              value = si.readUnsignedShort();
              break;              
            case SignedMSB2:
              value = si.readShort();
              break;
            case UnsignedMSB4:
              value = si.readUnsignedInt();
              break;
            case UnsignedMSB8:
              value = UnsignedLong.valueOf(si.readLong()).doubleValue();
              break;           
            case IEEE754MSBSingle:
              value = si.readFloat();
              break;
            case IEEE754MSBDouble:
              value = si.readDouble();
              break;
            }
  					//TODO test other input data types
  					if (sampleDirectionRight) {
  						xWrite = x;
  					} else {
  						xWrite = samples-x-1;
  					}
            value = (value * scalingFactor) + valueOffset;
            if (value < dataMin) {
              value = dataMin;
            }
            if (value > dataMax) {
              value = dataMax;
            }
            raster.setSample(xWrite, yWrite, 0, value);
  				}
  			}
			}
		} catch (Exception e) {
			String m = "EOF at byte number: "+countBytes+ "inputFile: " + inputStream;
			logger.error(m, e);
			throw new IOException(m);
		} finally {
			if (si != null) {
				try {si.close();} catch (IOException e) {}
			}
			if (inputStream != null) {
			  try {
			    inputStream.close();
			  } catch (IOException ignore) {
			    //Ignore
			  }
			}
		}
	}

	/**
   * Auto scales the given image by performing amplitude rescaling.
   * 
   * @param bufferedImage The image to rescale.
   * @return The rescaled image.
   */
  private BufferedImage scaleImage(BufferedImage bufferedImage) {
    double minValue = dataMin;
    double maxValue = dataMax;
    if ( (minValue == Double.NEGATIVE_INFINITY) 
        || (maxValue == Double.POSITIVE_INFINITY) ) {
      ParameterBlock pbMaxMin = new ParameterBlock();
      pbMaxMin.addSource(bufferedImage);
      RenderedOp extrema = JAI.create("extrema", pbMaxMin);
      double[] allMins = (double[])extrema.getProperty("minimum");
      double[] allMaxs = (double[])extrema.getProperty("maximum");
      if (minValue == Double.NEGATIVE_INFINITY) {
        minValue = allMins[0];
      }
      if (maxValue == Double.POSITIVE_INFINITY) {
        maxValue = allMaxs[0];
      }
      for(int v=1;v maxValue) maxValue = allMaxs[v];
      }
    }
    double[] subtractThis    = new double[1]; subtractThis[0]    = minValue;
    double[] multiplyBy = new double[1]; multiplyBy[0] = 255./(maxValue-minValue);
    PlanarImage planarImage = PlanarImage.wrapRenderedImage(bufferedImage);
    ParameterBlock pbSub = new ParameterBlock();
    pbSub.addSource(planarImage);
    pbSub.add(subtractThis);
    planarImage = (PlanarImage) JAI.create("subtractconst",pbSub,null);    
    ParameterBlock pbMult = new ParameterBlock();
    pbMult.addSource(planarImage);
    pbMult.add(multiplyBy);
    planarImage = (PlanarImage)JAI.create("multiplyconst",pbMult,null);
    return planarImage.getAsBufferedImage();
  }

  /**
   * Create a surrogate image from the given image so that it can be
   * displayable.
   * 
   * @param bufferedImage The given image to reformat.
   * 
   * @return The surrogate image.
   */
  private BufferedImage toDisplayableImage(BufferedImage bufferedImage) {
    ParameterBlock pbConvert = new ParameterBlock();
    pbConvert.addSource(bufferedImage);
    pbConvert.add(DataBuffer.TYPE_BYTE);
    PlanarImage planarImage = JAI.create("format", pbConvert);
    return planarImage.getAsBufferedImage();
  }
  
	private void writeRasterImage(OutputStream outputStream, BufferedImage bi) {
		// Store the image using the export format.
		try {
			if (exportType.equals("VICAR") || exportType.equals("PDS3")) {
				ImageIO.write(bi, "raw", outputStream);
			} else if (exportType.equalsIgnoreCase("fits")) {
				writeFitsFile(outputStream, bi);
			} else {
				ImageIO.write(bi, exportType, outputStream);
			}
		} catch (IOException e) {
			String message = "Error writing to output stream";
			logger.error(message, e);
		}
	}

	private void writeFitsFile(OutputStream outputStream, BufferedImage bi) {
		Fits f = new Fits();
		try {
			// FITS is defined with line direction up, opposite of java and other formats
		    
			// Flip the image vertically
			AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
			//PDS-573
			tx.translate(0, -bi.getHeight());
			//TODO should be no interpolation on a simple vertical flip
			AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
			bi = op.filter(bi, null);
			
			
			// TODO What order does raster return data....by columns then rows?
			ImageHDU hdu = (ImageHDU) FitsFactory.HDUFactory(bi.getData().getDataElements(0, 0, bi.getWidth(), bi.getHeight(), null));
			hdu.addValue("NAXIS", 2, "NUMBER OF AXES");
		  // PDS-573
			hdu.addValue("NAXIS1", bi.getWidth(), "NUMBER OF COLUMNS");
			hdu.addValue("NAXIS2", bi.getHeight(), "NUMBER OF ROWS");
			f.addHDU(hdu);
			BufferedDataOutputStream bdos = new BufferedDataOutputStream(outputStream);
			f.write(bdos);
			bdos.close();
		} catch (FitsException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void writeLabel(OutputStream outputStream, String type) throws AlreadyOpenException, IOException, Exception {
		if (type.equalsIgnoreCase("VICAR")) {
			VicarSystemLabelGenerator labelGenerator = new VicarSystemLabelGenerator();
			int cols = 0, rows = 0;
			if (pdsImage.getAxes() == 2) {
				for (AxisArray axis : pdsImage.getAxisArraies()) {
					//TODO axis ordering -- how does axis order related to index order?
					if (axis.getSequenceNumber() == 2) {
						cols = axis.getElements().intValueExact();
					} else {
						rows = axis.getElements().intValueExact();
					}
				}
			}
			labelGenerator.set_org("BSQ");  // Unless PDS label supports bands, a
											// Data Architecture will always be BSQ
			labelGenerator.set_nb(numberOfBands);
			labelGenerator.set_nl(cols);
			labelGenerator.set_ns(rows);
			labelGenerator.set_binc(1.0);
			labelGenerator.set_linc(1.0);
			labelGenerator.set_sinc(1.0);
			labelGenerator.set_datatype(getRawDataType().getVicarAlias());
			labelGenerator.set_tileHeight(rows);
			labelGenerator.set_tileWidth(cols);
			labelGenerator.set_pixelStride(1);
			labelGenerator.generateFile(outputStream);
		} else if (type.equalsIgnoreCase("PDS3")) {
			ImageToPDS_DOM imageToPdsDom = new ImageToPDS_DOM(bufferedImage);
			outputStream.write(new DOMtoPDSlabel(imageToPdsDom.getDocument()).toString().getBytes("ASCII"));
		} else {
			String message = "Unsupported label type: " + type;
			logger.error(message);
			throw new Exception(message);
		}
	}

	private IndexColorModel getColorModel() {
		return colorModel;
	}

	private void setColorModel(IndexColorModel colorModel) {
		this.colorModel = colorModel;
	}


	/** Return the target image pixel depth in bits
	 * @return targetPixelBitDepth
	 */
	public int getTargetPixelDepth() {
		return targetPixelBitDepth;
	}


	/** Set the target pixel bit depth
	 * @param targetPixelDepth the target pixel bit depth
	 */
	public void setTargetPixelDepth(int targetPixelDepth) {
		if (targetPixelDepth != 8 && targetPixelDepth != 16) {
			String message = "Supported pixel bit depths are 8 and 16";
			logger.error(message);
			throw new IllegalArgumentException(message);
		}
		this.targetPixelBitDepth = targetPixelDepth;
		this.targetLevels = (int) Math.pow(2,this.targetPixelBitDepth);
		switch (targetPixelBitDepth) {
			case 8:
				imageType = BufferedImage.TYPE_BYTE_INDEXED;
				break;
			case 16:
				imageType = BufferedImage.TYPE_USHORT_GRAY;
				break;
		}
	}


	private NumericDataType getRawDataType() {
		return rawDataType;
	}


	private void setRawDataType(NumericDataType rawDataType) {
		this.rawDataType = rawDataType;
		
	}


	/** Get whether or not input data elements are scaled up to the
	 * target pixel bit depth
	 * @return boolean
	 */
	public boolean maximizeDynamicRange() {
		return maximizeDynamicRange;
	}


	/** Set whether or not input data elements are scaled up to the
	 * maximum pixel bit depth
	 * @param dynamicRangeScaling
	 */
	public void maximizeDynamicRange(boolean dynamicRangeScaling) {
		this.maximizeDynamicRange = dynamicRangeScaling;
	}


	/** Get the export image format
	 * @return exportType the export image format
	 */
	public String getExportType() {
		return exportType;
	}


	/** Set the export image format.  The format is limited to those
	 * supported by Java.
	 * @param exportType the export image format
	 */
	@Override
	public void setExportType(String exportType) {
		Iterator imageWriters= ImageIO.getImageWritersByFormatName(exportType);
		if (imageWriters.hasNext()
				|| exportType.equalsIgnoreCase("VICAR")
				|| exportType.equalsIgnoreCase("PDS3")
				|| exportType.equalsIgnoreCase("fits")) {
			this.exportType = exportType;
		} else {
			String message = "The export image type " +exportType + " is not currently supported.";
			logger.error(message);
			throw new IllegalArgumentException(message);
		}
	}


	/** Is the sample direction to the right?
	 * @return sampleDirectionRight
	 */
	public boolean isSampleDirectionRight() {
		return sampleDirectionRight;
	}


	/** Set the sample direction.
	 * @param sampleDirectionRight
	 */
	public void setSampleDirectionRight(boolean sampleDirectionRight) {
		this.sampleDirectionRight = sampleDirectionRight;
	}


	/** Is the first index fastest?
	 * @return firstIndexFastest
	 */
	public boolean isFirstIndexFastest() {
		return firstIndexFastest;
	}


	/** Set whether the first index is fastest.
	 * @param firstIndexFastest
	 */
	public void setFirstIndexFastest(boolean firstIndexFastest) {
		this.firstIndexFastest = firstIndexFastest;
	}

	/** Get the Array 2D Image
	 * @return pdsImage
	 */
	public Array2DImage getArray2DImage() {
		return pdsImage;
	}


	/** Set the Array 2D Image
	 * @param img
	 */
	public void setArray2DImage(Array2DImage img) {
		this.pdsImage = img;
		setImageElementsDataType(pdsImage);
		setImageStatistics(pdsImage);
		setImageType();
	}



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy