/*========================================================================= Program: FusionViewer Module: $RCSfile: Image.java,v $ Language: Java Date: $Date: 2008/01/11 21:37:29 $ Version: $Revision: 1.4 $ Copyright (c) Insightful Corporation. All rights reserved. See Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ package org.fusionviewer.display; import java.nio.ByteBuffer; /**\class Image *\brief A 3D image volume stored by native code with the pixel values being stored in their * native format. * (i.e., float images are stored as an array of floats while int images * are stored as an array of ints) */ public class Image { static { System.loadLibrary("FusionViewer"); } private long m_nativeHandle; // pointer to the image -- should only be accessed by native code, can check // against 0 to see if the image is initialized private static String m_series[]; private float m_pixelSpacing[];// array of 3 elements -- spacing between pixels in each dimension in millimeters private int m_dimensions[]; // array of 3 elements -- number of pixels in each dimension private double m_minValue; // minimum pixel value in the image private double m_maxValue; // maximum pixel value in the image private boolean m_isByteType; // true if the image pixels are 8 bits /** * Load an image from a file * * @param filename path to the image file to read */ public Image(String filename, boolean isDICOM) { if(isDICOM == false){ if (!createFromFile(filename)) throw new IllegalArgumentException("Could not create image from file " + filename); } else { int septorIndex = filename.lastIndexOf(System.getProperty("file.separator")); String dicomDirectory = filename.substring(0, septorIndex+1); String dicomIdentifier = filename.substring(septorIndex+1, filename.length()); if(!createDICOMFromFile(dicomDirectory, dicomIdentifier)){ throw new IllegalArgumentException("Could not create DICOM image volume from file " + filename); } } if (m_dimensions.length < 2) { dispose(); throw new IllegalArgumentException("Unsupported image dimension: " + Integer.toString(m_dimensions.length)); } } /** * Load an image from multiple files * * @param filenames list of files to load the image from * @param dicom */ public Image(String[] filenames, boolean isDICOM) { if(isDICOM == false){ if (!createFromSequence(filenames)) throw new IllegalArgumentException("Could not create image from file list"); } if (m_dimensions.length < 2) { dispose(); throw new IllegalArgumentException("Unsupported image dimension: " + Integer.toString(m_dimensions.length)); } } /** * DICOM series contained in a directory * * @return array of DICOM image UIDs */ public static String[] getSeries(String directory) { m_series = getDICOMSeries(directory); return m_series; } /** * Dimensions of the image as an array as an array with 3 elements. * * @return array of image dimensions (index 0 = x, 1 = y, 2 = z) */ public int[] getDimensions() { if (m_dimensions != null) { int[] dims = new int[m_dimensions.length]; System.arraycopy(m_dimensions, 0, dims, 0, m_dimensions.length); return dims; } return new int[0]; } /** * Returns the size of the image at dimension i (i = 0: x, 1: y, 2: z) * * @param i dimension to query * @return size of the image along axis i */ public int getDimension(int i) { return m_dimensions[i]; } /** * Spacing between pixels in millimeters as an array with 3 elements. * * @return array of pixel spacing (index 0 = x, 1 = y, 2 = z) */ public float[] getPixelSpacing() { if (m_pixelSpacing != null) { float[] spacing = new float[m_pixelSpacing.length]; System.arraycopy(m_pixelSpacing, 0, spacing, 0, m_pixelSpacing.length); return spacing; } return new float[0]; } /** * Returns the pixel spacing of the image in dimension i (i = 0: x, 1: y, 2: z) * * @param i dimension to query * @return pixel spacing along axis i */ public float getPixelSpacing(int i) { return m_pixelSpacing[i]; } /** * Returns the minimum pixel value in the image. * * @return minimum pixel value */ public double getMinValue() { return m_minValue; } /** * Returns the maximum pixel value in the image. * * @return maximum pixel value */ public double getMaxValue() { return m_maxValue; } /** * Returns true if the image pixels are 8 bits. * * @return whether the image is 8-bit */ public boolean isByteType() { return m_isByteType; } /** * Helper function for the constructor. Loads an image from file filename. */ native public boolean createFromFile(String filename); /** * Helper function for the constructor. Loads a DICOM image from * directory and the series identifier. */ native public boolean createDICOMFromFile(String path, String seriesId); /** * Helper function for the constructor. Loads an image sequence from files. */ native public boolean createFromSequence(String[] files); /** * Dispose of the native image resources. */ native public void dispose(); /** * Helper function for the constructor. Loads an image sequence from files. * * @param path DICOM image directory */ native public static String[] getDICOMSeries(String path); /** * Return the value of the pixel at coordinates x, y, z in millimeters. * * @param x x coordinate * @param y y coordinate * @param z z coordinate * @return pixel value */ native public double getPixel(float x, float y, float z); /** * Copies a 2D slice from the image into a 32-bit color output buffer, performing * window and level operations and applying a color lookup table. * * @param firstDirection axis that will be the x axis in the output * @param secondDirection axis that will be the y axis in the output * @param slice slice to extract along the axis that is neither firstDirection nor secondDirection * @param startX index along firstDirection to start copying from * @param startY index along secondDirection to start copying from * @param width number of pixels along firstDirection to copy * @param height number of pixels along secondDirection to copy * @param offset value subtracted from the source pixel when copying * @param slope value multiplied with the source pixel when copying * @param colormap array of 256 red, green, and blue 8-bit triplets to map source pixels to after * they have been scaled by offset and slope; colormap[0] = red, colormap[1] = green, * colormap[2] = blue for pixel value 0, colormap at 3, 4, and 5 will be pixel value 1, * etc. * @param invert if true, inverts the color map * @param imageData buffer to copy the slice to; this buffer must be width * height * 4 bytes in size * and is to be interpreted as an interleaved 8-bit blue, green, red, alpha buffer. */ native public void loadSliceWindowLevel(int firstDirection, int secondDirection, int slice, int startX, int startY, int width, int height, float offset, float slope, ByteBuffer colormap, boolean invert, ByteBuffer imageData); /** * Copies a 2D slice into an intermediate 12-bit buffer. The pixel value are linearly * scaled into a range from 0 to 4095. This buffer is intended to be transformed using * fixed point math to scale the image. * * @param firstDirection axis that will be the x axis in the output * @param secondDirection axis that will be the y axis in the output * @param slice slice to extract along the axis that is neither firstDirection nor secondDirection * @param imageData buffer (see NativeBuffer) to copy the slice to; this buffer must be width * height * 2 * bytes in size and is to be interpreted as 12-bit grayscale values. */ native public void loadSlice(int firstDirection, int secondDirection, int slice, long imageData); /** * Applies a window and level operation to the 12-bit buffer at input. A colormap is then applied. * This is intended to be used with loadSlice as an alternative to loadSliceWindowLevel. * * @param input 12-bit input image pointer (see NativeBuffer) * @param output buffer to use as output; this buffer must be width * height * 4 bytes in size * and is to be interpreted as an interleaved 8-bit blue, green, red, alpha buffer. * @param size number of pixels in input * @param offset value subtracted from the source pixel when copying * @param slope value multiplied with the source pixel when copying * @param colormap array of 256 red, green, and blue 8-bit triplets to map source pixels to after * they have been scaled by offset and slope; colormap[0] = red, colormap[1] = green, * colormap[2] = blue for pixel value 0, colormap at 3, 4, and 5 will be pixel value 1, * etc. * @param invert if true, inverts the color map */ native public void windowLevel(long input, ByteBuffer output, int size, int offset, float slope, ByteBuffer colormap, boolean invert); /** * Copy a part of one image into another image with window and level, colormap, and scaling operations. * This is intended to be used with loadSlice as an alternative to loadSliceWindowLevel. * * @param inBuf 12-bit input image pointer (see NativeBuffer) * @param outBuf buffer to use as output; this buffer must be width * height * 4 bytes in size * and is to be interpreted as an interleaved 8-bit blue, green, red, alpha buffer. * @param inWidth width of the image in inBuf * @param inHeight height of the image in inBuf * @param outWidth width of the output image * @param outHeight height of the output image * @param xStart index along x to start copying from * @param yStart index along y to start copying from * @param hScale output is hScale wider than the input * @param vScale output is vScale taller than the input * @param offset value subtracted from the source pixel when copying * @param slope value multiplied with the source pixel when copying * @param colormap colormap array of 256 red, green, and blue 8-bit triplets to map source pixels to after * they have been scaled by offset and slope; colormap[0] = red, colormap[1] = green, * colormap[2] = blue for pixel value 0, colormap at 3, 4, and 5 will be pixel value 1, * etc. * @param invert if true, inverts the color map * @param flipY if true, flip the output image vertically * @param nearestNeighbor if true, use nearest neighbor interpolation instead of bilinear */ native public void scaleToOutput(long inBuf, ByteBuffer outBuf, int inWidth, int inHeight, int outWidth, int outHeight, int xStart, int yStart, float hScale, float vScale, int offset, float slope, ByteBuffer colormap, boolean invert, boolean flipY, boolean nearestNeighbor); /** * Measure the pixels in an ROI. All parameters are specified in image space. * * @param x left side of the ROI * @param y top side of the ROI * @param width ROI width * @param height ROI height * @param measurement sets members to the mean and standard deviation of pixels in the ROI */ native public void measureROI(int x, int y, int width, int height, int firstDirection, int secondDirection, int slice, ROIMeasurement measurement); /** * Get pixel values along a line. All parameters are specified in image space. * * @param x1 * @param y1 * @param z1 * @param x2 * @param y2 * @param z2 * @param measurement sets members to the pixel values along the line between (x1, y1, z1) and (x2, y2, z2) */ native public void measureLineProfile(int x1, int y1, int z1, int x2, int y2, int z2, LineProfileMeasurement measurement); }